diff --git a/CheatMenu/CheatMenu.cpp b/CheatMenu/CheatMenu.cpp index 2cbb1de..9467114 100644 --- a/CheatMenu/CheatMenu.cpp +++ b/CheatMenu/CheatMenu.cpp @@ -159,6 +159,12 @@ void CheatMenu::ApplyStyle() void MenuThread(void* param) { + if (!fs::is_directory(PLUGIN_PATH((char*)"CheatMenu"))) + { + flog << "CheatMenu folder not found. You need to put both \"CheatMenu.asi\" & \"CheatMenu\" folder in the same directory" << std::endl; + return; + } + static bool bGameInit = false; Hook::ApplyMouseFix(); @@ -180,7 +186,7 @@ void MenuThread(void* param) flog << "Starting...\nVersion: " MENU_TITLE "\nAuthor: Grinch_\nDiscord: " DISCORD_INVITE "\nMore Info: " GITHUB_LINK "\n" << std::endl; CFastman92limitAdjuster::Init(); - auto menu = new CheatMenu; + CheatMenu menu; Updater::CheckForUpdate(); while (true) diff --git a/CheatMenu/CheatMenu.vcxproj b/CheatMenu/CheatMenu.vcxproj index 9fb5bd4..b83f07b 100644 --- a/CheatMenu/CheatMenu.vcxproj +++ b/CheatMenu/CheatMenu.vcxproj @@ -15,6 +15,7 @@ true Win32Proj CheatMenu + 10.0 @@ -51,7 +52,7 @@ $(GTA_SA_DIR)\ $(ProjectDir).obj\GTASA\Debug\ - CheatMenu_d + CheatMenu .asi $(IncludePath);$(DIRECTX9_SDK_DIR)\Include\ $(LibraryPath);$(DIRECTX9_SDK_DIR)\Lib\x86\ @@ -67,6 +68,7 @@ $(PLUGIN_SDK_DIR)\plugin_sa\;$(PLUGIN_SDK_DIR)\plugin_sa\game_sa\;$(PLUGIN_SDK_DIR)\shared\;$(PLUGIN_SDK_DIR)\shared\game\;%(AdditionalIncludeDirectories) _NDEBUG;_CRT_SECURE_NO_WARNINGS;_CRT_NON_CONFORMING_SWPRINTFS;GTASA;GTAGAME_NAME="San Andreas";GTAGAME_ABBR="SA";GTAGAME_ABBRLOW="sa";GTAGAME_PROTAGONISTNAME="CJ";GTAGAME_CITYNAME="San Andreas";_DX9_SDK_INSTALLED;PLUGIN_SGV_10US;%(PreprocessorDefinitions) stdcpplatest + NoListing true @@ -78,6 +80,7 @@ Windows + false diff --git a/CheatMenu/Hook.cpp b/CheatMenu/Hook.cpp index 2bc73cb..ae5a778 100644 --- a/CheatMenu/Hook.cpp +++ b/CheatMenu/Hook.cpp @@ -166,6 +166,7 @@ Hook::Hook() { ImGui::CreateContext(); + // Nvidia Overlay crash fix if (init(kiero::RenderType::D3D9) == kiero::Status::Success) { Globals::renderer = Render_DirectX9; diff --git a/CheatMenu/Json.cpp b/CheatMenu/Json.cpp index 6d5e843..d0dd367 100644 --- a/CheatMenu/Json.cpp +++ b/CheatMenu/Json.cpp @@ -3,7 +3,7 @@ CJson::CJson(const char* name) { - if (name == "") + if (name == "" || !fs::is_directory(PLUGIN_PATH((char*)"CheatMenu"))) return; m_FilePath = PLUGIN_PATH((char*)"/CheatMenu/json/") + std::string(name) + ".json"; diff --git a/CheatMenu/MenuInfo.h b/CheatMenu/MenuInfo.h index a890d25..88f67bc 100644 --- a/CheatMenu/MenuInfo.h +++ b/CheatMenu/MenuInfo.h @@ -2,5 +2,5 @@ #define MENU_NAME "Cheat Menu" #define MENU_VERSION_NUMBER "2.8" #define MENU_VERSION MENU_VERSION_NUMBER"-beta" -#define BUILD_NUMBER "20210705" +#define BUILD_NUMBER "20210710" #define MENU_TITLE MENU_NAME " v" MENU_VERSION "(" BUILD_NUMBER ")" diff --git a/CheatMenu/Updater.cpp b/CheatMenu/Updater.cpp index c4bf7ea..290319e 100644 --- a/CheatMenu/Updater.cpp +++ b/CheatMenu/Updater.cpp @@ -64,6 +64,4 @@ void Updater::ShowUpdateScreen() { Updater::m_State = UPDATER_IDLE; } - - } diff --git a/CheatMenu/Util.cpp b/CheatMenu/Util.cpp index 5cb3c92..98483e6 100644 --- a/CheatMenu/Util.cpp +++ b/CheatMenu/Util.cpp @@ -174,17 +174,15 @@ int Util::GetLargestGangInZone() for (int i = 0; i != 10; ++i) { CVector pos = FindPlayerPed()->GetPosition(); - auto zone = new CZone(); - CZoneExtraInfo* zone_info = CTheZones::GetZoneInfo(&pos, &zone); + CZoneExtraInfo* zone_info = CTheZones::GetZoneInfo(&pos, nullptr); int density = zone_info->m_nGangDensity[i]; - + if (density > max_density) { max_density = density; gang_id = i; } - delete zone; } return gang_id; diff --git a/Depend/imgui/imconfig.h b/Depend/imgui/imconfig.h index 39de21c..ce60ddf 100644 --- a/Depend/imgui/imconfig.h +++ b/Depend/imgui/imconfig.h @@ -42,7 +42,8 @@ //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. -//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. +//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) +//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). //---- Include imgui_user.h at the end of imgui.h as a convenience diff --git a/Depend/imgui/imgui.cpp b/Depend/imgui/imgui.cpp index 9604520..d08f819 100644 --- a/Depend/imgui/imgui.cpp +++ b/Depend/imgui/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.82 +// dear imgui, v1.83 // (main code and documentation) // Help: @@ -11,16 +11,16 @@ // - FAQ http://dearimgui.org/faq // - Homepage & latest https://github.com/ocornut/imgui // - Releases & changelog https://github.com/ocornut/imgui/releases -// - Gallery https://github.com/ocornut/imgui/issues/3488 (please post your screenshots/video there!) +// - Gallery https://github.com/ocornut/imgui/issues/3793 (please post your screenshots/video there!) +// - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there) // - Glossary https://github.com/ocornut/imgui/wiki/Glossary -// - Wiki https://github.com/ocornut/imgui/wiki // - Issues & support https://github.com/ocornut/imgui/issues // - Discussions https://github.com/ocornut/imgui/discussions // Developed by Omar Cornut and every direct or indirect contributors to the GitHub. // See LICENSE.txt for copyright and licensing details (standard MIT License). // This library is free but needs your support to sustain development and maintenance. -// Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.org". +// Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.com". // Individuals: you can support continued development via donations. See docs/README or web page. // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library. @@ -134,17 +134,17 @@ CODE READ FIRST ---------- - - Remember to read the FAQ (https://www.dearimgui.org/faq) - - 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 destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs. + - Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki) + - 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 + destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, fewer bugs. - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features. - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build. - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori). - You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in the FAQ. + You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki. - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances. - For every application frame your UI code will be called only once. This is in contrast to e.g. Unity's own implementation of an IMGUI, + 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, where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches. - - Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right. + - Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right. - This codebase is also optimized to yield decent performances with typical "Debug" builds settings. - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected). If you get an assert, read the messages and comments around the assert. @@ -157,8 +157,8 @@ CODE HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI ---------------------------------------------- - - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h) - - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over master. + - Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h) + - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master". - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file. - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes. If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed @@ -173,12 +173,12 @@ CODE - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library. - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder. - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system. - It is recommended you build and statically link the .cpp files as part of your project and NOT as shared library (DLL). + It is recommended you build and statically link the .cpp files as part of your project and NOT as a shared library (DLL). - 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. - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them. - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide. Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" - phases of your own application. All rendering information are stored into command-lists that you will retrieve after calling ImGui::Render(). + phases of your own application. All rendering information is stored into command-lists that you will retrieve after calling ImGui::Render(). - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code. - 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. @@ -186,7 +186,7 @@ CODE HOW A SIMPLE APPLICATION MAY LOOK LIKE -------------------------------------- EXHIBIT 1: USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder). - The sub-folders in examples/ contains examples applications following this structure. + The sub-folders in examples/ contain examples applications following this structure. // Application init: create a dear imgui context, setup some options, load fonts ImGui::CreateContext(); @@ -236,7 +236,7 @@ CODE unsigned char* pixels = NULL; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - // At this point you've got the texture data and you need to upload that your your graphic system: + // At this point you've got the texture data and you need to upload that to your graphic system: // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'. // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID. MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32) @@ -255,7 +255,7 @@ CODE io.MouseDown[1] = my_mouse_buttons[1]; // Call NewFrame(), after this point you can use ImGui::* functions anytime - // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use Dear ImGui everywhere) + // (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere) ImGui::NewFrame(); // Most of your application code here @@ -275,14 +275,14 @@ CODE // Shutdown ImGui::DestroyContext(); - To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest your application, + To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application, you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! Please read the FAQ and example applications for details about this! HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE --------------------------------------------- - The backends in impl_impl_XXX.cpp files contains many working implementations of a rendering function. + The backends in impl_impl_XXX.cpp files contain many working implementations of a rendering function. void void MyImGuiRenderFunction(ImDrawData* draw_data) { @@ -304,11 +304,11 @@ CODE } else { - // The texture for the draw call is specified by pcmd->TextureId. + // The texture for the draw call is specified by pcmd->GetTexID(). // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization. - MyEngineBindTexture((MyTexture*)pcmd->TextureId); + MyEngineBindTexture((MyTexture*)pcmd->GetTexID()); - // We are using scissoring to clip some objects. All low-level graphics API should supports it. + // We are using scissoring to clip some objects. All low-level graphics API should support it. // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches // (some elements visible outside their bounds) but you can fix that once everything else works! // - Clipping coordinates are provided in imgui coordinates space: @@ -351,7 +351,7 @@ CODE Note that io.NavInputs[] is cleared by EndFrame(). - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values: 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. - - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone. + - We use a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone. 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.). - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://dearimgui.org/controls_sheets - 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 @@ -363,7 +363,7 @@ CODE Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements. 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. 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. - (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse as moving back and forth!) + (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!) (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 to set a boolean to ignore your other external mouse positions until the external source is moved again.) @@ -373,9 +373,12 @@ CODE Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix. 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. - When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. + 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. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID(). + - if you are using official backends from the source tree: you have nothing to do. + - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID(). - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags. - ImDrawCornerFlags_TopLeft -> use ImDrawFlags_RoundCornersTopLeft - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight @@ -424,7 +427,7 @@ CODE - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently). - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton. - 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. - - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on 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. + - 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. - 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! - 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(). replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags). @@ -433,7 +436,7 @@ CODE - 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. - 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. see https://github.com/ocornut/imgui/issues/3361 for all details. - kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version were removed directly as they were most unlikely ever used. + 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. for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`. - 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. - 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. @@ -453,7 +456,7 @@ CODE - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap - IMGUI_DISABLE_TEST_WINDOWS -> use IMGUI_DISABLE_DEMO_WINDOWS - - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was the vaguely documented and rarely if ever used). Instead we added an explicit PrimUnreserve() API. + - 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. - 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). - 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. - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency. @@ -481,7 +484,7 @@ CODE - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete). - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete). - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete). - - 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 arbitrary small value! + - 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! - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already). - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead! - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete). @@ -576,12 +579,12 @@ CODE - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset. - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity. - - 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(). + - 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(). - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it. - 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. - - 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. + - 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. - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore. - If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you, otherwise if <1.0f you need tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar. + 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. 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: 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); } 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. @@ -589,7 +592,7 @@ CODE - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection. - 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). - 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. - - 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). + - 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). - 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) - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete). - 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. @@ -620,7 +623,7 @@ CODE - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete). - 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. - 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 - - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely be used. Sorry! + - 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! - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete). - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete). - 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. @@ -675,7 +678,7 @@ CODE =========== Q: Where is the documentation? - A: This library is poorly documented at the moment and expects of the user to be acquainted with C/C++. + A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++. - Run the examples/ and explore them. - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function. - The demo covers most features of Dear ImGui, so you can read the code and see its output. @@ -685,7 +688,7 @@ CODE - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links. - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful. - Your programming IDE is your friend, find the type or function declaration to find comments - associated to it. + associated with it. Q: What is this library called? Q: Which version should I get? @@ -698,15 +701,15 @@ CODE Q: How to get started? A: Read 'PROGRAMMER GUIDE' above. Read examples/README.txt. - Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or to my application? + Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application? A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! - >> See https://www.dearimgui.org/faq for fully detailed answer. You really want to read this. + >> See https://www.dearimgui.org/faq for a fully detailed answer. You really want to read this. Q. How can I enable keyboard controls? Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display) - Q: I integrated Dear ImGui in my engine and little squares are showing instead of text.. - Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around.. - Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries.. + Q: I integrated Dear ImGui in my engine and little squares are showing instead of text... + Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around... + Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries... >> See https://www.dearimgui.org/faq Q&A: Usage @@ -744,16 +747,16 @@ CODE ============== Q: How can I help? - A: - Businesses: please reach out to "contact AT dearimgui.org" if you work in a place using Dear ImGui! + A: - Businesses: please reach out to "contact AT dearimgui.com" if you work in a place using Dear ImGui! We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts. - 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. + 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. - Individuals: you can support continued development via PayPal donations. See README. - - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt + - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, read docs/TODO.txt and see how you want to help and can help! - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc. - You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/3488). Visuals are ideal as they inspire other programmers. - But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions. - - 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). + You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers. + But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions. + - 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). */ @@ -816,6 +819,9 @@ CODE #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #endif +#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). +#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif // Clang/GCC warnings with -Weverything @@ -858,7 +864,7 @@ static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend) -static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow(). +static const float WINDOWS_HOVER_PADDING = 4.0f; // Extend outside window for hovering/resizing (maxxed with TouchPadding) and inside windows for borders. Affect FindHoveredWindow(). static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. 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. @@ -897,8 +903,8 @@ static void NavUpdateInitResult(); static float NavUpdatePageUpPageDown(); static inline void NavUpdateAnyRequestFlag(); static void NavEndFrame(); -static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand); -static void NavApplyItemToResult(ImGuiNavMoveResult* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel); +static bool NavScoreItem(ImGuiNavItemData* result, ImRect cand); +static void NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel); static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id); static ImVec2 NavCalcPreferredRefPos(); static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); @@ -933,7 +939,7 @@ static void UpdateViewportsNewFrame(); // DLL users: // - Heaps and globals are not shared across DLL boundaries! // - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from. -// - Same apply for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanism works without DLL). +// - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL). // - 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. // - 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). @@ -946,16 +952,16 @@ static void UpdateViewportsNewFrame(); // struct ImGuiContext; // extern thread_local ImGuiContext* MyImGuiTLS; // #define GImGui MyImGuiTLS -// And then define MyImGuiTLS in one of your cpp file. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword. -// - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586 -// - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace. +// 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. +// - Future development aims to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586 +// - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from a different namespace. // - DLL users: read comments above. #ifndef GImGui ImGuiContext* GImGui = NULL; #endif // Memory Allocator functions. Use SetAllocatorFunctions() to change them. -// - You probably don't want to modify those mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction. +// - 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. // - DLL users: read comments above. #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); } @@ -1133,11 +1139,18 @@ void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c) if (InputQueueSurrogate != 0) { if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate + { InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID); - else if (IM_UNICODE_CODEPOINT_MAX == (0xFFFF)) // Codepoint will not fit in ImWchar (extra parenthesis around 0xFFFF somehow fixes -Wunreachable-code with Clang) - cp = IM_UNICODE_CODEPOINT_INVALID; + } else + { +#if IM_UNICODE_CODEPOINT_MAX == 0xFFFF + cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar +#else cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000); +#endif + } + InputQueueSurrogate = 0; } InputQueueCharacters.push_back(cp); @@ -3166,7 +3179,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) return false; // Test if the item is disabled - if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) + if ((g.CurrentItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) return false; // Special handling for calling after Begin() which represent the title bar or tab. @@ -3192,7 +3205,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) return false; if (g.NavDisableMouseHover) return false; - if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None) || (window->DC.ItemFlags & ImGuiItemFlags_Disabled)) + if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None) || (g.CurrentItemFlags & ImGuiItemFlags_Disabled)) { g.HoveredIdDisabled = true; return false; @@ -3239,15 +3252,21 @@ void ImGui::SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatu } // Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out. -bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id) +void ImGui::ItemFocusable(ImGuiWindow* window, ImGuiID id) { ImGuiContext& g = *GImGui; + IM_ASSERT(id != 0 && id == window->DC.LastItemId); // Increment counters - const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; + // FIXME: ImGuiItemFlags_Disabled should disable more. + const bool is_tab_stop = (g.CurrentItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; window->DC.FocusCounterRegular++; if (is_tab_stop) + { window->DC.FocusCounterTabStop++; + if (g.NavId == id) + g.NavIdTabCounter = window->DC.FocusCounterTabStop; + } // Process TAB/Shift-TAB to tab *OUT* of the currently focused item. // (Note that we can always TAB out of a widget that doesn't allow tabbing in) @@ -3261,25 +3280,21 @@ bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id) if (g.TabFocusRequestCurrWindow == window) { if (window->DC.FocusCounterRegular == g.TabFocusRequestCurrCounterRegular) - return true; + { + window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_FocusedByCode; + return; + } if (is_tab_stop && window->DC.FocusCounterTabStop == g.TabFocusRequestCurrCounterTabStop) { g.NavJustTabbedId = id; - return true; + window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_FocusedByTabbing; + return; } // If another item is about to be focused, we clear our own active id if (g.ActiveId == id) ClearActiveID(); } - - return false; -} - -void ImGui::FocusableItemUnregister(ImGuiWindow* window) -{ - window->DC.FocusCounterRegular--; - window->DC.FocusCounterTabStop--; } float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) @@ -3731,10 +3746,17 @@ void ImGui::UpdateMouseWheel() // Mouse wheel scrolling // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent + if (g.IO.KeyCtrl) + return; + + // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead + // (we avoid doing it on OSX as it the OS input layer handles this already) + const bool swap_axis = g.IO.KeyShift && !g.IO.ConfigMacOSXBehaviors; + const float wheel_y = swap_axis ? 0.0f : g.IO.MouseWheel; + const float wheel_x = swap_axis ? g.IO.MouseWheel : g.IO.MouseWheelH; // Vertical Mouse Wheel scrolling - const float wheel_y = (g.IO.MouseWheel != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f; - if (wheel_y != 0.0f && !g.IO.KeyCtrl) + if (wheel_y != 0.0f) { StartLockWheelingWindow(window); while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)))) @@ -3748,8 +3770,7 @@ void ImGui::UpdateMouseWheel() } // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held - const float wheel_x = (g.IO.MouseWheelH != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheelH : (g.IO.MouseWheel != 0.0f && g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f; - if (wheel_x != 0.0f && !g.IO.KeyCtrl) + if (wheel_x != 0.0f) { StartLockWheelingWindow(window); while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)))) @@ -3771,12 +3792,14 @@ void ImGui::UpdateTabFocus() g.TabFocusPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)); if (g.ActiveId == 0 && g.TabFocusPressed) { - // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also - // manipulate the Next fields even, even though they will be turned into Curr fields by the code below. + // - This path is only taken when no widget are active/tabbed-into yet. + // Subsequent tabbing will be processed by FocusableItemRegister() + // - Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also + // manipulate the Next fields here even though they will be turned into Curr fields below. g.TabFocusRequestNextWindow = g.NavWindow; g.TabFocusRequestNextCounterRegular = INT_MAX; if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) - g.TabFocusRequestNextCounterTabStop = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); + g.TabFocusRequestNextCounterTabStop = g.NavIdTabCounter + (g.IO.KeyShift ? -1 : 0); else g.TabFocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0; } @@ -3803,6 +3826,7 @@ void ImGui::UpdateTabFocus() void ImGui::UpdateHoveredWindowAndCaptureFlags() { ImGuiContext& g = *GImGui; + g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING)); // Find the window hovered by mouse: // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow. @@ -3902,7 +3926,8 @@ void ImGui::NewFrame() g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); - g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX; + g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame)); + g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX; UpdateViewportsNewFrame(); @@ -4020,7 +4045,7 @@ void ImGui::NewFrame() UpdateTabFocus(); // Mark all windows as not visible and compact unused memory. - IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size); + IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size); const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer; for (int i = 0; i != g.Windows.Size; i++) { @@ -4039,6 +4064,9 @@ void ImGui::NewFrame() for (int i = 0; i < g.TablesLastTimeActive.Size; i++) if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time) TableGcCompactTransientBuffers(g.Tables.GetByIndex(i)); + for (int i = 0; i < g.TablesTempDataStack.Size; i++) + if (g.TablesTempDataStack[i].LastTimeActive >= 0.0f && g.TablesTempDataStack[i].LastTimeActive < memory_compact_start_time) + TableGcCompactTransientBuffers(&g.TablesTempDataStack[i]); if (g.GcCompactAll) GcCompactTransientMiscBuffers(); g.GcCompactAll = false; @@ -4052,7 +4080,7 @@ void ImGui::NewFrame() g.CurrentWindowStack.resize(0); g.BeginPopupStack.resize(0); g.ItemFlagsStack.resize(0); - g.ItemFlagsStack.push_back(ImGuiItemFlags_Default_); + g.ItemFlagsStack.push_back(ImGuiItemFlags_None); g.GroupStack.resize(0); ClosePopupsOverWindow(g.NavWindow, false); @@ -4078,20 +4106,20 @@ void ImGui::UpdateDebugToolItemPicker() if (g.DebugItemPickerActive) { const ImGuiID hovered_id = g.HoveredIdPreviousFrame; - ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); - if (ImGui::IsKeyPressedMap(ImGuiKey_Escape)) + SetMouseCursor(ImGuiMouseCursor_Hand); + if (IsKeyPressedMap(ImGuiKey_Escape)) g.DebugItemPickerActive = false; - if (ImGui::IsMouseClicked(0) && hovered_id) + if (IsMouseClicked(0) && hovered_id) { g.DebugItemPickerBreakId = hovered_id; g.DebugItemPickerActive = false; } - ImGui::SetNextWindowBgAlpha(0.60f); - ImGui::BeginTooltip(); - ImGui::Text("HoveredId: 0x%08X", hovered_id); - ImGui::Text("Press ESC to abort picking."); - ImGui::TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!"); - ImGui::EndTooltip(); + SetNextWindowBgAlpha(0.60f); + BeginTooltip(); + Text("HoveredId: 0x%08X", hovered_id); + Text("Press ESC to abort picking."); + TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!"); + EndTooltip(); } } @@ -4113,10 +4141,8 @@ void ImGui::Initialize(ImGuiContext* context) g.SettingsHandlers.push_back(ini_handler); } -#ifdef IMGUI_HAS_TABLE // Add .ini handle for ImGuiTable type TableSettingsInstallHandler(context); -#endif // #ifdef IMGUI_HAS_TABLE // Create default viewport ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); @@ -4183,7 +4209,9 @@ void ImGui::Shutdown(ImGuiContext* context) g.ShrinkWidthBuffer.clear(); g.Tables.Clear(); - g.CurrentTableStack.clear(); + for (int i = 0; i < g.TablesTempDataStack.Size; i++) + g.TablesTempDataStack[i].~ImGuiTableTempData(); + g.TablesTempDataStack.clear(); g.DrawChannelsTempMergeBuffer.clear(); g.ClipboardHandlerData.clear(); @@ -4459,6 +4487,7 @@ void ImGui::Render() for (int n = 0; n != g.Windows.Size; n++) { ImGuiWindow* window = g.Windows[n]; + IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1]) AddRootWindowToDrawData(window); } @@ -4532,10 +4561,11 @@ static void FindHoveredWindow() hovered_window = g.MovingWindow; ImVec2 padding_regular = g.Style.TouchExtraPadding; - ImVec2 padding_for_resize_from_edges = g.IO.ConfigWindowsResizeFromEdges ? ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS)) : padding_regular; + ImVec2 padding_for_resize = g.IO.ConfigWindowsResizeFromEdges ? g.WindowsHoverPadding : padding_regular; for (int i = g.Windows.Size - 1; i >= 0; i--) { ImGuiWindow* window = g.Windows[i]; + IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer. if (!window->Active || window->Hidden) continue; if (window->Flags & ImGuiWindowFlags_NoMouseInputs) @@ -4546,7 +4576,7 @@ static void FindHoveredWindow() if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) bb.Expand(padding_regular); else - bb.Expand(padding_for_resize_from_edges); + bb.Expand(padding_for_resize); if (!bb.Contains(g.IO.MousePos)) continue; @@ -4562,6 +4592,7 @@ static void FindHoveredWindow() if (hovered_window == NULL) hovered_window = window; + IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer. if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow)) hovered_window_ignoring_moving_window = window; if (hovered_window && hovered_window_ignoring_moving_window) @@ -4975,7 +5006,7 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b parent_window->DC.CursorPos = child_window->Pos; // Process navigation-in immediately so NavInit can run on first frame - if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll)) + if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavHasScroll)) { FocusWindow(child_window); NavInitWindow(child_window, false); @@ -5022,13 +5053,13 @@ void ImGui::EndChild() ImGuiWindow* parent_window = g.CurrentWindow; ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); ItemSize(sz); - if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened)) + if ((window->DC.NavLayersActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened)) { ItemAdd(bb, window->ChildId); RenderNavHighlight(bb, window->ChildId); // When browsing a window that has no activable items (scroll only) we keep a highlight on the child - if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow) + if (window->DC.NavLayersActiveMask == 0 && window == g.NavWindow) RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin); } else @@ -5129,7 +5160,12 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); } - g.WindowsFocusOrder.push_back(window); + if (!(flags & ImGuiWindowFlags_ChildWindow)) + { + g.WindowsFocusOrder.push_back(window); + window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1); + } + if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) g.Windows.push_front(window); // Quite slow but rare and only once else @@ -5165,8 +5201,9 @@ static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& s if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize))) { ImGuiWindow* window_for_height = window; + const float decoration_up_height = window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight(); new_size = ImMax(new_size, g.Style.WindowMinSize); - new_size.y = ImMax(new_size.y, window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows + new_size.y = ImMax(new_size.y, decoration_up_height + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows } return new_size; } @@ -5195,9 +5232,9 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont { ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; - ImVec2 size_decorations = ImVec2(0.0f, window->TitleBarHeight() + window->MenuBarHeight()); + const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); ImVec2 size_pad = window->WindowPadding * 2.0f; - ImVec2 size_desired = size_contents + size_pad + size_decorations; + ImVec2 size_desired = size_contents + size_pad + ImVec2(0.0f, decoration_up_height); if (window->Flags & ImGuiWindowFlags_Tooltip) { // Tooltip always resize @@ -5219,8 +5256,8 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont // When the window cannot fit all contents (either because of constraints, either because screen is too small), // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit); - bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - size_decorations.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); - bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - size_decorations.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); + 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); + 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); if (will_have_scrollbar_x) size_auto_fit.y += style.ScrollbarSize; if (will_have_scrollbar_y) @@ -5262,53 +5299,64 @@ static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& co *out_size = size_constrained; } +// Data for resizing from corner struct ImGuiResizeGripDef { ImVec2 CornerPosN; ImVec2 InnerDir; int AngleMin12, AngleMax12; }; - static const ImGuiResizeGripDef resize_grip_def[4] = { - { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right - { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left - { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused) - { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 }, // Upper-right (Unused) + { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right + { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left + { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused) + { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 } // Upper-right (Unused) }; +// Data for resizing from borders struct ImGuiResizeBorderDef { ImVec2 InnerDir; - ImVec2 CornerPosN1, CornerPosN2; + ImVec2 SegmentN1, SegmentN2; float OuterAngle; }; - static const ImGuiResizeBorderDef resize_border_def[4] = { - { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Top + { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f }, // Left { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right - { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f }, // Bottom - { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f } // Left + { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Up + { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f } // Down }; static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness) { ImRect rect = window->Rect(); - if (thickness == 0.0f) rect.Max -= ImVec2(1, 1); - if (border_n == 0) { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); } // Top - if (border_n == 1) { return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); } // Right - if (border_n == 2) { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); } // Bottom - if (border_n == 3) { return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); } // Left + if (thickness == 0.0f) + rect.Max -= ImVec2(1, 1); + 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); } + 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); } + 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); } + 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); } IM_ASSERT(0); return ImRect(); } // 0..3: corners (Lower-right, Lower-left, Unused, Unused) -// 4..7: borders (Top, Right, Bottom, Left) -ImGuiID ImGui::GetWindowResizeID(ImGuiWindow* window, int n) +ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n) { - IM_ASSERT(n >= 0 && n <= 7); + IM_ASSERT(n >= 0 && n < 4); + ImGuiID id = window->ID; + id = ImHashStr("#RESIZE", 0, id); + id = ImHashData(&n, sizeof(int), id); + return id; +} + +// Borders (Left, Right, Up, Down) +ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir) +{ + IM_ASSERT(dir >= 0 && dir < 4); + int n = (int)dir + 4; ImGuiID id = window->ID; id = ImHashStr("#RESIZE", 0, id); id = ImHashData(&n, sizeof(int), id); @@ -5331,7 +5379,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0; const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f); - const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f; + const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_HOVER_PADDING : 0.0f; ImVec2 pos_target(FLT_MAX, FLT_MAX); ImVec2 size_target(FLT_MAX, FLT_MAX); @@ -5343,15 +5391,16 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s PushID("#RESIZE"); for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) { - const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; - const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); + const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n]; + const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, def.CornerPosN); // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window - ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size); + bool hovered, held; + ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.InnerDir * grip_hover_inner_size); if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x); if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y); - bool hovered, held; - ButtonBehavior(resize_rect, window->GetID(resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); + ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID() + ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255)); if (hovered || held) g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; @@ -5367,39 +5416,41 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s { // Resize from any of the four corners // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position - ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPosN); // Corner of the window corresponding to our corner grip - ImVec2 clamp_min = ImVec2(grip.CornerPosN.x == 1.0f ? visibility_rect.Min.x : -FLT_MAX, grip.CornerPosN.y == 1.0f ? visibility_rect.Min.y : -FLT_MAX); - ImVec2 clamp_max = ImVec2(grip.CornerPosN.x == 0.0f ? visibility_rect.Max.x : +FLT_MAX, grip.CornerPosN.y == 0.0f ? visibility_rect.Max.y : +FLT_MAX); + 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); + 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); + 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 corner_target = ImClamp(corner_target, clamp_min, clamp_max); - CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target); + CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target); } + + // Only lower-left grip is visible before hovering/activating if (resize_grip_n == 0 || held || hovered) resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); } for (int border_n = 0; border_n < resize_border_count; border_n++) { + const ImGuiResizeBorderDef& def = resize_border_def[border_n]; + const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; + bool hovered, held; - ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); - ButtonBehavior(border_rect, window->GetID(border_n + 4), &hovered, &held, ImGuiButtonFlags_FlattenChildren); + ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_HOVER_PADDING); + ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID() + ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren); //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held) { - g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; + g.MouseCursor = (axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; if (held) *border_held = border_n; } if (held) { + ImVec2 clamp_min(border_n == ImGuiDir_Right ? visibility_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down ? visibility_rect.Min.y : -FLT_MAX); + ImVec2 clamp_max(border_n == ImGuiDir_Left ? visibility_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? visibility_rect.Max.y : +FLT_MAX); ImVec2 border_target = window->Pos; - ImVec2 border_posn; - if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Top - if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Right - if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Bottom - if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Left - ImVec2 clamp_min = ImVec2(border_n == 1 ? visibility_rect.Min.x : -FLT_MAX, border_n == 2 ? visibility_rect.Min.y : -FLT_MAX); - ImVec2 clamp_max = ImVec2(border_n == 3 ? visibility_rect.Max.x : +FLT_MAX, border_n == 0 ? visibility_rect.Max.y : +FLT_MAX); + border_target[axis] = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + WINDOWS_HOVER_PADDING; border_target = ImClamp(border_target, clamp_min, clamp_max); - CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target); + CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target); } } PopID(); @@ -5466,8 +5517,8 @@ static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) { const ImGuiResizeBorderDef& def = resize_border_def[border_held]; ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f); - window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle); - window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f); + 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); + 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); window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), 0, ImMax(2.0f, border_size)); // Thicker than usual } if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) @@ -5573,8 +5624,8 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None); // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer) - const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; + const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; // Layout buttons @@ -5611,7 +5662,7 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl *p_open = false; window->DC.NavLayerCurrent = ImGuiNavLayer_Main; - window->DC.ItemFlags = item_flags_backup; + g.CurrentItemFlags = item_flags_backup; // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker) // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code.. @@ -5695,14 +5746,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update the Appearing flag bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on - const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0); if (flags & ImGuiWindowFlags_Popup) { ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed window_just_activated_by_user |= (window != popup_ref.Window); } - window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize); + window->Appearing = window_just_activated_by_user; if (window->Appearing) SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); @@ -5829,6 +5879,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS // Update contents size from last frame for auto-fitting (or use explicit size) + const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0); CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal); if (window->HiddenFramesCanSkipItems > 0) window->HiddenFramesCanSkipItems--; @@ -6191,8 +6242,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; window->DC.NavLayerCurrent = ImGuiNavLayer_Main; - window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext; - window->DC.NavLayerActiveMaskNext = 0x00; + window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext; + window->DC.NavLayersActiveMaskNext = 0x00; window->DC.NavHideHighlightOneFrame = false; window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f); @@ -6257,7 +6308,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Pull/inherit current state - window->DC.ItemFlags = g.ItemFlagsStack.back(); // Inherit from shared stack + g.CurrentItemFlags = g.ItemFlagsStack.back(); // Inherit from shared stack window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : 0; // Inherit from parent only // -V595 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); @@ -6294,6 +6345,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update the Hidden flag window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0) || (window->HiddenFramesForRenderOnly > 0); + // Disable inputs for requested number of frames + if (window->DisableInputsFrames > 0) + { + window->DisableInputsFrames--; + window->Flags |= ImGuiWindowFlags_NoInputs; + } + // Update the SkipItems flag, used to early out of all items functions (no layout required) bool skip_items = false; if (window->Collapsed || !window->Active || window->Hidden) @@ -6342,15 +6400,22 @@ void ImGui::End() void ImGui::BringWindowToFocusFront(ImGuiWindow* window) { ImGuiContext& g = *GImGui; + IM_ASSERT(window == window->RootWindow); + + const int cur_order = window->FocusOrder; + IM_ASSERT(g.WindowsFocusOrder[cur_order] == window); if (g.WindowsFocusOrder.back() == window) return; - for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the top-most window - if (g.WindowsFocusOrder[i] == window) - { - memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*)); - g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window; - break; - } + + const int new_order = g.WindowsFocusOrder.Size - 1; + for (int n = cur_order; n < new_order; n++) + { + g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1]; + g.WindowsFocusOrder[n]->FocusOrder--; + IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n); + } + g.WindowsFocusOrder[new_order] = window; + window->FocusOrder = (short)new_order; } void ImGui::BringWindowToDisplayFront(ImGuiWindow* window) @@ -6430,18 +6495,13 @@ void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWind { ImGuiContext& g = *GImGui; - int start_idx = g.WindowsFocusOrder.Size - 1; - if (under_this_window != NULL) - { - int under_this_window_idx = FindWindowFocusIndex(under_this_window); - if (under_this_window_idx != -1) - start_idx = under_this_window_idx - 1; - } + const int start_idx = ((under_this_window != NULL) ? FindWindowFocusIndex(under_this_window) : g.WindowsFocusOrder.Size) - 1; for (int i = start_idx; i >= 0; i--) { // 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. ImGuiWindow* window = g.WindowsFocusOrder[i]; - if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow)) + IM_ASSERT(window == window->RootWindow); + if (window != ignore_window && window->WasActive) if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) { ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window); @@ -6490,24 +6550,22 @@ void ImGui::PopFont() void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiItemFlags item_flags = window->DC.ItemFlags; + ImGuiItemFlags item_flags = g.CurrentItemFlags; IM_ASSERT(item_flags == g.ItemFlagsStack.back()); if (enabled) item_flags |= option; else item_flags &= ~option; - window->DC.ItemFlags = item_flags; + g.CurrentItemFlags = item_flags; g.ItemFlagsStack.push_back(item_flags); } void ImGui::PopItemFlag() { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack. g.ItemFlagsStack.pop_back(); - window->DC.ItemFlags = g.ItemFlagsStack.back(); + g.CurrentItemFlags = g.ItemFlagsStack.back(); } // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system. @@ -6934,11 +6992,11 @@ void ImGui::SetItemDefaultFocus() ImGuiWindow* window = g.CurrentWindow; if (!window->Appearing) return; - if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent) + if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == window->DC.NavLayerCurrent) { g.NavInitRequest = false; - g.NavInitResultId = g.NavWindow->DC.LastItemId; - g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos); + g.NavInitResultId = window->DC.LastItemId; + g.NavInitResultRectRel = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos); NavUpdateAnyRequestFlag(); if (!IsItemVisible()) SetScrollHereY(); @@ -7076,7 +7134,7 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() ImGuiContext& g = *GImGui; // Check user IM_ASSERT macro - // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means you assert macro is incorrectly defined! + // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined! // If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block. // This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.) // #define IM_ASSERT(EXPR) if (SomeCode(EXPR)) SomeMoreCode(); // Wrong! @@ -7155,13 +7213,11 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi ImGuiContext& g = *GImGui; while (g.CurrentWindowStack.Size > 0) { -#ifdef IMGUI_HAS_TABLE while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow)) { if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name); EndTable(); } -#endif ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(window != NULL); while (g.CurrentTabBar != NULL) //-V1044 @@ -7331,7 +7387,7 @@ void ImGui::ItemSize(const ImRect& bb, float text_baseline_y) // Declare item bounding box for clipping and interaction. // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface // declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction. -bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) +bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemAddFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -7347,7 +7403,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick). // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null. // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere. - window->DC.NavLayerActiveMaskNext |= (1 << window->DC.NavLayerCurrent); + window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent); if (g.NavId == id || g.NavAnyRequest) if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) @@ -7380,6 +7436,11 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) return false; //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] + // Tab stop handling (previously was using internal ItemFocusable() api) + // 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) + if (flags & ImGuiItemAddFlags_Focusable) + ItemFocusable(window, id); + // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) if (IsMouseHoveringRect(bb.Min, bb.Max)) window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect; @@ -7760,24 +7821,29 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) ImVec2 scroll = window->Scroll; if (window->ScrollTarget.x < FLT_MAX) { + float decoration_total_width = window->ScrollbarSizes.x; float center_x_ratio = window->ScrollTargetCenterRatio.x; float scroll_target_x = window->ScrollTarget.x; - float snap_x_min = 0.0f; - float snap_x_max = window->ScrollMax.x + window->Size.x; if (window->ScrollTargetEdgeSnapDist.x > 0.0f) + { + float snap_x_min = 0.0f; + float snap_x_max = window->ScrollMax.x + window->SizeFull.x - decoration_total_width; scroll_target_x = CalcScrollEdgeSnap(scroll_target_x, snap_x_min, snap_x_max, window->ScrollTargetEdgeSnapDist.x, center_x_ratio); - scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - window->ScrollbarSizes.x); + } + scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - decoration_total_width); } if (window->ScrollTarget.y < FLT_MAX) { - float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); + float decoration_total_height = window->TitleBarHeight() + window->MenuBarHeight() + window->ScrollbarSizes.y; float center_y_ratio = window->ScrollTargetCenterRatio.y; float scroll_target_y = window->ScrollTarget.y; - float snap_y_min = 0.0f; - float snap_y_max = window->ScrollMax.y + window->Size.y - decoration_up_height; if (window->ScrollTargetEdgeSnapDist.y > 0.0f) + { + float snap_y_min = 0.0f; + float snap_y_max = window->ScrollMax.y + window->SizeFull.y - decoration_total_height; scroll_target_y = CalcScrollEdgeSnap(scroll_target_y, snap_y_min, snap_y_max, window->ScrollTargetEdgeSnapDist.y, center_y_ratio); - scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height); + } + scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - decoration_total_height); } scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f)); scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f)); @@ -7890,7 +7956,8 @@ void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio) { IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); - local_y -= window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect + const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect; + local_y -= decoration_up_height; window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); // Convert local position to scroll offset window->ScrollTargetCenterRatio.y = center_y_ratio; window->ScrollTargetEdgeSnapDist.y = 0.0f; @@ -7913,7 +7980,7 @@ void ImGui::SetScrollHereX(float center_x_ratio) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - float spacing_x = g.Style.ItemSpacing.x; + float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x); float target_pos_x = ImLerp(window->DC.LastItemRect.Min.x - spacing_x, window->DC.LastItemRect.Max.x + spacing_x, center_x_ratio); SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos @@ -7926,7 +7993,7 @@ void ImGui::SetScrollHereY(float center_y_ratio) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - float spacing_y = g.Style.ItemSpacing.y; + float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y); float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio); SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos @@ -8057,6 +8124,11 @@ void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags) OpenPopupEx(g.CurrentWindow->GetID(str_id), popup_flags); } +void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags) +{ + OpenPopupEx(id, popup_flags); +} + // Mark popup as open (toggle toward open state). // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). @@ -8316,10 +8388,21 @@ void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags } // This is a helper to handle the simplest case of associating one named popup to one given widget. -// - You can pass a NULL str_id to use the identifier of the last item. -// - You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters). -// - This is essentially the same as calling OpenPopupOnItemClick() + BeginPopup() but written to avoid -// computing the ID twice because BeginPopupContextXXX functions may be called very frequently. +// - To create a popup associated to the last item, you generally want to pass a NULL value to str_id. +// - To create a popup with a specific identifier, pass it in str_id. +// - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call. +// - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id. +// - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters). +// This is essentially the same as: +// id = str_id ? GetID(str_id) : GetItemID(); +// OpenPopupOnItemClick(str_id); +// return BeginPopup(id); +// Which is essentially the same as: +// id = str_id ? GetID(str_id) : GetItemID(); +// if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right)) +// OpenPopup(id); +// return BeginPopup(id); +// The main difference being that this is tweaked to avoid computing the ID twice. bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiWindow* window = GImGui->CurrentWindow; @@ -8494,13 +8577,13 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) //----------------------------------------------------------------------------- // FIXME-NAV: The existence of SetNavID vs SetFocusID properly needs to be clarified/reworked. -void ImGui::SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel) +void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel) { ImGuiContext& g = *GImGui; IM_ASSERT(g.NavWindow != NULL); IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu); g.NavId = id; - g.NavLayer = (ImGuiNavLayer)nav_layer; + g.NavLayer = nav_layer; g.NavFocusScopeId = focus_scope_id; g.NavWindow->NavLastIds[nav_layer] = id; g.NavWindow->NavRectRel[nav_layer] = rect_rel; @@ -8563,7 +8646,7 @@ static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect } // Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057 -static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) +static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -8694,7 +8777,7 @@ static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) return new_best; } -static void ImGui::NavApplyItemToResult(ImGuiNavMoveResult* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel) +static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel) { result->Window = window; result->ID = id; @@ -8709,7 +8792,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con //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. // return; - const ImGuiItemFlags item_flags = window->DC.ItemFlags; + const ImGuiItemFlags item_flags = g.CurrentItemFlags; const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); // Process Init Request @@ -8732,7 +8815,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy) if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) { - ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; + ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; #if IMGUI_DEBUG_NAV_SCORING // [DEBUG] Score all items in NavWindow at all times if (!g.NavMoveRequest) @@ -8759,7 +8842,6 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con g.NavLayer = window->DC.NavLayerCurrent; g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; g.NavIdIsAlive = true; - g.NavIdTabCounter = window->DC.FocusCounterTabStop; window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) } } @@ -8851,10 +8933,16 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) { ImGuiContext& g = *GImGui; IM_ASSERT(window == g.NavWindow); + + if (window->Flags & ImGuiWindowFlags_NoNavInputs) + { + g.NavId = g.NavFocusScopeId = 0; + return; + } + bool init_for_nav = false; - if (!(window->Flags & ImGuiWindowFlags_NoNavInputs)) - if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) - init_for_nav = true; + if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) + init_for_nav = true; IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer); if (init_for_nav) { @@ -8968,8 +9056,16 @@ static void ImGui::NavUpdate() io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; if (io.KeyShift) io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; - if (io.KeyAlt && !io.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu. + + // AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl) + // But also even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway. + if (io.KeyAlt && !io.KeyCtrl) io.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; + + // We automatically cancel toggling nav layer when any text has been typed while holding Alt. (See #370) + if (io.KeyAlt && !io.KeyCtrl && g.NavWindowingToggleLayer && io.InputQueueCharacters.Size > 0) + g.NavWindowingToggleLayer = false; + #undef NAV_MAP_KEY } memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration)); @@ -9035,6 +9131,11 @@ static void ImGui::NavUpdate() if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel)) ClearActiveID(); } + else if (g.NavLayer != ImGuiNavLayer_Main) + { + // Leave the "menu" layer + NavRestoreLayer(ImGuiNavLayer_Main); + } else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) { // Exit child window @@ -9051,11 +9152,6 @@ static void ImGui::NavUpdate() if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); } - else if (g.NavLayer != ImGuiNavLayer_Main) - { - // Leave the "menu" layer - NavRestoreLayer(ImGuiNavLayer_Main); - } else { // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were @@ -9145,7 +9241,7 @@ static void ImGui::NavUpdate() // *Fallback* manual-scroll with Nav directional keys when window has no navigable item ImGuiWindow* window = g.NavWindow; 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. - if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) + if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) { if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); @@ -9211,6 +9307,7 @@ static void ImGui::NavUpdateInitResult() return; // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) + // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently. IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name); SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel); if (g.NavInitRequestFromMove) @@ -9236,7 +9333,7 @@ static void ImGui::NavUpdateMoveResult() } // Select which result to use - ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; + ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page. if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) @@ -9303,7 +9400,7 @@ static float ImGui::NavUpdatePageUpPageDown() const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End); if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed { - if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll) + if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll) { // Fallback manual-scroll when window has no navigable item if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) @@ -9421,13 +9518,13 @@ static void ImGui::NavEndFrame() } } -static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N) +static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) { ImGuiContext& g = *GImGui; - for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--) - if (g.WindowsFocusOrder[i] == window) - return i; - return -1; + IM_UNUSED(g); + int order = window->FocusOrder; + IM_ASSERT(g.WindowsFocusOrder[order] == window); + return order; } static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N) @@ -9566,8 +9663,14 @@ static void ImGui::NavUpdateWindowing() if (apply_focus_window->NavLastIds[0] == 0) NavInitWindow(apply_focus_window, false); - // If the window only has a menu layer, select it directly - if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu)) + // If the window has ONLY a menu layer (no main layer), select it directly + // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame, + // so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since + // the target window as already been previewed once. + // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases, + // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask* + // won't be valid. + if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu)) g.NavLayer = ImGuiNavLayer_Menu; } if (apply_focus_window) @@ -9576,10 +9679,12 @@ static void ImGui::NavUpdateWindowing() // Apply menu/layer toggle if (apply_toggle_layer && g.NavWindow) { + ClearActiveID(); + // Move to parent menu if necessary ImGuiWindow* new_nav_window = g.NavWindow; while (new_nav_window->ParentWindow - && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0 + && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) new_nav_window = new_nav_window->ParentWindow; @@ -9593,7 +9698,7 @@ static void ImGui::NavUpdateWindowing() g.NavDisableMouseHover = true; // Reinitialize navigation when entering menu bar with the Alt key. - const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main; + const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main; if (new_nav_layer == ImGuiNavLayer_Menu) g.NavWindow->NavLastIds[new_nav_layer] = 0; NavRestoreLayer(new_nav_layer); @@ -9629,6 +9734,7 @@ void ImGui::NavUpdateWindowingOverlay() for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--) { ImGuiWindow* window = g.WindowsFocusOrder[n]; + IM_ASSERT(window != NULL); // Fix static analyzers if (!IsWindowNavFocusable(window)) continue; const char* label = window->Name; @@ -10545,9 +10651,9 @@ static void ImGui::UpdateViewportsNewFrame() ImGuiViewportP* viewport = g.Viewports[n]; // Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again. - viewport->WorkOffsetMin = viewport->CurrWorkOffsetMin; - viewport->WorkOffsetMax = viewport->CurrWorkOffsetMax; - viewport->CurrWorkOffsetMin = viewport->CurrWorkOffsetMax = ImVec2(0.0f, 0.0f); + viewport->WorkOffsetMin = viewport->BuildWorkOffsetMin; + viewport->WorkOffsetMax = viewport->BuildWorkOffsetMax; + viewport->BuildWorkOffsetMin = viewport->BuildWorkOffsetMax = ImVec2(0.0f, 0.0f); viewport->UpdateWorkRect(); } } @@ -10804,6 +10910,10 @@ static void MetricsHelpMarker(const char* desc) } } +#ifndef IMGUI_DISABLE_DEMO_WINDOWS +namespace ImGui { void ShowFontAtlas(ImFontAtlas* atlas); } +#endif + void ImGui::ShowMetricsWindow(bool* p_open) { if (!Begin("Dear ImGui Metrics/Debugger", p_open)) @@ -10999,14 +11109,22 @@ void ImGui::ShowMetricsWindow(bool* p_open) } // Details for Tables -#ifdef IMGUI_HAS_TABLE if (TreeNode("Tables", "Tables (%d)", g.Tables.GetSize())) { for (int n = 0; n < g.Tables.GetSize(); n++) DebugNodeTable(g.Tables.GetByIndex(n)); TreePop(); } -#endif // #ifdef IMGUI_HAS_TABLE + + // Details for Fonts +#ifndef IMGUI_DISABLE_DEMO_WINDOWS + ImFontAtlas* atlas = g.IO.Fonts; + if (TreeNode("Fonts", "Fonts (%d)", atlas->Fonts.Size)) + { + ShowFontAtlas(atlas); + TreePop(); + } +#endif // Details for Docking #ifdef IMGUI_HAS_DOCK @@ -11039,26 +11157,24 @@ void ImGui::ShowMetricsWindow(bool* p_open) BulletText("%s", g.SettingsHandlers[n].TypeName); TreePop(); } - if (TreeNode("SettingsWindows", "Settings packed json: Windows: %d bytes", g.SettingsWindows.size())) + if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size())) { for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) DebugNodeWindowSettings(settings); TreePop(); } -#ifdef IMGUI_HAS_TABLE - if (TreeNode("SettingsTables", "Settings packed json: Tables: %d bytes", g.SettingsTables.size())) + if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size())) { for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) DebugNodeTableSettings(settings); TreePop(); } -#endif // #ifdef IMGUI_HAS_TABLE #ifdef IMGUI_HAS_DOCK #endif // #ifdef IMGUI_HAS_DOCK - if (TreeNode("SettingsIniData", "Settings unpacked json (.ini): %d bytes", g.SettingsIniData.size())) + if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size())) { InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly); TreePop(); @@ -11069,7 +11185,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Misc Details if (TreeNode("Internal state")) { - const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); + const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad", "Nav", "Clipboard" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); Text("WINDOWING"); Indent(); @@ -11127,7 +11243,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) } } -#ifdef IMGUI_HAS_TABLE // Overlay: Display Tables Rectangles if (cfg->ShowTablesRects) { @@ -11154,7 +11269,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) } } } -#endif // #ifdef IMGUI_HAS_TABLE #ifdef IMGUI_HAS_DOCK // Overlay: Display Docking info @@ -11401,12 +11515,19 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) 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" : ""); BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems); - BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); - BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); - if (!window->NavRectRel[0].IsInverted()) - BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); - else - BulletText("NavRectRel[0]: "); + for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++) + { + ImRect r = window->NavRectRel[layer]; + if (r.Min.x >= r.Max.y && r.Min.y >= r.Max.y) + { + BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]); + continue; + } + 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); + if (IsItemHovered()) + GetForegroundDrawList(window)->AddRect(r.Min + window->Pos, r.Max + window->Pos, IM_COL32(255, 255, 0, 255)); + } + BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); } if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); } if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); } diff --git a/Depend/imgui/imgui.h b/Depend/imgui/imgui.h index 874ed8f..06a610d 100644 --- a/Depend/imgui/imgui.h +++ b/Depend/imgui/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.82 +// dear imgui, v1.83 // (headers) // Help: @@ -11,9 +11,9 @@ // - FAQ http://dearimgui.org/faq // - Homepage & latest https://github.com/ocornut/imgui // - Releases & changelog https://github.com/ocornut/imgui/releases -// - Gallery https://github.com/ocornut/imgui/issues/3488 (please post your screenshots/video there!) +// - Gallery https://github.com/ocornut/imgui/issues/3793 (please post your screenshots/video there!) +// - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there) // - Glossary https://github.com/ocornut/imgui/wiki/Glossary -// - Wiki https://github.com/ocornut/imgui/wiki // - Issues & support https://github.com/ocornut/imgui/issues // - Discussions https://github.com/ocornut/imgui/discussions @@ -60,8 +60,8 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) -#define IMGUI_VERSION "1.82" -#define IMGUI_VERSION_NUM 18200 +#define IMGUI_VERSION "1.83" +#define IMGUI_VERSION_NUM 18300 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -100,7 +100,20 @@ Index of this file: #define IM_FMTLIST(FMT) #endif +// Disable some of MSVC most aggressive Debug runtime checks in function header/footer (used in some simple/low-level functions) +#if defined(_MSC_VER) && !defined(__clang__) && !defined(IMGUI_DEBUG_PARANOID) +#define IM_MSVC_RUNTIME_CHECKS_OFF __pragma(runtime_checks("",off)) __pragma(check_stack(off)) __pragma(strict_gs_check(push,off)) +#define IM_MSVC_RUNTIME_CHECKS_RESTORE __pragma(runtime_checks("",restore)) __pragma(check_stack()) __pragma(strict_gs_check(pop)) +#else +#define IM_MSVC_RUNTIME_CHECKS_OFF +#define IM_MSVC_RUNTIME_CHECKS_RESTORE +#endif + // Warnings +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). +#endif #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wold-style-cast" @@ -227,6 +240,7 @@ typedef unsigned long long ImU64; // 64-bit unsigned integer (post C++11) #endif // 2D vector (often used to store positions or sizes) +IM_MSVC_RUNTIME_CHECKS_OFF struct ImVec2 { float x, y; @@ -249,6 +263,7 @@ struct ImVec4 IM_VEC4_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec4. #endif }; +IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] Dear ImGui end-user API functions @@ -376,7 +391,7 @@ namespace ImGui IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); // modify a style float variable. always use this if you modify the style after NewFrame(). IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); // modify a style ImVec2 variable. always use this if you modify the style after NewFrame(). IMGUI_API void PopStyleVar(int count = 1); - IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets + IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // == tab stop enable. Allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets IMGUI_API void PopAllowKeyboardFocus(); IMGUI_API void PushButtonRepeat(bool repeat); // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (using io.KeyRepeatDelay/io.KeyRepeatRate setting). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame. IMGUI_API void PopButtonRepeat(); @@ -507,8 +522,8 @@ namespace ImGui IMGUI_API bool DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", ImGuiSliderFlags flags = 0); IMGUI_API bool DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", ImGuiSliderFlags flags = 0); IMGUI_API bool DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", const char* format_max = NULL, ImGuiSliderFlags flags = 0); - IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0); - IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0); + IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0); + IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0); // Widgets: Regular Sliders // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped and can go off-bounds. @@ -614,13 +629,14 @@ namespace ImGui // - Use BeginMenuBar() on a window ImGuiWindowFlags_MenuBar to append to its menu bar. // - Use BeginMainMenuBar() to create a menu bar at the top of the screen and append to it. // - Use BeginMenu() to create a menu. You can call BeginMenu() multiple time with the same identifier to append more items to it. + // - Not that MenuItem() keyboardshortcuts are displayed as a convenience but _not processed_ by Dear ImGui at the moment. IMGUI_API bool BeginMenuBar(); // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set on parent window). IMGUI_API void EndMenuBar(); // only call EndMenuBar() if BeginMenuBar() returns true! IMGUI_API bool BeginMainMenuBar(); // create and append to a full screen menu-bar. IMGUI_API void EndMainMenuBar(); // only call EndMainMenuBar() if BeginMainMenuBar() returns true! IMGUI_API bool BeginMenu(const char* label, bool enabled = true); // create a sub-menu entry. only call EndMenu() if this returns true! IMGUI_API void EndMenu(); // only call EndMenu() if BeginMenu() returns true! - IMGUI_API bool MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true); // return true when activated. shortcuts are displayed for convenience but not processed by ImGui at the moment + IMGUI_API bool MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true); // return true when activated. IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true); // return true when activated + toggle (*p_selected) if p_selected != NULL // Tooltips @@ -650,18 +666,20 @@ namespace ImGui // - CloseCurrentPopup(): use inside the BeginPopup()/EndPopup() scope to close manually. // - CloseCurrentPopup() is called by default by Selectable()/MenuItem() when activated (FIXME: need some options). // - Use ImGuiPopupFlags_NoOpenOverExistingPopup to avoid opening a popup if there's already one at the same level. This is equivalent to e.g. testing for !IsAnyPopupOpen() prior to OpenPopup(). + // - Use IsWindowAppearing() after BeginPopup() to tell if a window just opened. IMGUI_API void OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags = 0); // call to mark popup as open (don't call every frame!). - IMGUI_API void OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // helper to open popup when clicked on last item. return true when just opened. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors) + IMGUI_API void OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags = 0); // id overload to facilitate calling from nested stacks + IMGUI_API void OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors) IMGUI_API void CloseCurrentPopup(); // manually close the popup we have begin-ed into. // Popups: open+begin combined functions helpers // - Helpers to do OpenPopup+BeginPopup where the Open action is triggered by e.g. hovering an item and right-clicking. // - They are convenient to easily create context menus, hence the name. // - IMPORTANT: Notice that BeginPopupContextXXX takes ImGuiPopupFlags just like OpenPopup() and unlike BeginPopup(). For full consistency, we may add ImGuiWindowFlags to the BeginPopupContextXXX functions in the future. // - IMPORTANT: we exceptionally default their flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter, so if you add other flags remember to re-add the ImGuiPopupFlags_MouseButtonRight. - IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked on last item. if you can pass a NULL str_id only if the previous item had an id. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp! + IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked on last item. Use str_id==NULL to associate the popup to previous item. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp! IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);// open+begin popup when clicked on current window. IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked in void (where there are no windows). - // Popups: test function + // Popups: query functions // - IsPopupOpen(): return true if the popup is open at the current BeginPopup() level of the popup stack. // - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId: return true if any popup is open at the current BeginPopup() level of the popup stack. // - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId + ImGuiPopupFlags_AnyPopupLevel: return true if any popup is open. @@ -723,6 +741,7 @@ namespace ImGui IMGUI_API int TableGetRowIndex(); // return current row index. IMGUI_API const char* TableGetColumnName(int column_n = -1); // return "" if column didn't have a name declared by TableSetupColumn(). Pass -1 to use current column. IMGUI_API ImGuiTableColumnFlags TableGetColumnFlags(int column_n = -1); // return column flags so you can query their Enabled/Visible/Sorted/Hovered status flags. Pass -1 to use current column. + IMGUI_API void TableSetColumnEnabled(int column_n, bool v);// change enabled/disabled state of a column, set to false to hide the column. Note that end-user can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) IMGUI_API void TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n = -1); // change the color of a cell, row, or column. See ImGuiTableBgTarget_ flags for details. // Legacy Columns API (2020: prefer using Tables!) @@ -777,7 +796,7 @@ namespace ImGui IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window. IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget. - // Item/Widgets Utilities + // Item/Widgets Utilities and Query Functions // - Most of the functions are referring to the previous Item that has been submitted. // - See Demo Window under "Widgets->Querying Status" for an interactive visualization of most of those functions. IMGUI_API bool IsItemHovered(ImGuiHoveredFlags flags = 0); // is the last item hovered? (and usable, aka not blocked by a popup, etc.). See ImGuiHoveredFlags for more options. @@ -802,7 +821,7 @@ namespace ImGui // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. // - In 'docking' branch with multi-viewport enabled, we extend this concept to have multiple active viewports. // - In the future we will extend this concept further to also represent Platform Monitor and support a "no main platform window" operation mode. - IMGUI_API ImGuiViewport* GetMainViewport(); // return primary/default viewport. + IMGUI_API ImGuiViewport* GetMainViewport(); // return primary/default viewport. This can never be NULL. // Miscellaneous Utilities IMGUI_API bool IsRectVisible(const ImVec2& size); // test if rectangle (of given size, starting from cursor position) is visible / not clipped. @@ -872,6 +891,7 @@ namespace ImGui IMGUI_API const char* SaveIniSettingsToMemory(size_t* out_ini_size = NULL); // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings. // Debug Utilities + // - This is used by the IMGUI_CHECKVERSION() macro. IMGUI_API bool DebugCheckVersionAndDataLayout(const char* version_str, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_drawvert, size_t sz_drawidx); // This is called by IMGUI_CHECKVERSION() macro. // Memory Allocators @@ -952,10 +972,7 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_NoUndoRedo = 1 << 16, // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). ImGuiInputTextFlags_CharsScientific = 1 << 17, // Allow 0123456789.+-*/eE (Scientific notation input) ImGuiInputTextFlags_CallbackResize = 1 << 18, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this) - ImGuiInputTextFlags_CallbackEdit = 1 << 19, // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active) - // [Internal] - ImGuiInputTextFlags_Multiline = 1 << 20, // For internal use by InputTextMultiline() - ImGuiInputTextFlags_NoMarkEdited = 1 << 21 // For internal use by functions using InputText() before reformatting data + ImGuiInputTextFlags_CallbackEdit = 1 << 19 // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active) // Obsolete names (will be removed soon) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -1631,6 +1648,7 @@ template void IM_DELETE(T* p) { if (p) { p->~T(); ImGui::MemFree(p // Do NOT use this class as a std::vector replacement in your own code! Many of the structures used by dear imgui can be safely initialized by a zero-memset. //----------------------------------------------------------------------------- +IM_MSVC_RUNTIME_CHECKS_OFF template struct ImVector { @@ -1689,6 +1707,7 @@ struct ImVector inline bool find_erase_unsorted(const T& v) { const T* it = find(v); if (it < Data + Size) { erase_unsorted(it); return true; } return false; } inline int index_from_ptr(const T* it) const { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; return (int)off; } }; +IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] ImGuiStyle @@ -1845,7 +1864,7 @@ struct ImGuiIO bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving! bool NavActive; // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. bool NavVisible; // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events). - float Framerate; // Application framerate estimate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames. + float Framerate; // Rough estimate of application framerate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames. int MetricsRenderVertices; // Vertices output during last call to Render() int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3 int MetricsRenderWindows; // Number of visible windows @@ -2224,6 +2243,9 @@ struct ImDrawCmd void* UserCallbackData; // 4-8 // The draw callback code can access this. ImDrawCmd() { memset(this, 0, sizeof(*this)); } // Also ensure our padding fields are zeroed + + // Since 1.83: returns ImTextureID associated with this draw call. Warning: DO NOT assume this is always same as 'TextureId' (we will change this function for an upcoming feature) + inline ImTextureID GetTexID() const { return TextureId; } }; // Vertex index, default to 16-bit @@ -2818,6 +2840,10 @@ enum ImDrawCornerFlags_ #pragma GCC diagnostic pop #endif +#ifdef _MSC_VER +#pragma warning (pop) +#endif + // Include imgui_user.h at the end of imgui.h (convenient for user to only explicitly include vanilla imgui.h) #ifdef IMGUI_INCLUDE_IMGUI_USER_H #include "imgui_user.h" diff --git a/Depend/imgui/imgui_demo.cpp b/Depend/imgui/imgui_demo.cpp index dd1ab23..5437301 100644 --- a/Depend/imgui/imgui_demo.cpp +++ b/Depend/imgui/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.82 +// dear imgui, v1.83 // (demo code) // Help: @@ -6,9 +6,9 @@ // - Newcomers, read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase. // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that. // Read imgui.cpp for more details, documentation and comments. -// Get latest version at https://github.com/ocornut/imgui +// Get the latest version at https://github.com/ocornut/imgui -// Message to the person tempted to delete this file when integrating Dear ImGui into their code base: +// Message to the person tempted to delete this file when integrating Dear ImGui into their codebase: // Do NOT remove this file from your project! Think again! It is the most useful reference code that you and other // coders will want to refer to and call. Have the ImGui::ShowDemoWindow() function wired in an always-available // debug menu of your game/app! Removing this file from your project is hindering access to documentation for everyone @@ -16,19 +16,19 @@ // Everything in this file will be stripped out by the linker if you don't call ImGui::ShowDemoWindow(). // If you want to link core Dear ImGui in your shipped builds but want a thorough guarantee that the demo will not be // linked, you can setup your imconfig.h with #define IMGUI_DISABLE_DEMO_WINDOWS and those functions will be empty. -// In other situation, whenever you have Dear ImGui available you probably want this to be available for reference. +// In another situation, whenever you have Dear ImGui available you probably want this to be available for reference. // Thank you, // -Your beloved friend, imgui_demo.cpp (which you won't delete) // Message to beginner C/C++ programmers about the meaning of the 'static' keyword: -// In this demo code, we frequently we use 'static' variables inside functions. A static variable persist across calls, +// In this demo code, we frequently use 'static' variables inside functions. A static variable persists across calls, // so it is essentially like a global variable but declared inside the scope of the function. We do this as a way to // gather code and data in the same place, to make the demo source code faster to read, faster to write, and smaller // in size. It also happens to be a convenient way of storing simple UI related information as long as your function // doesn't need to be reentrant or used in multiple threads. This might be a pattern you will want to use in your code, // but most of the real data you would be editing is likely going to be stored outside your functions. -// The Demo code in this file is designed to be easy to copy-and-paste in into your application! +// The Demo code in this file is designed to be easy to copy-and-paste into your application! // Because of this: // - We never omit the ImGui:: prefix when calling functions, even though most code here is in the same namespace. // - We try to declare static variables in the local scope, as close as possible to the code using them. @@ -54,6 +54,7 @@ Index of this file: // - sub section: ShowDemoWindowTables() // - sub section: ShowDemoWindowMisc() // [SECTION] About Window / ShowAboutWindow() +// [SECTION] Font Viewer / ShowFontAtlas() // [SECTION] Style Editor / ShowStyleEditor() // [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar() // [SECTION] Example App: Debug Console / ShowExampleAppConsole() @@ -92,7 +93,8 @@ Index of this file: // Visual Studio warnings #ifdef _MSC_VER -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#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). #endif // Clang/GCC warnings with -Weverything @@ -940,7 +942,7 @@ static void ShowDemoWindowWidgets() ImGuiIO& io = ImGui::GetIO(); ImGui::TextWrapped( "Below we are displaying the font texture (which is the only texture we have access to in this demo). " - "Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture json. " + "Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. " "Hover the texture for a zoomed view!"); // Below we are displaying the font texture because it is the only texture we have access to inside the demo! @@ -1553,7 +1555,7 @@ static void ShowDemoWindowWidgets() // Plot/Graph widgets are not very good. // Consider writing your own, or using a third-party one, see: // - ImPlot https://github.com/epezent/implot - // - others https://github.com/ocornut/imgui/wiki/Useful-Widgets + // - others https://github.com/ocornut/imgui/wiki/Useful-Extensions if (ImGui::TreeNode("Plots Widgets")) { static bool animate = true; @@ -1602,7 +1604,7 @@ static void ShowDemoWindowWidgets() }; static int func_type = 0, display_count = 70; ImGui::Separator(); - ImGui::SetNextItemWidth(100); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::Combo("func", &func_type, "Sin\0Saw\0"); ImGui::SameLine(); ImGui::SliderInt("Sample count", &display_count, 1, 400); @@ -2392,7 +2394,7 @@ static void ShowDemoWindowLayout() // the POV of the parent window). See 'Demo->Querying Status (Active/Focused/Hovered etc.)' for details. { static int offset_x = 0; - ImGui::SetNextItemWidth(100); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::DragInt("Offset X", &offset_x, 1.0f, -1000, 1000); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (float)offset_x); @@ -2414,15 +2416,15 @@ static void ShowDemoWindowLayout() if (ImGui::TreeNode("Widgets Width")) { + static float f = 0.0f; + static bool show_indented_items = true; + ImGui::Checkbox("Show indented items", &show_indented_items); + // Use SetNextItemWidth() to set the width of a single upcoming item. // Use PushItemWidth()/PopItemWidth() to set the width of a group of items. // In real code use you'll probably want to choose width values that are proportional to your font size // e.g. Using '20.0f * GetFontSize()' as width instead of '200.0f', etc. - static float f = 0.0f; - static bool show_indented_items = true; - ImGui::Checkbox("Show indented items", &show_indented_items); - ImGui::Text("SetNextItemWidth/PushItemWidth(100)"); ImGui::SameLine(); HelpMarker("Fixed width."); ImGui::PushItemWidth(100); @@ -2838,6 +2840,8 @@ static void ShowDemoWindowLayout() { for (int item = 0; item < 100; item++) { + if (item > 0) + ImGui::SameLine(); if (enable_track && item == track_item) { ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item); @@ -2847,7 +2851,6 @@ static void ShowDemoWindowLayout() { ImGui::Text("Item %d", item); } - ImGui::SameLine(); } } float scroll_x = ImGui::GetScrollX(); @@ -3191,46 +3194,84 @@ static void ShowDemoWindowPopups() if (ImGui::TreeNode("Context menus")) { + HelpMarker("\"Context\" functions are simple helpers to associate a Popup to a given Item or Window identifier."); + // BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing: - // if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right)) - // OpenPopup(id); - // return BeginPopup(id); - // For more advanced uses you may want to replicate and customize this code. - // See details in BeginPopupContextItem(). - static float value = 0.5f; - ImGui::Text("Value = %.3f (<-- right-click here)", value); - if (ImGui::BeginPopupContextItem("item context menu")) + // if (id == 0) + // id = GetItemID(); // Use last item id + // if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right)) + // OpenPopup(id); + // return BeginPopup(id); + // For advanced advanced uses you may want to replicate and customize this code. + // See more details in BeginPopupContextItem(). + + // Example 1 + // When used after an item that has an ID (e.g. Button), we can skip providing an ID to BeginPopupContextItem(), + // and BeginPopupContextItem() will use the last item ID as the popup ID. { - if (ImGui::Selectable("Set to zero")) value = 0.0f; - if (ImGui::Selectable("Set to PI")) value = 3.1415f; - ImGui::SetNextItemWidth(-FLT_MIN); - ImGui::DragFloat("##Value", &value, 0.1f, 0.0f, 0.0f); - ImGui::EndPopup(); + const char* names[5] = { "Label1", "Label2", "Label3", "Label4", "Label5" }; + for (int n = 0; n < 5; n++) + { + ImGui::Selectable(names[n]); + if (ImGui::BeginPopupContextItem()) // <-- use last item id as popup id + { + ImGui::Text("This a popup for \"%s\"!", names[n]); + if (ImGui::Button("Close")) + ImGui::CloseCurrentPopup(); + ImGui::EndPopup(); + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Right-click to open popup"); + } } - // We can also use OpenPopupOnItemClick() which is the same as BeginPopupContextItem() but without the - // Begin() call. So here we will make it that clicking on the text field with the right mouse button (1) - // will toggle the visibility of the popup above. - ImGui::Text("(You can also right-click me to open the same popup as above.)"); - ImGui::OpenPopupOnItemClick("item context menu", 1); - - // When used after an item that has an ID (e.g.Button), we can skip providing an ID to BeginPopupContextItem(). - // BeginPopupContextItem() will use the last item ID as the popup ID. - // In addition here, we want to include your editable label inside the button label. - // We use the ### operator to override the ID (read FAQ about ID for details) - static char name[32] = "Label1"; - char buf[64]; - sprintf(buf, "Button: %s###Button", name); // ### operator override ID ignoring the preceding label - ImGui::Button(buf); - if (ImGui::BeginPopupContextItem()) + // Example 2 + // Popup on a Text() element which doesn't have an identifier: we need to provide an identifier to BeginPopupContextItem(). + // Using an explicit identifier is also convenient if you want to activate the popups from different locations. { - ImGui::Text("Edit name:"); - ImGui::InputText("##edit", name, IM_ARRAYSIZE(name)); - if (ImGui::Button("Close")) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); + HelpMarker("Text() elements don't have stable identifiers so we need to provide one."); + static float value = 0.5f; + ImGui::Text("Value = %.3f <-- (1) right-click this value", value); + if (ImGui::BeginPopupContextItem("my popup")) + { + if (ImGui::Selectable("Set to zero")) value = 0.0f; + if (ImGui::Selectable("Set to PI")) value = 3.1415f; + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::DragFloat("##Value", &value, 0.1f, 0.0f, 0.0f); + ImGui::EndPopup(); + } + + // We can also use OpenPopupOnItemClick() to toggle the visibility of a given popup. + // Here we make it that right-clicking this other text element opens the same popup as above. + // The popup itself will be submitted by the code above. + ImGui::Text("(2) Or right-click this text"); + ImGui::OpenPopupOnItemClick("my popup", ImGuiPopupFlags_MouseButtonRight); + + // Back to square one: manually open the same popup. + if (ImGui::Button("(3) Or click this button")) + ImGui::OpenPopup("my popup"); + } + + // Example 3 + // When using BeginPopupContextItem() with an implicit identifier (NULL == use last item ID), + // we need to make sure your item identifier is stable. + // In this example we showcase altering the item label while preserving its identifier, using the ### operator (see FAQ). + { + HelpMarker("Showcase using a popup ID linked to item ID, with the item having a changing label + stable ID using the ### operator."); + static char name[32] = "Label1"; + char buf[64]; + sprintf(buf, "Button: %s###Button", name); // ### operator override ID ignoring the preceding label + ImGui::Button(buf); + if (ImGui::BeginPopupContextItem()) + { + ImGui::Text("Edit name:"); + ImGui::InputText("##edit", name, IM_ARRAYSIZE(name)); + if (ImGui::Button("Close")) + ImGui::CloseCurrentPopup(); + ImGui::EndPopup(); + } + ImGui::SameLine(); ImGui::Text("(<-- right-click here)"); } - ImGui::SameLine(); ImGui::Text("(<-- right-click here)"); ImGui::TreePop(); } @@ -5461,7 +5502,7 @@ static void ShowDemoWindowMisc() ImGui::InputText("3", buf, IM_ARRAYSIZE(buf)); ImGui::PushAllowKeyboardFocus(false); ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf)); - //ImGui::SameLine(); HelpMarker("Use ImGui::PushAllowKeyboardFocus(bool) to disable tabbing through certain widgets."); + ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); ImGui::PopAllowKeyboardFocus(); ImGui::InputText("5", buf, IM_ARRAYSIZE(buf)); ImGui::TreePop(); @@ -5487,6 +5528,7 @@ static void ShowDemoWindowMisc() if (focus_3) ImGui::SetKeyboardFocusHere(); ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf)); if (ImGui::IsItemActive()) has_focus = 3; + ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); ImGui::PopAllowKeyboardFocus(); if (has_focus) @@ -5702,31 +5744,15 @@ void ImGui::ShowAboutWindow(bool* p_open) } //----------------------------------------------------------------------------- -// [SECTION] Style Editor / ShowStyleEditor() +// [SECTION] Font viewer / ShowFontAtlas() //----------------------------------------------------------------------------- -// - ShowStyleSelector() // - ShowFontSelector() -// - ShowStyleEditor() +// - ShowFont() +// - ShowFontAtlas() //----------------------------------------------------------------------------- -// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options. -// Here we use the simplified Combo() api that packs items into a single literal string. -// Useful for quick combo boxes where the choices are known locally. -bool ImGui::ShowStyleSelector(const char* label) -{ - static int style_idx = -1; - if (ImGui::Combo(label, &style_idx, "Dark\0Light\0Classic\0")) - { - switch (style_idx) - { - case 0: ImGui::StyleColorsDark(); break; - case 1: ImGui::StyleColorsLight(); break; - case 2: ImGui::StyleColorsClassic(); break; - } - return true; - } - return false; -} +// This isn't worth putting in public API but we want Metrics to use it +namespace ImGui { void ShowFontAtlas(ImFontAtlas* atlas); } // Demo helper function to select among loaded fonts. // Here we use the regular BeginCombo()/EndCombo() api which is more the more flexible one. @@ -5755,7 +5781,7 @@ void ImGui::ShowFontSelector(const char* label) } // [Internal] Display details for a single font, called by ShowStyleEditor(). -static void NodeFont(ImFont* font) +static void ShowFont(ImFont* font) { ImGuiIO& io = ImGui::GetIO(); ImGuiStyle& style = ImGui::GetStyle(); @@ -5765,9 +5791,13 @@ static void NodeFont(ImFont* font) if (!font_details_opened) return; + // Display preview text ImGui::PushFont(font); ImGui::Text("The quick brown fox jumps over the lazy dog"); ImGui::PopFont(); + + // Display details + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // Scale only this font ImGui::SameLine(); HelpMarker( "Note than the default embedded font is NOT meant to be scaled.\n\n" @@ -5785,9 +5815,10 @@ static void NodeFont(ImFont* font) if (const ImFontConfig* cfg = &font->ConfigData[config_i]) ImGui::BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)", config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y); + + // Display all glyphs of the fonts in separate pages of 256 characters if (ImGui::TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) { - // Display all glyphs of the fonts in separate pages of 256 characters const ImU32 glyph_col = ImGui::GetColorU32(ImGuiCol_Text); for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256) { @@ -5842,6 +5873,50 @@ static void NodeFont(ImFont* font) ImGui::TreePop(); } +void ImGui::ShowFontAtlas(ImFontAtlas* atlas) +{ + for (int i = 0; i < atlas->Fonts.Size; i++) + { + ImFont* font = atlas->Fonts[i]; + ImGui::PushID(font); + ShowFont(font); + ImGui::PopID(); + } + if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) + { + ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); + ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0, 0), ImVec2(1, 1), tint_col, border_col); + ImGui::TreePop(); + } +} + +//----------------------------------------------------------------------------- +// [SECTION] Style Editor / ShowStyleEditor() +//----------------------------------------------------------------------------- +// - ShowStyleSelector() +// - ShowStyleEditor() +//----------------------------------------------------------------------------- + +// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options. +// Here we use the simplified Combo() api that packs items into a single literal string. +// Useful for quick combo boxes where the choices are known locally. +bool ImGui::ShowStyleSelector(const char* label) +{ + static int style_idx = -1; + if (ImGui::Combo(label, &style_idx, "Dark\0Light\0Classic\0")) + { + switch (style_idx) + { + case 0: ImGui::StyleColorsDark(); break; + case 1: ImGui::StyleColorsLight(); break; + case 2: ImGui::StyleColorsClassic(); break; + } + return true; + } + return false; +} + void ImGui::ShowStyleEditor(ImGuiStyle* ref) { // You can pass in a reference ImGuiStyle structure to compare to, revert to and save to @@ -5998,21 +6073,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGuiIO& io = ImGui::GetIO(); ImFontAtlas* atlas = io.Fonts; HelpMarker("Read FAQ and docs/FONTS.md for details on font loading."); - ImGui::PushItemWidth(120); - for (int i = 0; i < atlas->Fonts.Size; i++) - { - ImFont* font = atlas->Fonts[i]; - ImGui::PushID(font); - NodeFont(font); - ImGui::PopID(); - } - if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) - { - ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); - ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); - ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0, 0), ImVec2(1, 1), tint_col, border_col); - ImGui::TreePop(); - } + ImGui::ShowFontAtlas(atlas); // Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below. // (we enforce hard clamping manually as by default DragFloat/SliderFloat allows CTRL+Click text to get out of bounds). @@ -6024,6 +6085,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) "rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\n" "Using those settings here will give you poor quality results."); static float window_scale = 1.0f; + ImGui::PushItemWidth(ImGui::GetFontSize() * 8); if (ImGui::DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp)) // Scale only this window ImGui::SetWindowFontScale(window_scale); ImGui::DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp); // Scale everything @@ -6040,10 +6102,10 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::Checkbox("Anti-aliased lines use texture", &style.AntiAliasedLinesUseTex); ImGui::SameLine(); - HelpMarker("Faster lines using texture json. Require backend to render with bilinear filtering (not point/nearest filtering)."); + HelpMarker("Faster lines using texture data. Require backend to render with bilinear filtering (not point/nearest filtering)."); ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); - ImGui::PushItemWidth(100); + ImGui::PushItemWidth(ImGui::GetFontSize() * 8); ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f"); if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f; @@ -6298,7 +6360,7 @@ struct ExampleAppConsole ImGui::TextWrapped( "This example implements a console with basic coloring, completion (TAB key) and history (Up/Down keys). A more elaborate " - "implementation may want to store entries along with extra json such as timestamp, emitter, etc."); + "implementation may want to store entries along with extra data such as timestamp, emitter, etc."); ImGui::TextWrapped("Enter 'HELP' for help."); // TODO: display items starting from the bottom @@ -6857,7 +6919,7 @@ static void ShowExampleAppPropertyEditor(bool* p_open) HelpMarker( "This example shows how you may implement a property editor using two columns.\n" - "All objects/fields json are dummies here.\n" + "All objects/fields data are dummies here.\n" "Remember that in many simple cases, you can use ImGui::SameLine(xxx) to position\n" "your cursor horizontally instead of using the Columns() API."); diff --git a/Depend/imgui/imgui_draw.cpp b/Depend/imgui/imgui_draw.cpp index c41a1ba..54490ed 100644 --- a/Depend/imgui/imgui_draw.cpp +++ b/Depend/imgui/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.82 +// dear imgui, v1.83 // (drawing and font code) /* @@ -54,9 +54,12 @@ Index of this file: // Visual Studio warnings #ifdef _MSC_VER -#pragma warning (disable: 4127) // condition expression is constant -#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#pragma warning (disable: 4127) // condition expression is constant +#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#pragma warning (disable: 6255) // [Static Analyzer] _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead. +#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). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer) #endif // Clang/GCC warnings with -Weverything @@ -105,6 +108,9 @@ namespace IMGUI_STB_NAMESPACE #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration +#pragma warning (disable: 6011) // (stb_rectpack) Dereferencing NULL pointer 'cur->next'. +#pragma warning (disable: 6385) // (stb_truetype) Reading invalid data from 'buffer': the readable size is '_Old_3`kernel_width' bytes, but '3' bytes may be read. +#pragma warning (disable: 28182) // (stb_rectpack) Dereferencing NULL pointer. 'cur' contains the same NULL value as 'cur->next' did. #endif #if defined(__clang__) @@ -145,7 +151,7 @@ namespace IMGUI_STB_NAMESPACE #define STBTT_sqrt(x) ImSqrt(x) #define STBTT_pow(x,y) ImPow(x,y) #define STBTT_fabs(x) ImFabs(x) -#define STBTT_ifloor(x) ((int)ImFloorStd(x)) +#define STBTT_ifloor(x) ((int)ImFloorSigned(x)) #define STBTT_iceil(x) ((int)ImCeil(x)) #define STBTT_STATIC #define STB_TRUETYPE_IMPLEMENTATION @@ -688,8 +694,9 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c // On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superfluous function calls to optimize debug/non-inlined builds. // Those macros expects l-values. -#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = 1.0f / ImSqrt(d2); VX *= inv_len; VY *= inv_len; } } while (0) -#define IM_FIXNORMAL2F(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 < 0.5f) d2 = 0.5f; float inv_lensq = 1.0f / d2; VX *= inv_lensq; VY *= inv_lensq; } while (0) +#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImRsqrt(d2); VX *= inv_len; VY *= inv_len; } } while (0) +#define IM_FIXNORMAL2F_MAX_INVLEN2 100.0f // 500.0f (see #4053, #3366) +#define IM_FIXNORMAL2F(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.000001f) { float inv_len2 = 1.0f / d2; if (inv_len2 > IM_FIXNORMAL2F_MAX_INVLEN2) inv_len2 = IM_FIXNORMAL2F_MAX_INVLEN2; VX *= inv_len2; VY *= inv_len2; } } while (0) // TODO: Thickness anti-aliased lines cap are missing their AA fringe. // We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds. @@ -1037,7 +1044,6 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_ _Path.push_back(center); return; } - IM_ASSERT(a_min_sample <= a_max_sample); // Calculate arc auto segment step size if (a_step <= 0) @@ -1046,17 +1052,7 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_ // Make sure we never do steps larger than one quarter of the circle a_step = ImClamp(a_step, 1, IM_DRAWLIST_ARCFAST_TABLE_SIZE / 4); - // Normalize a_min_sample to always start lie in [0..IM_DRAWLIST_ARCFAST_SAMPLE_MAX] range. - if (a_min_sample < 0) - { - int normalized_sample = a_min_sample % IM_DRAWLIST_ARCFAST_SAMPLE_MAX; - if (normalized_sample < 0) - normalized_sample += IM_DRAWLIST_ARCFAST_SAMPLE_MAX; - a_max_sample += (normalized_sample - a_min_sample); - a_min_sample = normalized_sample; - } - - const int sample_range = a_max_sample - a_min_sample; + const int sample_range = ImAbs(a_max_sample - a_min_sample); const int a_next_step = a_step; int samples = sample_range + 1; @@ -1082,16 +1078,40 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_ ImVec2* out_ptr = _Path.Data + (_Path.Size - samples); int sample_index = a_min_sample; - for (int a = a_min_sample; a <= a_max_sample; a += a_step, sample_index += a_step, a_step = a_next_step) + if (sample_index < 0 || sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX) { - // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more - if (sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX) - sample_index -= IM_DRAWLIST_ARCFAST_SAMPLE_MAX; + sample_index = sample_index % IM_DRAWLIST_ARCFAST_SAMPLE_MAX; + if (sample_index < 0) + sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX; + } - const ImVec2 s = _Data->ArcFastVtx[sample_index]; - out_ptr->x = center.x + s.x * radius; - out_ptr->y = center.y + s.y * radius; - out_ptr++; + if (a_max_sample >= a_min_sample) + { + for (int a = a_min_sample; a <= a_max_sample; a += a_step, sample_index += a_step, a_step = a_next_step) + { + // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more + if (sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX) + sample_index -= IM_DRAWLIST_ARCFAST_SAMPLE_MAX; + + const ImVec2 s = _Data->ArcFastVtx[sample_index]; + out_ptr->x = center.x + s.x * radius; + out_ptr->y = center.y + s.y * radius; + out_ptr++; + } + } + else + { + for (int a = a_min_sample; a >= a_max_sample; a -= a_step, sample_index -= a_step, a_step = a_next_step) + { + // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more + if (sample_index < 0) + sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX; + + const ImVec2 s = _Data->ArcFastVtx[sample_index]; + out_ptr->x = center.x + s.x * radius; + out_ptr->y = center.y + s.y * radius; + out_ptr++; + } } if (extra_max_sample) @@ -1116,7 +1136,6 @@ void ImDrawList::_PathArcToN(const ImVec2& center, float radius, float a_min, fl _Path.push_back(center); return; } - IM_ASSERT(a_min <= a_max); // Note that we are adding a point at both a_min and a_max. // If you are trying to draw a full closed circle you don't want the overlapping points! @@ -1136,7 +1155,6 @@ void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_ _Path.push_back(center); return; } - IM_ASSERT(a_min_of_12 <= a_max_of_12); _PathArcToFastEx(center, radius, a_min_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, a_max_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 0); } @@ -1147,7 +1165,6 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa _Path.push_back(center); return; } - IM_ASSERT(a_min <= a_max); if (num_segments > 0) { @@ -1158,28 +1175,33 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa // Automatic segment count if (radius <= _Data->ArcFastRadiusCutoff) { + const bool a_is_reverse = a_max < a_min; + // We are going to use precomputed values for mid samples. // Determine first and last sample in lookup table that belong to the arc. - const int a_min_sample = (int)ImCeil(IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f)); - const int a_max_sample = (int)( IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f)); - const int a_mid_samples = ImMax(a_max_sample - a_min_sample, 0); + const float a_min_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f); + const float a_max_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f); + + const int a_min_sample = a_is_reverse ? (int)ImFloorSigned(a_min_sample_f) : (int)ImCeil(a_min_sample_f); + const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloorSigned(a_max_sample_f); + const int a_mid_samples = a_is_reverse ? ImMax(a_min_sample - a_max_sample, 0) : ImMax(a_max_sample - a_min_sample, 0); const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX; const float a_max_segment_angle = a_max_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX; - const bool a_emit_start = (a_min_segment_angle - a_min) > 0.0f; - const bool a_emit_end = (a_max - a_max_segment_angle) > 0.0f; + const bool a_emit_start = (a_min_segment_angle - a_min) != 0.0f; + const bool a_emit_end = (a_max - a_max_segment_angle) != 0.0f; _Path.reserve(_Path.Size + (a_mid_samples + 1 + (a_emit_start ? 1 : 0) + (a_emit_end ? 1 : 0))); if (a_emit_start) _Path.push_back(ImVec2(center.x + ImCos(a_min) * radius, center.y + ImSin(a_min) * radius)); - if (a_max_sample >= a_min_sample) + if (a_mid_samples > 0) _PathArcToFastEx(center, radius, a_min_sample, a_max_sample, 0); if (a_emit_end) _Path.push_back(ImVec2(center.x + ImCos(a_max) * radius, center.y + ImSin(a_max) * radius)); } else { - const float arc_length = a_max - a_min; + const float arc_length = ImAbs(a_max - a_min); const int circle_segment_count = _CalcCircleAutoSegmentCount(radius); const int arc_segment_count = ImMax((int)ImCeil(circle_segment_count * arc_length / (IM_PI * 2.0f)), (int)(2.0f * IM_PI / arc_length)); _PathArcToN(center, radius, a_min, a_max, arc_segment_count); @@ -3469,6 +3491,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons return text_size; } +// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound. void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, ImWchar c) const { const ImFontGlyph* glyph = FindGlyph(c); @@ -3483,6 +3506,7 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col draw_list->PrimRectUV(ImVec2(pos.x + glyph->X0 * scale, pos.y + glyph->Y0 * scale), ImVec2(pos.x + glyph->X1 * scale, pos.y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col); } +// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound. void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const { if (!text_end) @@ -3752,7 +3776,7 @@ void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, Im if (font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2])) { pos -= offset; - const ImTextureID tex_id = font_atlas->TexID; + ImTextureID tex_id = font_atlas->TexID; draw_list->PushTextureID(tex_id); draw_list->AddImage(tex_id, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow); draw_list->AddImage(tex_id, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow); diff --git a/Depend/imgui/imgui_impl_dx11.cpp b/Depend/imgui/imgui_impl_dx11.cpp index f114cdc..5cb2fd0 100644 --- a/Depend/imgui/imgui_impl_dx11.cpp +++ b/Depend/imgui/imgui_impl_dx11.cpp @@ -11,6 +11,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-05-19: DirectX11: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-02-18: DirectX11: Change blending equation to preserve alpha in output buffer. // 2019-08-01: DirectX11: Fixed code querying the Geometry Shader state (would generally error with Debug layer enabled). // 2019-07-21: DirectX11: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX10_RenderDrawData. Clearing Hull/Domain/Compute shaders without backup/restore. @@ -198,7 +199,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) DXGI_FORMAT IndexBufferFormat; ID3D11InputLayout* InputLayout; }; - BACKUP_DX11_STATE old; + BACKUP_DX11_STATE old = {}; old.ScissorRectsCount = old.ViewportsCount = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects); ctx->RSGetViewports(&old.ViewportsCount, old.Viewports); @@ -248,7 +249,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) ctx->RSSetScissorRects(1, &r); // Bind texture, Draw - ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->TextureId; + ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->GetTexID(); ctx->PSSetShaderResources(0, 1, &texture_srv); ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset); } @@ -305,6 +306,7 @@ static void ImGui_ImplDX11_CreateFontsTexture() subResource.SysMemPitch = desc.Width * 4; subResource.SysMemSlicePitch = 0; g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); + IM_ASSERT(pTexture != NULL); // Create texture view D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; diff --git a/Depend/imgui/imgui_impl_dx9.cpp b/Depend/imgui/imgui_impl_dx9.cpp index b8044d9..419b187 100644 --- a/Depend/imgui/imgui_impl_dx9.cpp +++ b/Depend/imgui/imgui_impl_dx9.cpp @@ -11,6 +11,9 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-05-19: DirectX9: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) +// 2021-04-23: DirectX9: Explicitly setting up more graphics states to increase compatibility with unusual non-default states. +// 2021-03-18: DirectX9: Calling IDirect3DStateBlock9::Capture() after CreateStateBlock() as a workaround for state restoring issues (see #3857). // 2021-03-03: DirectX9: Added support for IMGUI_USE_BGRA_PACKED_COLOR in user's imconfig file. // 2021-02-18: DirectX9: Change blending equation to preserve alpha in output buffer. // 2019-05-29: DirectX9: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. @@ -65,11 +68,13 @@ static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data) // Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient) g_pd3dDevice->SetPixelShader(NULL); g_pd3dDevice->SetVertexShader(NULL); + g_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); + g_pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); + g_pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); + g_pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); - g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE); g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE); g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); - g_pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); g_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); @@ -77,8 +82,12 @@ static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data) g_pd3dDevice->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE); g_pd3dDevice->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA); g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); - g_pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); g_pd3dDevice->SetRenderState(D3DRS_FOGENABLE, FALSE); + g_pd3dDevice->SetRenderState(D3DRS_RANGEFOGENABLE, FALSE); + g_pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, FALSE); + g_pd3dDevice->SetRenderState(D3DRS_STENCILENABLE, FALSE); + g_pd3dDevice->SetRenderState(D3DRS_CLIPPING, TRUE); + g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE); g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); @@ -137,6 +146,11 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) IDirect3DStateBlock9* d3d9_state_block = NULL; if (g_pd3dDevice->CreateStateBlock(D3DSBT_ALL, &d3d9_state_block) < 0) return; + if (d3d9_state_block->Capture() < 0) + { + d3d9_state_block->Release(); + return; + } // Backup the DX9 transform (DX9 documentation suggests that it is included in the StateBlock but it doesn't appear to) D3DMATRIX last_world, last_view, last_projection; @@ -144,16 +158,25 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) g_pd3dDevice->GetTransform(D3DTS_VIEW, &last_view); g_pd3dDevice->GetTransform(D3DTS_PROJECTION, &last_projection); + // Allocate buffers + CUSTOMVERTEX* vtx_dst; + ImDrawIdx* idx_dst; + if (g_pVB->Lock(0, (UINT)(draw_data->TotalVtxCount * sizeof(CUSTOMVERTEX)), (void**)&vtx_dst, D3DLOCK_DISCARD) < 0) + { + d3d9_state_block->Release(); + return; + } + if (g_pIB->Lock(0, (UINT)(draw_data->TotalIdxCount * sizeof(ImDrawIdx)), (void**)&idx_dst, D3DLOCK_DISCARD) < 0) + { + g_pVB->Unlock(); + d3d9_state_block->Release(); + return; + } + // Copy and convert all vertices into a single contiguous buffer, convert colors to DX9 default format. // FIXME-OPT: This is a minor waste of resource, the ideal is to use imconfig.h and // 1) to avoid repacking colors: #define IMGUI_USE_BGRA_PACKED_COLOR // 2) to avoid repacking vertices: #define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; } - CUSTOMVERTEX* vtx_dst; - ImDrawIdx* idx_dst; - if (g_pVB->Lock(0, (UINT)(draw_data->TotalVtxCount * sizeof(CUSTOMVERTEX)), (void**)&vtx_dst, D3DLOCK_DISCARD) < 0) - return; - if (g_pIB->Lock(0, (UINT)(draw_data->TotalIdxCount * sizeof(ImDrawIdx)), (void**)&idx_dst, D3DLOCK_DISCARD) < 0) - return; for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; @@ -204,7 +227,7 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) else { const RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) }; - const LPDIRECT3DTEXTURE9 texture = (LPDIRECT3DTEXTURE9)pcmd->TextureId; + const LPDIRECT3DTEXTURE9 texture = (LPDIRECT3DTEXTURE9)pcmd->GetTexID(); g_pd3dDevice->SetTexture(0, texture); g_pd3dDevice->SetScissorRect(&r); g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, pcmd->VtxOffset + global_vtx_offset, 0, (UINT)cmd_list->VtxBuffer.Size, pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3); diff --git a/Depend/imgui/imgui_impl_win32.cpp b/Depend/imgui/imgui_impl_win32.cpp index 04e462c..a198da9 100644 --- a/Depend/imgui/imgui_impl_win32.cpp +++ b/Depend/imgui/imgui_impl_win32.cpp @@ -25,13 +25,14 @@ // Using XInput for gamepad (will load DLL dynamically) #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD -#include +#include typedef DWORD (WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*); typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); #endif // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-03-23: Inputs: Clearing keyboard down array when losing focus (WM_KILLFOCUS). // 2021-02-18: Added ImGui_ImplWin32_EnableAlphaCompositing(). Non Visual Studio users will need to link with dwmapi.lib (MinGW/gcc: use -ldwmapi). // 2021-02-17: Fixed ImGui_ImplWin32_EnableDpiAwareness() attempting to get SetProcessDpiAwareness from shcore.dll on Windows 8 whereas it is only supported on Windows 8.1. // 2021-01-25: Inputs: Dynamically loading XInput DLL. @@ -134,7 +135,7 @@ bool ImGui_ImplWin32_Init(void* hwnd) break; } #endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - + return true; } @@ -148,7 +149,7 @@ void ImGui_ImplWin32_Shutdown() g_XInputGetCapabilities = NULL; g_XInputGetState = NULL; #endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - + g_hWnd = NULL; g_Time = 0; g_TicksPerSecond = 0; @@ -193,6 +194,7 @@ static bool ImGui_ImplWin32_UpdateMouseCursor() static void ImGui_ImplWin32_UpdateMousePos() { ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(g_hWnd != 0); // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) if (io.WantSetMousePos) @@ -372,6 +374,9 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA if (wParam < 256) io.KeysDown[wParam] = 0; return 0; + case WM_KILLFOCUS: + memset(io.KeysDown, 0, sizeof(io.KeysDown)); + return 0; case WM_CHAR: // You can also use ToAscii()+GetKeyboardState() to retrieve characters. if (wParam > 0 && wParam < 0x10000) diff --git a/Depend/imgui/imgui_internal.h b/Depend/imgui/imgui_internal.h index ea50a49..8006246 100644 --- a/Depend/imgui/imgui_internal.h +++ b/Depend/imgui/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.82 +// dear imgui, v1.83 // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! @@ -51,10 +51,19 @@ Index of this file: #include // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf #include // INT_MIN, INT_MAX +// Enable SSE intrinsics if available +#if defined __SSE__ || defined __x86_64__ || defined _M_X64 +#define IMGUI_ENABLE_SSE +#include +#endif + // Visual Studio warnings #ifdef _MSC_VER #pragma warning (push) -#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) +#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) +#pragma warning (disable: 26812) // The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer) +#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). + #endif // Clang/GCC warnings with -Weverything @@ -64,6 +73,7 @@ Index of this file: #pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' #endif #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' +#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok, for ImFloorSigned() #pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h #pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h #pragma clang diagnostic ignored "-Wold-style-cast" @@ -106,7 +116,7 @@ struct ImGuiGroupData; // Stacked storage data for BeginGroup()/End struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box struct ImGuiLastItemDataBackup; // Backup and restore IsItemHovered() internal data struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only -struct ImGuiNavMoveResult; // Result of a gamepad/keyboard directional navigation move query result +struct ImGuiNavItemData; // Result of a gamepad/keyboard directional navigation move query result struct ImGuiMetricsConfig; // Storage for ShowMetricsWindow() and DebugNodeXXX() functions struct ImGuiNextWindowData; // Storage for SetNextWindow** functions struct ImGuiNextItemData; // Storage for SetNextItem** functions @@ -120,15 +130,17 @@ struct ImGuiTabBar; // Storage for a tab bar struct ImGuiTabItem; // Storage for a tab item (within a tab bar) struct ImGuiTable; // Storage for a table struct ImGuiTableColumn; // Storage for one column of a table +struct ImGuiTableTempData; // Temporary storage for one table (one per table in the stack), shared between tables. struct ImGuiTableSettings; // Storage for a table .ini settings struct ImGuiTableColumnsSettings; // Storage for a column .ini settings struct ImGuiWindow; // Storage for one window -struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame) +struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame, in practice we currently keep it for each window) struct ImGuiWindowSettings; // Storage for a window .ini settings (we keep one of those even if the actual window wasn't instanced during this session) // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag() +typedef int ImGuiItemAddFlags; // -> enum ImGuiItemAddFlags_ // Flags: for ItemAdd() typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns() typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight() @@ -227,6 +239,13 @@ namespace ImStb #define IMGUI_CDECL #endif +// Warnings +#if defined(_MSC_VER) && !defined(__clang__) +#define IM_MSVC_WARNING_SUPPRESS(XXXX) __pragma(warning(suppress: XXXX)) +#else +#define IM_MSVC_WARNING_SUPPRESS(XXXX) +#endif + // Debug Tools // Use 'Metrics->Tools->Item Picker' to break into the call-stack of a specific item. #ifndef IM_DEBUG_BREAK @@ -314,6 +333,7 @@ IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, cons // We are keeping those disabled by default so they don't leak in user space, to allow user enabling implicit cast operators between ImVec2 and their own types (using IM_VEC2_CLASS_EXTRA etc.) // We unfortunately don't have a unary- operator for ImVec2 because this would needs to be defined inside the class itself. #ifdef IMGUI_DEFINE_MATH_OPERATORS +IM_MSVC_RUNTIME_CHECKS_OFF static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x * rhs, lhs.y * rhs); } static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x / rhs, lhs.y / rhs); } static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); } @@ -329,6 +349,7 @@ static inline ImVec2& operator/=(ImVec2& lhs, const ImVec2& rhs) static inline ImVec4 operator+(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); } static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w); } static inline ImVec4 operator*(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); } +IM_MSVC_RUNTIME_CHECKS_RESTORE #endif // Helpers: File System @@ -354,6 +375,7 @@ IMGUI_API ImU64 ImFileWrite(const void* data, ImU64 size, ImU64 coun IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size = NULL, int padding_bytes = 0); // Helpers: Maths +IM_MSVC_RUNTIME_CHECKS_OFF // - Wrapper for standard libs functions. (Note that imgui_demo.cpp does _not_ use them to keep the code easy to copy) #ifndef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS #define ImFabs(X) fabsf(X) @@ -364,16 +386,23 @@ IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* #define ImAcos(X) acosf(X) #define ImAtan2(Y, X) atan2f((Y), (X)) #define ImAtof(STR) atof(STR) -#define ImFloorStd(X) floorf(X) // We already uses our own ImFloor() { return (float)(int)v } internally so the standard one wrapper is named differently (it's used by e.g. stb_truetype) +//#define ImFloorStd(X) floorf(X) // We use our own, see ImFloor() and ImFloorSigned() #define ImCeil(X) ceilf(X) static inline float ImPow(float x, float y) { return powf(x, y); } // DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision static inline double ImPow(double x, double y) { return pow(x, y); } static inline float ImLog(float x) { return logf(x); } // DragBehaviorT/SliderBehaviorT uses ImLog with either float/double and need the precision static inline double ImLog(double x) { return log(x); } +static inline int ImAbs(int x) { return x < 0 ? -x : x; } static inline float ImAbs(float x) { return fabsf(x); } static inline double ImAbs(double x) { return fabs(x); } static inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : ((x > 0.0f) ? 1.0f : 0.0f); } // Sign operator - returns -1, 0 or 1 based on sign of argument static inline double ImSign(double x) { return (x < 0.0) ? -1.0 : ((x > 0.0) ? 1.0 : 0.0); } +#ifdef IMGUI_ENABLE_SSE +static inline float ImRsqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); } +#else +static inline float ImRsqrt(float x) { return 1.0f / sqrtf(x); } +#endif +static inline double ImRsqrt(double x) { return 1.0 / sqrt(x); } #endif // - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support variety of types: signed/unsigned int/long long float/double // (Exceptionally using templates here but we could also redefine them for those types) @@ -394,14 +423,16 @@ static inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t) static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } static inline float ImLengthSqr(const ImVec2& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y); } static inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); } -static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return 1.0f / ImSqrt(d); return fail_value; } +static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; } static inline float ImFloor(float f) { return (float)(int)(f); } +static inline float ImFloorSigned(float f) { return (float)((f >= 0 || (int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf() static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); } static inline int ImModPositive(int a, int b) { return (a + b) % b; } static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); } static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } +IM_MSVC_RUNTIME_CHECKS_RESTORE // Helpers: Geometry IMGUI_API ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t); @@ -417,6 +448,7 @@ IMGUI_API ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy); // Helper: ImVec1 (1D vector) // (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches) +IM_MSVC_RUNTIME_CHECKS_OFF struct ImVec1 { float x; @@ -470,6 +502,7 @@ struct IMGUI_API ImRect bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; } ImVec4 ToVec4() const { return ImVec4(Min.x, Min.y, Max.x, Max.y); } }; +IM_MSVC_RUNTIME_CHECKS_RESTORE // Helper: ImBitArray inline bool ImBitArrayTestBit(const ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); return (arr[n >> 5] & mask) != 0; } @@ -489,12 +522,12 @@ inline void ImBitArraySetBitRange(ImU32* arr, int n, int n2) // Works on ran } // Helper: ImBitArray class (wrapper over ImBitArray functions) -// Store 1-bit per value. NOT CLEARED by constructor. +// Store 1-bit per value. template struct IMGUI_API ImBitArray { ImU32 Storage[(BITCOUNT + 31) >> 5]; - ImBitArray() { } + ImBitArray() { ClearAllBits(); } void ClearAllBits() { memset(Storage, 0, sizeof(Storage)); } void SetAllBits() { memset(Storage, 255, sizeof(Storage)); } bool TestBit(int n) const { IM_ASSERT(n < BITCOUNT); return ImBitArrayTestBit(Storage, n); } @@ -694,32 +727,51 @@ enum ImGuiItemFlags_ ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) - ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. - ImGuiItemFlags_Default_ = 0 + ImGuiItemFlags_ReadOnly = 1 << 7 // false // [ALPHA] Allow hovering interactions but underlying value is not changed. +}; + +// Flags for ItemAdd() +// FIXME-NAV: _Focusable is _ALMOST_ what you would expect to be called '_TabStop' but because SetKeyboardFocusHere() works on items with no TabStop we distinguish Focusable from TabStop. +enum ImGuiItemAddFlags_ +{ + ImGuiItemAddFlags_None = 0, + ImGuiItemAddFlags_Focusable = 1 << 0 // FIXME-NAV: In current/legacy scheme, Focusable+TabStop support are opt-in by widgets. We will transition it toward being opt-out, so this flag is expected to eventually disappear. }; // Storage for LastItem data enum ImGuiItemStatusFlags_ { ImGuiItemStatusFlags_None = 0, - ImGuiItemStatusFlags_HoveredRect = 1 << 0, - ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // LastItemDisplayRect is valid + ImGuiItemStatusFlags_HoveredRect = 1 << 0, // Mouse position is within item rectangle (does NOT mean that the window is in correct z-order and can be hovered!, this is only one part of the most-common IsItemHovered test) + ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // window->DC.LastItemDisplayRect is valid ImGuiItemStatusFlags_Edited = 1 << 2, // Value exposed by item was edited in the current frame (should match the bool return value of most widgets) - ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected" because reporting the change allows us to handle clipping with less issues. + ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected", only state changes, in order to easily handle clipping with less issues. ImGuiItemStatusFlags_ToggledOpen = 1 << 4, // Set when TreeNode() reports toggling their open state. ImGuiItemStatusFlags_HasDeactivated = 1 << 5, // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag. ImGuiItemStatusFlags_Deactivated = 1 << 6, // Only valid if ImGuiItemStatusFlags_HasDeactivated is set. - ImGuiItemStatusFlags_HoveredWindow = 1 << 7 // Override the HoveredWindow test to allow cross-window hover testing. + ImGuiItemStatusFlags_HoveredWindow = 1 << 7, // Override the HoveredWindow test to allow cross-window hover testing. + ImGuiItemStatusFlags_FocusedByCode = 1 << 8, // Set when the Focusable item just got focused from code. + ImGuiItemStatusFlags_FocusedByTabbing = 1 << 9, // Set when the Focusable item just got focused by Tabbing. + ImGuiItemStatusFlags_Focused = ImGuiItemStatusFlags_FocusedByCode | ImGuiItemStatusFlags_FocusedByTabbing #ifdef IMGUI_ENABLE_TEST_ENGINE , // [imgui_tests only] - ImGuiItemStatusFlags_Openable = 1 << 10, // - ImGuiItemStatusFlags_Opened = 1 << 11, // - ImGuiItemStatusFlags_Checkable = 1 << 12, // - ImGuiItemStatusFlags_Checked = 1 << 13 // + ImGuiItemStatusFlags_Openable = 1 << 20, // + ImGuiItemStatusFlags_Opened = 1 << 21, // + ImGuiItemStatusFlags_Checkable = 1 << 22, // + ImGuiItemStatusFlags_Checked = 1 << 23 // #endif }; +// Extend ImGuiInputTextFlags_ +enum ImGuiInputTextFlagsPrivate_ +{ + // [Internal] + ImGuiInputTextFlags_Multiline = 1 << 26, // For internal use by InputTextMultiline() + ImGuiInputTextFlags_NoMarkEdited = 1 << 27, // For internal use by functions using InputText() before reformatting data + ImGuiInputTextFlags_MergedItem = 1 << 28 // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match. +}; + // Extend ImGuiButtonFlags_ enum ImGuiButtonFlagsPrivate_ { @@ -827,6 +879,7 @@ enum ImGuiInputSource ImGuiInputSource_Keyboard, ImGuiInputSource_Gamepad, ImGuiInputSource_Nav, // Stored in g.ActiveIdSource only + ImGuiInputSource_Clipboard, // Currently only used by InputText() ImGuiInputSource_COUNT }; @@ -976,7 +1029,7 @@ struct IMGUI_API ImGuiInputTextState bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection bool Edited; // edited this frame - ImGuiInputTextFlags UserFlags; // Temporarily set while we call user's callback + ImGuiInputTextFlags Flags; // copy of InputText() flags ImGuiInputTextCallback UserCallback; // " void* UserCallbackData; // " @@ -1009,18 +1062,18 @@ struct ImGuiPopupData ImGuiPopupData() { memset(this, 0, sizeof(*this)); OpenFrameCount = -1; } }; -struct ImGuiNavMoveResult +struct ImGuiNavItemData { - ImGuiWindow* Window; // Best candidate window - ImGuiID ID; // Best candidate ID - ImGuiID FocusScopeId; // Best candidate focus scope ID - float DistBox; // Best candidate box distance to current NavId - float DistCenter; // Best candidate center distance to current NavId - float DistAxial; - ImRect RectRel; // Best candidate bounding box in window relative space + ImGuiWindow* Window; // Init,Move // Best candidate window (result->ItemWindow->RootWindowForNav == request->Window) + ImGuiID ID; // Init,Move // Best candidate item ID + ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID + ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space + float DistBox; // Move // Best candidate box distance to current NavId + float DistCenter; // Move // Best candidate center distance to current NavId + float DistAxial; // Move // Best candidate axial distance to current NavId - ImGuiNavMoveResult() { Clear(); } - void Clear() { Window = NULL; ID = FocusScopeId = 0; DistBox = DistCenter = DistAxial = FLT_MAX; RectRel = ImRect(); } + ImGuiNavItemData() { Clear(); } + void Clear() { Window = NULL; ID = FocusScopeId = 0; RectRel = ImRect(); DistBox = DistCenter = DistAxial = FLT_MAX; } }; enum ImGuiNextWindowDataFlags_ @@ -1180,14 +1233,21 @@ struct ImGuiViewportP : public ImGuiViewport ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!) ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height). - ImVec2 CurrWorkOffsetMin; // Work Area: Offset being built/increased during current frame - ImVec2 CurrWorkOffsetMax; // Work Area: Offset being built/decreased during current frame + ImVec2 BuildWorkOffsetMin; // Work Area: Offset being built during current frame. Generally >= 0.0f. + ImVec2 BuildWorkOffsetMax; // Work Area: Offset being built during current frame. Generally <= 0.0f. - ImGuiViewportP() { DrawListsLastFrame[0] = DrawListsLastFrame[1] = -1; DrawLists[0] = DrawLists[1] = NULL; } - ~ImGuiViewportP() { if (DrawLists[0]) IM_DELETE(DrawLists[0]); if (DrawLists[1]) IM_DELETE(DrawLists[1]); } - ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } - ImRect GetWorkRect() const { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); } - void UpdateWorkRect() { WorkPos = ImVec2(Pos.x + WorkOffsetMin.x, Pos.y + WorkOffsetMin.y); WorkSize = ImVec2(ImMax(0.0f, Size.x - WorkOffsetMin.x + WorkOffsetMax.x), ImMax(0.0f, Size.y - WorkOffsetMin.y + WorkOffsetMax.y)); } + ImGuiViewportP() { DrawListsLastFrame[0] = DrawListsLastFrame[1] = -1; DrawLists[0] = DrawLists[1] = NULL; } + ~ImGuiViewportP() { if (DrawLists[0]) IM_DELETE(DrawLists[0]); if (DrawLists[1]) IM_DELETE(DrawLists[1]); } + + // Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect) + ImVec2 CalcWorkRectPos(const ImVec2& off_min) const { return ImVec2(Pos.x + off_min.x, Pos.y + off_min.y); } + ImVec2 CalcWorkRectSize(const ImVec2& off_min, const ImVec2& off_max) const { return ImVec2(ImMax(0.0f, Size.x - off_min.x + off_max.x), ImMax(0.0f, Size.y - off_min.y + off_max.y)); } + void UpdateWorkRect() { WorkPos = CalcWorkRectPos(WorkOffsetMin); WorkSize = CalcWorkRectSize(WorkOffsetMin, WorkOffsetMax); } // Update public fields + + // Helpers to retrieve ImRect (we don't need to store BuildWorkRect as every access tend to change it, hence the code asymmetry) + ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } + ImRect GetWorkRect() const { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); } + ImRect GetBuildWorkRect() const { ImVec2 pos = CalcWorkRectPos(BuildWorkOffsetMin); ImVec2 size = CalcWorkRectSize(BuildWorkOffsetMin, BuildWorkOffsetMax); return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); } }; //----------------------------------------------------------------------------- @@ -1311,11 +1371,12 @@ struct ImGuiContext // Windows state ImVector Windows; // Windows, sorted in display order, back to front - ImVector WindowsFocusOrder; // Windows, sorted in focus order, back to front. (FIXME: We could only store root windows here! Need to sort out the Docking equivalent which is RootWindowDockStop and is unfortunately a little more dynamic) + ImVector WindowsFocusOrder; // Root windows, sorted in focus order, back to front. ImVector WindowsTempSortBuffer; // Temporary buffer used in EndFrame() to reorder windows so parents are kept before their child ImVector CurrentWindowStack; ImGuiStorage WindowsById; // Map window's ImGuiID to ImGuiWindow* int WindowsActiveCount; // Number of unique windows submitted by frame + ImVec2 WindowsHoverPadding; // Padding around resizable windows for which hovering on counts as hovering the window == ImMax(style.TouchExtraPadding, WINDOWS_HOVER_PADDING) ImGuiWindow* CurrentWindow; // Window being drawn into ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs. ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set. @@ -1325,6 +1386,7 @@ struct ImGuiContext float WheelingWindowTimer; // Item/widgets state and tracking information + ImGuiItemFlags CurrentItemFlags; // == g.ItemFlagsStack.back() ImGuiID HoveredId; // Hovered widget, filled during the frame ImGuiID HoveredIdPreviousFrame; bool HoveredIdAllowOverlap; @@ -1407,9 +1469,9 @@ struct ImGuiContext ImGuiKeyModFlags NavMoveRequestKeyMods; ImGuiDir NavMoveDir, NavMoveDirLast; // Direction of the move request (left/right/up/down), direction of the previous move request ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename? - ImGuiNavMoveResult NavMoveResultLocal; // Best move request candidate within NavWindow - ImGuiNavMoveResult NavMoveResultLocalVisibleSet; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) - ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) + ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow + ImGuiNavItemData NavMoveResultLocalVisibleSet; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) + ImGuiNavItemData NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) ImGuiWindow* NavWrapRequestWindow; // Window which requested trying nav wrap-around. ImGuiNavMoveFlags NavWrapRequestFlags; // Wrap-around operation flags. @@ -1428,7 +1490,7 @@ struct ImGuiContext int TabFocusRequestCurrCounterTabStop; // Tab item being requested for focus, stored as an index int TabFocusRequestNextCounterRegular; // Stored for next frame int TabFocusRequestNextCounterTabStop; // " - bool TabFocusPressed; // + bool TabFocusPressed; // Set in NewFrame() when user pressed Tab // Render float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) @@ -1455,8 +1517,9 @@ struct ImGuiContext // Table ImGuiTable* CurrentTable; + int CurrentTableStackIdx; ImPool Tables; - ImVector CurrentTableStack; + ImVector TablesTempDataStack; ImVector TablesLastTimeActive; // Last used timestamp of each tables (SOA, for efficient GC) ImVector DrawChannelsTempMergeBuffer; @@ -1523,6 +1586,7 @@ struct ImGuiContext // Misc float FramerateSecPerFrame[120]; // Calculate estimate of framerate for user over the last 2 seconds. int FramerateSecPerFrameIdx; + int FramerateSecPerFrameCount; float FramerateSecPerFrameAccum; int WantCaptureMouseNextFrame; // Explicit capture via CaptureKeyboardFromApp()/CaptureMouseFromApp() sets those flags int WantCaptureKeyboardNextFrame; @@ -1553,6 +1617,7 @@ struct ImGuiContext WheelingWindow = NULL; WheelingWindowTimer = 0.0f; + CurrentItemFlags = ImGuiItemFlags_None; HoveredId = HoveredIdPreviousFrame = 0; HoveredIdAllowOverlap = false; HoveredIdUsingMouseWheel = HoveredIdPreviousFrameUsingMouseWheel = false; @@ -1632,6 +1697,7 @@ struct ImGuiContext memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal)); CurrentTable = NULL; + CurrentTableStackIdx = -1; CurrentTabBar = NULL; LastValidMousePos = ImVec2(0.0f, 0.0f); @@ -1668,7 +1734,7 @@ struct ImGuiContext DebugItemPickerBreakId = 0; memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); - FramerateSecPerFrameIdx = 0; + FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0; FramerateSecPerFrameAccum = 0.0f; WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1; memset(TempBuffer, 0, sizeof(TempBuffer)); @@ -1706,8 +1772,8 @@ struct IMGUI_API ImGuiWindowTempData // Keyboard/Gamepad navigation ImGuiNavLayer NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) - int NavLayerActiveMask; // Which layers have been written to (result from previous frame) - int NavLayerActiveMaskNext; // Which layers have been written to (accumulator for current frame) + short NavLayersActiveMask; // Which layers have been written to (result from previous frame) + short NavLayersActiveMaskNext;// Which layers have been written to (accumulator for current frame) ImGuiID NavFocusScopeIdCurrent; // Current focus scope ID while appending bool NavHideHighlightOneFrame; bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) @@ -1729,7 +1795,6 @@ struct IMGUI_API ImGuiWindowTempData // Local parameters stacks // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. - ImGuiItemFlags ItemFlags; // == g.ItemFlagsStack.back() float ItemWidth; // Current item width (>0.0: width in pixels, <0.0: align xx pixels to the right of window). float TextWrapPos; // Current text wrap pos. ImVector ItemWidthStack; // Store item widths to restore (attention: .back() is not == ItemWidth) @@ -1774,8 +1839,9 @@ struct IMGUI_API ImGuiWindow bool HasCloseButton; // Set when the window has a close button (p_open != NULL) signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) - short BeginOrderWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0. - short BeginOrderWithinContext; // Order within entire imgui context. This is mostly used for debugging submission order related issues. + short BeginOrderWithinParent; // Begin() order within immediate parent window, if we are a child window. Otherwise 0. + short BeginOrderWithinContext; // Begin() order within entire imgui context. This is mostly used for debugging submission order related issues. + short FocusOrder; // Order within WindowsFocusOrder[], altered when windows are focused. ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) ImS8 AutoFitFramesX, AutoFitFramesY; ImS8 AutoFitChildAxises; @@ -1784,6 +1850,7 @@ struct IMGUI_API ImGuiWindow ImS8 HiddenFramesCanSkipItems; // Hide the window for N frames ImS8 HiddenFramesCannotSkipItems; // Hide the window for N frames while allowing items to be submitted so we can measure their size ImS8 HiddenFramesForRenderOnly; // Hide the window until frame N at Render() time only + ImS8 DisableInputsFrames; // Disable window interactions for N frames ImGuiCond SetWindowPosAllowFlags : 8; // store acceptable condition flags for SetNextWindowPos() use. ImGuiCond SetWindowSizeAllowFlags : 8; // store acceptable condition flags for SetNextWindowSize() use. ImGuiCond SetWindowCollapsedAllowFlags : 8; // store acceptable condition flags for SetNextWindowCollapsed() use. @@ -1877,6 +1944,7 @@ enum ImGuiTabBarFlagsPrivate_ // Extend ImGuiTabItemFlags_ enum ImGuiTabItemFlagsPrivate_ { + ImGuiTabItemFlags_SectionMask_ = ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing, ImGuiTabItemFlags_NoCloseButton = 1 << 20, // Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout) ImGuiTabItemFlags_Button = 1 << 21 // Used by TabItemButton, change the tab item behavior to mimic a button }; @@ -1922,7 +1990,7 @@ struct ImGuiTabBar float ScrollingRectMinX; float ScrollingRectMaxX; ImGuiID ReorderRequestTabId; - ImS8 ReorderRequestDir; + ImS16 ReorderRequestOffset; ImS8 BeginCount; bool WantLayout; bool VisibleTabWasSubmitted; @@ -1947,8 +2015,6 @@ struct ImGuiTabBar // [SECTION] Table support //----------------------------------------------------------------------------- -#ifdef IMGUI_HAS_TABLE - #define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code which cannot be used as a regular color. #define IMGUI_TABLE_MAX_COLUMNS 64 // sizeof(ImU64) * 8. This is solely because we frequently encode columns set in a ImU64. #define IMGUI_TABLE_MAX_DRAW_CHANNELS (4 + 64 * 2) // See TableSetupDrawChannels() @@ -2025,12 +2091,13 @@ struct ImGuiTableCellData ImGuiTableColumnIdx Column; // Column number }; -// FIXME-TABLE: transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData +// FIXME-TABLE: more transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData struct ImGuiTable { ImGuiID ID; ImGuiTableFlags Flags; void* RawData; // Single allocation to hold Columns[], DisplayOrderToIndex[] and RowCellData[] + ImGuiTableTempData* TempData; // Transient data while table is active. Point within g.CurrentTableStack[] ImSpan Columns; // Point within RawData[] ImSpan DisplayOrderToIndex; // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1) ImSpan RowCellData; // Point within RawData[]. Store cells background requests for current row. @@ -2082,22 +2149,11 @@ struct ImGuiTable ImRect Bg0ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG0/1 channel. This tends to be == OuterWindow->ClipRect at BeginTable() because output in BG0/BG1 is cpu-clipped ImRect Bg2ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG2 channel. This tends to be a correct, tight-fit, because output to BG2 are done by widgets relying on regular ClipRect. ImRect HostClipRect; // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window. - ImRect HostBackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable() - ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable() ImRect HostBackupInnerClipRect; // Backup of InnerWindow->ClipRect during PushTableBackground()/PopTableBackground() - ImVec2 HostBackupPrevLineSize; // Backup of InnerWindow->DC.PrevLineSize at the end of BeginTable() - ImVec2 HostBackupCurrLineSize; // Backup of InnerWindow->DC.CurrLineSize at the end of BeginTable() - ImVec2 HostBackupCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable() - ImVec2 UserOuterSize; // outer_size.x passed to BeginTable() - ImVec1 HostBackupColumnsOffset; // Backup of OuterWindow->DC.ColumnsOffset at the end of BeginTable() - float HostBackupItemWidth; // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable() - int HostBackupItemWidthStackSize;// Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable() ImGuiWindow* OuterWindow; // Parent window for the table ImGuiWindow* InnerWindow; // Window holding the table data (== OuterWindow or a child window) ImGuiTextBuffer ColumnsNames; // Contiguous buffer holding columns names - ImDrawListSplitter DrawSplitter; // We carry our own ImDrawList splitter to allow recursion (FIXME: could be stored outside, worst case we need 1 splitter per recursing table) - ImGuiTableColumnSortSpecs SortSpecsSingle; - ImVector SortSpecsMulti; // FIXME-OPT: Using a small-vector pattern would work be good. + ImDrawListSplitter* DrawSplitter; // Shortcut to TempData->DrawSplitter while in table. Isolate draw commands per columns to avoid switching clip rect constantly ImGuiTableSortSpecs SortSpecs; // Public facing sorts specs, this is what we return in TableGetSortSpecs() ImGuiTableColumnIdx SortSpecsCount; ImGuiTableColumnIdx ColumnsEnabledCount; // Number of enabled columns (<= ColumnsCount) @@ -2144,6 +2200,32 @@ struct ImGuiTable IMGUI_API ~ImGuiTable() { IM_FREE(RawData); } }; +// Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table). +// - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure. +// - We also leave out of this structure data that tend to be particularly useful for debugging/metrics. +// FIXME-TABLE: more transient data could be stored here: DrawSplitter, incoming RowData? +struct ImGuiTableTempData +{ + int TableIndex; // Index in g.Tables.Buf[] pool + float LastTimeActive; // Last timestamp this structure was used + + ImVec2 UserOuterSize; // outer_size.x passed to BeginTable() + ImDrawListSplitter DrawSplitter; + ImGuiTableColumnSortSpecs SortSpecsSingle; + ImVector SortSpecsMulti; // FIXME-OPT: Using a small-vector pattern would be good. + + ImRect HostBackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable() + ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable() + ImVec2 HostBackupPrevLineSize; // Backup of InnerWindow->DC.PrevLineSize at the end of BeginTable() + ImVec2 HostBackupCurrLineSize; // Backup of InnerWindow->DC.CurrLineSize at the end of BeginTable() + ImVec2 HostBackupCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable() + ImVec1 HostBackupColumnsOffset; // Backup of OuterWindow->DC.ColumnsOffset at the end of BeginTable() + float HostBackupItemWidth; // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable() + int HostBackupItemWidthStackSize;//Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable() + + IMGUI_API ImGuiTableTempData() { memset(this, 0, sizeof(*this)); LastTimeActive = -1.0f; } +}; + // sizeof() ~ 12 struct ImGuiTableColumnSettings { @@ -2182,8 +2264,6 @@ struct ImGuiTableSettings ImGuiTableColumnSettings* GetColumnSettings() { return (ImGuiTableColumnSettings*)(this + 1); } }; -#endif // #ifdef IMGUI_HAS_TABLE - //----------------------------------------------------------------------------- // [SECTION] ImGui internal API // No guarantee of forward compatibility here! @@ -2262,7 +2342,7 @@ namespace ImGui inline ImGuiItemStatusFlags GetItemStatusFlags() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.LastItemStatusFlags; } inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; } inline ImGuiID GetFocusID() { ImGuiContext& g = *GImGui; return g.NavId; } - inline ImGuiItemFlags GetItemsFlags() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.ItemFlags; } + inline ImGuiItemFlags GetItemFlags() { ImGuiContext& g = *GImGui; return g.CurrentItemFlags; } IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); IMGUI_API void SetFocusID(ImGuiID id, ImGuiWindow* window); IMGUI_API void ClearActiveID(); @@ -2276,12 +2356,11 @@ namespace ImGui // Basic Helpers for widget code IMGUI_API void ItemSize(const ImVec2& size, float text_baseline_y = -1.0f); IMGUI_API void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f); - IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL); + IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemAddFlags flags = 0); IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id); + IMGUI_API void ItemFocusable(ImGuiWindow* window, ImGuiID id); IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged); IMGUI_API void SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); - IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id); // Return true if focus is requested - IMGUI_API void FocusableItemUnregister(ImGuiWindow* window); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); IMGUI_API void PushMultiItemsWidths(int components, float width_full); @@ -2291,6 +2370,15 @@ namespace ImGui IMGUI_API ImVec2 GetContentRegionMaxAbs(); IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess); +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + // If you have old/custom copy-and-pasted widgets that used FocusableItemRegister(): + // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool focused = FocusableItemRegister(...)' + // (New) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' + // Widget code are simplified as there's no need to call FocusableItemUnregister() while managing the transition from regular widget to TempInputText() + inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { IM_ASSERT(0); IM_UNUSED(window); IM_UNUSED(id); return false; } // -> pass ImGuiItemAddFlags_Focusable flag to ItemAdd() + inline void FocusableItemUnregister(ImGuiWindow* window) { IM_ASSERT(0); IM_UNUSED(window); } // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem +#endif + // Logging/Capture IMGUI_API void LogBegin(ImGuiLogType type, int auto_open_depth); // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name. IMGUI_API void LogToBuffer(int auto_open_depth = -1); // Start logging/capturing to internal buffer @@ -2308,6 +2396,7 @@ namespace ImGui IMGUI_API ImGuiWindow* GetTopMostPopupModal(); IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window); IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy); + IMGUI_API bool BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags); // Gamepad/Keyboard Navigation IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); @@ -2319,7 +2408,7 @@ namespace ImGui IMGUI_API ImVec2 GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f); IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate); IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again. - IMGUI_API void SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel); + IMGUI_API void SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel); // Focus Scope (WIP) // This is generally used to identify a selection set (multiple of which may be in the same window), as selection @@ -2360,7 +2449,6 @@ namespace ImGui // Tables: Candidates for public API IMGUI_API void TableOpenContextMenu(int column_n = -1); - IMGUI_API void TableSetColumnEnabled(int column_n, bool enabled); IMGUI_API void TableSetColumnWidth(int column_n, float width); IMGUI_API void TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs); IMGUI_API int TableGetHoveredColumn(); // May use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead. Return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered. @@ -2398,6 +2486,7 @@ namespace ImGui IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table); IMGUI_API void TableRemove(ImGuiTable* table); IMGUI_API void TableGcCompactTransientBuffers(ImGuiTable* table); + IMGUI_API void TableGcCompactTransientBuffers(ImGuiTableTempData* table); IMGUI_API void TableGcCompactSettings(); // Tables: Settings @@ -2414,7 +2503,8 @@ namespace ImGui IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); - IMGUI_API void TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir); + IMGUI_API void TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int offset); + IMGUI_API void TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, ImVec2 mouse_pos); IMGUI_API bool TabBarProcessReorder(ImGuiTabBar* tab_bar); IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags); IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button); @@ -2461,7 +2551,8 @@ namespace ImGui IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col); IMGUI_API ImRect GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis); IMGUI_API ImGuiID GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis); - IMGUI_API ImGuiID GetWindowResizeID(ImGuiWindow* window, int n); // 0..3: corners, 4..7: borders + IMGUI_API ImGuiID GetWindowResizeCornerID(ImGuiWindow* window, int n); // 0..3: corners + IMGUI_API ImGuiID GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir); IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags); IMGUI_API bool CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value); IMGUI_API bool CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value); diff --git a/Depend/imgui/imgui_tables.cpp b/Depend/imgui/imgui_tables.cpp index edeef73..593e3b0 100644 --- a/Depend/imgui/imgui_tables.cpp +++ b/Depend/imgui/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.82 +// dear imgui, v1.83 // (tables and columns code) /* @@ -8,6 +8,7 @@ Index of this file: // [SECTION] Commentary // [SECTION] Header mess // [SECTION] Tables: Main code +// [SECTION] Tables: Simple accessors // [SECTION] Tables: Row changes // [SECTION] Tables: Columns changes // [SECTION] Tables: Columns width management @@ -73,7 +74,7 @@ Index of this file: // (Read carefully because this is subtle but it does make sense!) //----------------------------------------------------------------------------- // About 'outer_size': -// Its meaning needs to differ slightly depending of if we are using ScrollX/ScrollY flags. +// Its meaning needs to differ slightly depending on if we are using ScrollX/ScrollY flags. // Default value is ImVec2(0.0f, 0.0f). // X // - outer_size.x <= 0.0f -> Right-align from window/work-rect right-most edge. With -FLT_MIN or 0.0f will align exactly on right-most edge. @@ -90,7 +91,7 @@ Index of this file: // Outer size is also affected by the NoHostExtendX/NoHostExtendY flags. // Important to that note how the two flags have slightly different behaviors! // - ImGuiTableFlags_NoHostExtendX -> Make outer width auto-fit to columns (overriding outer_size.x value). Only available when ScrollX/ScrollY are disabled and Stretch columns are not used. -// - ImGuiTableFlags_NoHostExtendY -> Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible. +// - ImGuiTableFlags_NoHostExtendY -> Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY is disabled. Data below the limit will be clipped and not visible. // In theory ImGuiTableFlags_NoHostExtendY could be the default and any non-scrolling tables with outer_size.y != 0.0f would use exact height. // This would be consistent but perhaps less useful and more confusing (as vertically clipped items are not easily noticeable) //----------------------------------------------------------------------------- @@ -132,7 +133,7 @@ Index of this file: // - the typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns. // - using mixed policies with ScrollX does not make much sense, as using Stretch columns with ScrollX does not make much sense in the first place! // that is, unless 'inner_width' is passed to BeginTable() to explicitly provide a total width to layout columns in. -// - when using ImGuiTableFlags_SizingFixedSame with mixed columns, only the Fixed/Auto columns will match their widths to the maximum contents width. +// - when using ImGuiTableFlags_SizingFixedSame with mixed columns, only the Fixed/Auto columns will match their widths to the width of the maximum contents. // - when using ImGuiTableFlags_SizingStretchSame with mixed columns, only the Stretch columns will match their weight/widths. //----------------------------------------------------------------------------- // About using column width: @@ -140,9 +141,9 @@ Index of this file: // - you may use GetContentRegionAvail().x to query the width available in a given column. // - right-side alignment features such as SetNextItemWidth(-x) or PushItemWidth(-x) will rely on this width. // If the column is not resizable and has no width specified with TableSetupColumn(): -// - its width will be automatic and be the set to the max of items submitted. +// - its width will be automatic and be set to the max of items submitted. // - therefore you generally cannot have ALL items of the columns use e.g. SetNextItemWidth(-FLT_MIN). -// - but if the column has one or more item of known/fixed size, this will become the reference width used by SetNextItemWidth(-FLT_MIN). +// - but if the column has one or more items of known/fixed size, this will become the reference width used by SetNextItemWidth(-FLT_MIN). //----------------------------------------------------------------------------- @@ -161,7 +162,7 @@ Index of this file: // - Both TableSetColumnIndex() and TableNextColumn() return true when the column is visible or performing // width measurements. Otherwise, you may skip submitting the contents of a cell/column, BUT ONLY if you know // it is not going to contribute to row height. -// In many situations, you may skip submitting contents for every columns but one (e.g. the first one). +// In many situations, you may skip submitting contents for every column but one (e.g. the first one). // - Case A: column is not hidden by user, and at least partially in sight (most common case). // - Case B: column is clipped / out of sight (because of scrolling or parent ClipRect): TableNextColumn() return false as a hint but we still allow layout output. // - Case C: column is hidden explicitly by the user (e.g. via the context menu, or _DefaultHide column flag, etc.). @@ -172,7 +173,7 @@ Index of this file: // ClipRect: normal zero-width zero-width -> [internal] when ClipRect is zero, ItemAdd() will return false and most widgets will early out mid-way. // ImDrawList output: normal dummy dummy -> [internal] when using the dummy channel, ImDrawList submissions (if any) will be wasted (because cliprect is zero-width anyway). // -// - We need distinguish those cases because non-hidden columns that are clipped outside of scrolling bounds should still contribute their height to the row. +// - We need to distinguish those cases because non-hidden columns that are clipped outside of scrolling bounds should still contribute their height to the row. // However, in the majority of cases, the contribution to row height is the same for all columns, or the tallest cells are known by the programmer. //----------------------------------------------------------------------------- // About clipping/culling of whole Tables: @@ -209,6 +210,8 @@ Index of this file: #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #endif +#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). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif // Clang/GCC warnings with -Weverything @@ -235,6 +238,19 @@ Index of this file: //----------------------------------------------------------------------------- // [SECTION] Tables: Main code //----------------------------------------------------------------------------- +// - TableFixFlags() [Internal] +// - TableFindByID() [Internal] +// - BeginTable() +// - BeginTableEx() [Internal] +// - TableBeginInitMemory() [Internal] +// - TableBeginApplyRequests() [Internal] +// - TableSetupColumnFlags() [Internal] +// - TableUpdateLayout() [Internal] +// - TableUpdateBorders() [Internal] +// - EndTable() +// - TableSetupColumn() +// - TableSetupScrollFreeze() +//----------------------------------------------------------------------------- // Configuration static const int TABLE_DRAW_CHANNEL_BG0 = 0; @@ -327,6 +343,16 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG if (instance_no > 0) IM_ASSERT(table->ColumnsCount == columns_count && "BeginTable(): Cannot change columns count mid-frame while preserving same ID"); + // Acquire temporary buffers + const int table_idx = g.Tables.GetIndex(table); + g.CurrentTableStackIdx++; + if (g.CurrentTableStackIdx + 1 > g.TablesTempDataStack.Size) + g.TablesTempDataStack.resize(g.CurrentTableStackIdx + 1, ImGuiTableTempData()); + ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempDataStack[g.CurrentTableStackIdx]; + temp_data->TableIndex = table_idx; + table->DrawSplitter = &table->TempData->DrawSplitter; + table->DrawSplitter->Clear(); + // Fix flags table->IsDefaultSizingPolicy = (flags & ImGuiTableFlags_SizingMask_) == 0; flags = TableFixFlags(flags, outer_window); @@ -340,7 +366,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->ColumnsCount = columns_count; table->IsLayoutLocked = false; table->InnerWidth = inner_width; - table->UserOuterSize = outer_size; + temp_data->UserOuterSize = outer_size; // When not using a child window, WorkRect.Max will grow as we append contents. if (use_child_window) @@ -389,14 +415,14 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->HostIndentX = inner_window->DC.Indent.x; table->HostClipRect = inner_window->ClipRect; table->HostSkipItems = inner_window->SkipItems; - table->HostBackupWorkRect = inner_window->WorkRect; - table->HostBackupParentWorkRect = inner_window->ParentWorkRect; - table->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset; - table->HostBackupPrevLineSize = inner_window->DC.PrevLineSize; - table->HostBackupCurrLineSize = inner_window->DC.CurrLineSize; - table->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos; - table->HostBackupItemWidth = outer_window->DC.ItemWidth; - table->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; + temp_data->HostBackupWorkRect = inner_window->WorkRect; + temp_data->HostBackupParentWorkRect = inner_window->ParentWorkRect; + temp_data->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset; + temp_data->HostBackupPrevLineSize = inner_window->DC.PrevLineSize; + temp_data->HostBackupCurrLineSize = inner_window->DC.CurrLineSize; + temp_data->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos; + temp_data->HostBackupItemWidth = outer_window->DC.ItemWidth; + temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); // Padding and Spacing @@ -439,8 +465,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->BorderColorLight = GetColorU32(ImGuiCol_TableBorderLight); // Make table current - const int table_idx = g.Tables.GetIndex(table); - g.CurrentTableStack.push_back(ImGuiPtrOrIndex(table_idx)); g.CurrentTable = table; outer_window->DC.CurrentTableIdx = table_idx; if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly. @@ -453,13 +477,18 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG if (table_idx >= g.TablesLastTimeActive.Size) g.TablesLastTimeActive.resize(table_idx + 1, -1.0f); g.TablesLastTimeActive[table_idx] = (float)g.Time; + temp_data->LastTimeActive = (float)g.Time; table->MemoryCompacted = false; // Setup memory buffer (clear data if columns count changed) - const int stored_size = table->Columns.size(); - if (stored_size != 0 && stored_size != columns_count) + ImGuiTableColumn* old_columns_to_preserve = NULL; + void* old_columns_raw_data = NULL; + const int old_columns_count = table->Columns.size(); + if (old_columns_count != 0 && old_columns_count != columns_count) { - IM_FREE(table->RawData); + // Attempt to preserve width on column count change (#4046) + old_columns_to_preserve = table->Columns.Data; + old_columns_raw_data = table->RawData; table->RawData = NULL; } if (table->RawData == NULL) @@ -482,14 +511,24 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG for (int n = 0; n < columns_count; n++) { ImGuiTableColumn* column = &table->Columns[n]; - float width_auto = column->WidthAuto; - *column = ImGuiTableColumn(); - column->WidthAuto = width_auto; - column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker + if (old_columns_to_preserve && n < old_columns_count) + { + // FIXME: We don't attempt to preserve column order in this path. + *column = old_columns_to_preserve[n]; + } + else + { + float width_auto = column->WidthAuto; + *column = ImGuiTableColumn(); + column->WidthAuto = width_auto; + column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker + column->IsEnabled = column->IsEnabledNextFrame = true; + } column->DisplayOrder = table->DisplayOrderToIndex[n] = (ImGuiTableColumnIdx)n; - column->IsEnabled = column->IsEnabledNextFrame = true; } } + if (old_columns_raw_data) + IM_FREE(old_columns_raw_data); // Load settings if (table->IsSettingsRequestLoad) @@ -1087,7 +1126,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // Initial state ImGuiWindow* inner_window = table->InnerWindow; if (table->Flags & ImGuiTableFlags_NoClip) - table->DrawSplitter.SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); + table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); else inner_window->DrawList->PushClipRect(inner_window->ClipRect.Min, inner_window->ClipRect.Max, false); } @@ -1175,6 +1214,7 @@ void ImGui::EndTable() const ImGuiTableFlags flags = table->Flags; ImGuiWindow* inner_window = table->InnerWindow; ImGuiWindow* outer_window = table->OuterWindow; + ImGuiTableTempData* temp_data = table->TempData; IM_ASSERT(inner_window == g.CurrentWindow); IM_ASSERT(outer_window == inner_window || outer_window == inner_window->ParentWindow); @@ -1187,9 +1227,9 @@ void ImGui::EndTable() TableOpenContextMenu((int)table->HoveredColumnBody); // Finalize table height - inner_window->DC.PrevLineSize = table->HostBackupPrevLineSize; - inner_window->DC.CurrLineSize = table->HostBackupCurrLineSize; - inner_window->DC.CursorMaxPos = table->HostBackupCursorMaxPos; + inner_window->DC.PrevLineSize = temp_data->HostBackupPrevLineSize; + inner_window->DC.CurrLineSize = temp_data->HostBackupCurrLineSize; + inner_window->DC.CursorMaxPos = temp_data->HostBackupCursorMaxPos; const float inner_content_max_y = table->RowPosY2; IM_ASSERT(table->RowPosY2 == inner_window->DC.CursorPos.y); if (inner_window != outer_window) @@ -1236,10 +1276,11 @@ void ImGui::EndTable() #endif // Flatten channels and merge draw calls - table->DrawSplitter.SetCurrentChannel(inner_window->DrawList, 0); + ImDrawListSplitter* splitter = table->DrawSplitter; + splitter->SetCurrentChannel(inner_window->DrawList, 0); if ((table->Flags & ImGuiTableFlags_NoClip) == 0) TableMergeDrawChannels(table); - table->DrawSplitter.Merge(inner_window->DrawList); + splitter->Merge(inner_window->DrawList); // Update ColumnsAutoFitWidth to get us ahead for host using our size to auto-resize without waiting for next BeginTable() const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1); @@ -1281,18 +1322,18 @@ void ImGui::EndTable() // Pop from id stack IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table->ID + table->InstanceCurrent, "Mismatching PushID/PopID!"); - IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= table->HostBackupItemWidthStackSize, "Too many PopItemWidth!"); + IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= temp_data->HostBackupItemWidthStackSize, "Too many PopItemWidth!"); PopID(); // Restore window data that we modified const ImVec2 backup_outer_max_pos = outer_window->DC.CursorMaxPos; - inner_window->WorkRect = table->HostBackupWorkRect; - inner_window->ParentWorkRect = table->HostBackupParentWorkRect; + inner_window->WorkRect = temp_data->HostBackupWorkRect; + inner_window->ParentWorkRect = temp_data->HostBackupParentWorkRect; inner_window->SkipItems = table->HostSkipItems; outer_window->DC.CursorPos = table->OuterRect.Min; - outer_window->DC.ItemWidth = table->HostBackupItemWidth; - outer_window->DC.ItemWidthStack.Size = table->HostBackupItemWidthStackSize; - outer_window->DC.ColumnsOffset = table->HostBackupColumnsOffset; + outer_window->DC.ItemWidth = temp_data->HostBackupItemWidth; + outer_window->DC.ItemWidthStack.Size = temp_data->HostBackupItemWidthStackSize; + outer_window->DC.ColumnsOffset = temp_data->HostBackupColumnsOffset; // Layout in outer window // (FIXME: To allow auto-fit and allow desirable effect of SameLine() we dissociate 'used' vs 'ideal' size by overriding @@ -1315,20 +1356,20 @@ void ImGui::EndTable() IM_ASSERT((table->Flags & ImGuiTableFlags_ScrollX) == 0); outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth); } - else if (table->UserOuterSize.x <= 0.0f) + else if (temp_data->UserOuterSize.x <= 0.0f) { const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.x : 0.0f; - outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - table->UserOuterSize.x); + outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - temp_data->UserOuterSize.x); outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth)); } else { outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Max.x); } - if (table->UserOuterSize.y <= 0.0f) + if (temp_data->UserOuterSize.y <= 0.0f) { const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.y : 0.0f; - outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - table->UserOuterSize.y); + outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - temp_data->UserOuterSize.y); outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y)); } else @@ -1344,8 +1385,15 @@ void ImGui::EndTable() // Clear or restore current table, if any IM_ASSERT(g.CurrentWindow == outer_window && g.CurrentTable == table); - g.CurrentTableStack.pop_back(); - g.CurrentTable = g.CurrentTableStack.Size ? g.Tables.GetByIndex(g.CurrentTableStack.back().Index) : NULL; + IM_ASSERT(g.CurrentTableStackIdx >= 0); + g.CurrentTableStackIdx--; + temp_data = g.CurrentTableStackIdx >= 0 ? &g.TablesTempDataStack[g.CurrentTableStackIdx] : NULL; + g.CurrentTable = temp_data ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL; + if (g.CurrentTable) + { + g.CurrentTable->TempData = temp_data; + g.CurrentTable->DrawSplitter = &temp_data->DrawSplitter; + } outer_window->DC.CurrentTableIdx = g.CurrentTable ? g.Tables.GetIndex(g.CurrentTable) : -1; } @@ -1435,6 +1483,20 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows) table->IsUnfrozenRows = (table->FreezeRowsCount == 0); // Make sure this is set before TableUpdateLayout() so ImGuiListClipper can benefit from it.b } +//----------------------------------------------------------------------------- +// [SECTION] Tables: Simple accessors +//----------------------------------------------------------------------------- +// - TableGetColumnCount() +// - TableGetColumnName() +// - TableGetColumnName() [Internal] +// - TableSetColumnEnabled() [Internal] +// - TableGetColumnFlags() +// - TableGetCellBgRect() [Internal] +// - TableGetColumnResizeID() [Internal] +// - TableGetHoveredColumn() [Internal] +// - TableSetBgColor() +//----------------------------------------------------------------------------- + int ImGui::TableGetColumnCount() { ImGuiContext& g = *GImGui; @@ -1463,6 +1525,9 @@ const char* ImGui::TableGetColumnName(const ImGuiTable* table, int column_n) return &table->ColumnsNames.Buf[column->NameOffset]; } +// Request enabling/disabling a column (often perceived as "showing/hiding" from users point of view) +// Note that end-user can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) +// Request will be applied during next layout, which happens on the first call to TableNextRow() after BeginTable() // For the getter you can use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsEnabled) void ImGui::TableSetColumnEnabled(int column_n, bool enabled) { @@ -1701,7 +1766,7 @@ void ImGui::TableEndRow(ImGuiTable* table) // always followed by a change of clipping rectangle we perform the smallest overwrite possible here. if ((table->Flags & ImGuiTableFlags_NoClip) == 0) window->DrawList->_CmdHeader.ClipRect = table->Bg0ClipRectForDrawCmd.ToVec4(); - table->DrawSplitter.SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0); + table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0); } // Draw row background @@ -1773,7 +1838,7 @@ void ImGui::TableEndRow(ImGuiTable* table) // Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect); - table->DrawSplitter.SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent); + table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent); } if (!(table->RowFlags & ImGuiTableRowFlags_Headers)) @@ -1888,14 +1953,14 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n) if (table->Flags & ImGuiTableFlags_NoClip) { // FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed. - table->DrawSplitter.SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); + table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); //IM_ASSERT(table->DrawSplitter._Current == TABLE_DRAW_CHANNEL_NOCLIP); } else { // FIXME-TABLE: Could avoid this if draw channel is dummy channel? SetWindowClipRectBeforeSetChannel(window, column->ClipRect); - table->DrawSplitter.SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); + table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); } // Logging @@ -2143,7 +2208,7 @@ void ImGui::TablePushBackgroundChannel() // Optimization: avoid SetCurrentChannel() + PushClipRect() table->HostBackupInnerClipRect = window->ClipRect; SetWindowClipRectBeforeSetChannel(window, table->Bg2ClipRectForDrawCmd); - table->DrawSplitter.SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent); + table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent); } void ImGui::TablePopBackgroundChannel() @@ -2155,7 +2220,7 @@ void ImGui::TablePopBackgroundChannel() // Optimization: avoid PopClipRect() + SetCurrentChannel() SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect); - table->DrawSplitter.SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); + table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); } // Allocate draw channels. Called by TableUpdateLayout() @@ -2181,7 +2246,7 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table) const int channels_for_bg = 1 + 1 * freeze_row_multiplier; const int channels_for_dummy = (table->ColumnsEnabledCount < table->ColumnsCount || table->VisibleMaskByIndex != table->EnabledMaskByIndex) ? +1 : 0; const int channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy; - table->DrawSplitter.Split(table->InnerWindow->DrawList, channels_total); + table->DrawSplitter->Split(table->InnerWindow->DrawList, channels_total); table->DummyDrawChannel = (ImGuiTableDrawChannelIdx)((channels_for_dummy > 0) ? channels_total - 1 : -1); table->Bg2DrawChannelCurrent = TABLE_DRAW_CHANNEL_BG2_FROZEN; table->Bg2DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)((table->FreezeRowsCount > 0) ? 2 + channels_for_row : TABLE_DRAW_CHANNEL_BG2_FROZEN); @@ -2245,7 +2310,7 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table) void ImGui::TableMergeDrawChannels(ImGuiTable* table) { ImGuiContext& g = *GImGui; - ImDrawListSplitter* splitter = &table->DrawSplitter; + ImDrawListSplitter* splitter = table->DrawSplitter; const bool has_freeze_v = (table->FreezeRowsCount > 0); const bool has_freeze_h = (table->FreezeColumnsCount > 0); IM_ASSERT(splitter->_Current == 0); @@ -2256,10 +2321,11 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) ImRect ClipRect; int ChannelsCount; ImBitArray ChannelsMask; + + MergeGroup() { ChannelsCount = 0; } }; int merge_group_mask = 0x00; MergeGroup merge_groups[4]; - memset(merge_groups, 0, sizeof(merge_groups)); // 1. Scan channels and take note of those which can be merged for (int column_n = 0; column_n < table->ColumnsCount; column_n++) @@ -2337,7 +2403,6 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data; ImBitArray remaining_mask; // We need 132-bit of storage - remaining_mask.ClearAllBits(); remaining_mask.SetBitRange(LEADING_DRAW_CHANNELS, splitter->_Count); remaining_mask.ClearBit(table->Bg2DrawChannelUnfrozen); IM_ASSERT(has_freeze_v == false || table->Bg2DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG2_FROZEN); @@ -2416,7 +2481,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) return; ImDrawList* inner_drawlist = inner_window->DrawList; - table->DrawSplitter.SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0); + table->DrawSplitter->SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0); inner_drawlist->PushClipRect(table->Bg0ClipRectForDrawCmd.Min, table->Bg0ClipRectForDrawCmd.Max, false); // Draw inner border and resizing feedback @@ -2676,20 +2741,22 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table) TableSortSpecsSanitize(table); // Write output - table->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount); - ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data; - for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - ImGuiTableColumn* column = &table->Columns[column_n]; - if (column->SortOrder == -1) - continue; - IM_ASSERT(column->SortOrder < table->SortSpecsCount); - ImGuiTableColumnSortSpecs* sort_spec = &sort_specs[column->SortOrder]; - sort_spec->ColumnUserID = column->UserID; - sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n; - sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder; - sort_spec->SortDirection = column->SortDirection; - } + ImGuiTableTempData* temp_data = table->TempData; + temp_data->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount); + ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &temp_data->SortSpecsSingle : temp_data->SortSpecsMulti.Data; + if (sort_specs != NULL) + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + { + ImGuiTableColumn* column = &table->Columns[column_n]; + if (column->SortOrder == -1) + continue; + IM_ASSERT(column->SortOrder < table->SortSpecsCount); + ImGuiTableColumnSortSpecs* sort_spec = &sort_specs[column->SortOrder]; + sort_spec->ColumnUserID = column->UserID; + sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n; + sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder; + sort_spec->SortDirection = column->SortDirection; + } table->SortSpecs.Specs = sort_specs; table->SortSpecs.SpecsCount = table->SortSpecsCount; table->SortSpecs.SpecsDirty = true; // Mark as dirty for user @@ -3318,6 +3385,9 @@ static void TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandle for (int column_n = 0; column_n < settings->ColumnsCount; column_n++, column++) { // "Column 0 UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v" + bool save_column = column->UserID != 0 || save_size || save_visible || save_order || (save_sort && column->SortOrder != -1); + if (!save_column) + continue; buf->appendf("Column %-2d", column_n); if (column->UserID != 0) buf->appendf(" UserID=%08X", column->UserID); if (save_size && column->IsStretch) buf->appendf(" Weight=%.4f", column->WidthOrWeight); @@ -3371,8 +3441,6 @@ void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table) //IMGUI_DEBUG_LOG("TableGcCompactTransientBuffers() id=0x%08X\n", table->ID); ImGuiContext& g = *GImGui; IM_ASSERT(table->MemoryCompacted == false); - table->DrawSplitter.ClearFreeMemory(); - table->SortSpecsMulti.clear(); table->SortSpecs.Specs = NULL; table->IsSortSpecsDirty = true; table->ColumnsNames.clear(); @@ -3382,6 +3450,13 @@ void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table) g.TablesLastTimeActive[g.Tables.GetIndex(table)] = -1.0f; } +void ImGui::TableGcCompactTransientBuffers(ImGuiTableTempData* temp_data) +{ + temp_data->DrawSplitter.ClearFreeMemory(); + temp_data->SortSpecsMulti.clear(); + temp_data->LastTimeActive = -1.0f; +} + // Compact and remove unused settings data (currently only used by TestEngine) void ImGui::TableGcCompactSettings() { diff --git a/Depend/imgui/imgui_widgets.cpp b/Depend/imgui/imgui_widgets.cpp index c5ee895..46d2174 100644 --- a/Depend/imgui/imgui_widgets.cpp +++ b/Depend/imgui/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.82 +// dear imgui, v1.83 // (widgets code) /* @@ -59,6 +59,8 @@ Index of this file: #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #endif +#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). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif // Clang/GCC warnings with -Weverything @@ -122,7 +124,7 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); //------------------------------------------------------------------------- // For InputTextEx() -static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data); +static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source); static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); @@ -350,17 +352,20 @@ void ImGui::LabelTextV(const char* label, const char* fmt, va_list args) const ImGuiStyle& style = g.Style; const float w = CalcItemWidth(); + const char* value_text_begin = &g.TempBuffer[0]; + const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + const ImVec2 value_size = CalcTextSize(value_text_begin, value_text_end, false); const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2)); - const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y * 2) + label_size); + + const ImVec2 pos = window->DC.CursorPos; + const ImRect value_bb(pos, pos + ImVec2(w, value_size.y + style.FramePadding.y * 2)); + const ImRect total_bb(pos, pos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), ImMax(value_size.y, label_size.y) + style.FramePadding.y * 2)); ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, 0)) return; // Render - const char* value_text_begin = &g.TempBuffer[0]; - const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f, 0.5f)); + RenderTextClipped(value_bb.Min + style.FramePadding, value_bb.Max, value_text_begin, value_text_end, &value_size, ImVec2(0.0f, 0.0f)); if (label_size.x > 0.0f) RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label); } @@ -683,7 +688,7 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags if (!ItemAdd(bb, id)) return false; - if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) + if (g.CurrentItemFlags & ImGuiItemFlags_ButtonRepeat) flags |= ImGuiButtonFlags_Repeat; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); @@ -759,7 +764,7 @@ bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiBu if (!ItemAdd(bb, id)) return false; - if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) + if (g.CurrentItemFlags & ImGuiItemFlags_ButtonRepeat) flags |= ImGuiButtonFlags_Repeat; bool hovered, held; @@ -1078,7 +1083,7 @@ bool ImGui::Checkbox(const char* label, bool* v) ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, id)) { - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); return false; } @@ -1094,7 +1099,7 @@ bool ImGui::Checkbox(const char* label, bool* v) RenderNavHighlight(total_bb, id); RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); ImU32 check_col = GetColorU32(ImGuiCol_CheckMark); - bool mixed_value = (window->DC.ItemFlags & ImGuiItemFlags_MixedValue) != 0; + bool mixed_value = (g.CurrentItemFlags & ImGuiItemFlags_MixedValue) != 0; if (mixed_value) { // Undocumented tristate/mixed/indeterminate checkbox (#2644) @@ -1114,7 +1119,7 @@ bool ImGui::Checkbox(const char* label, bool* v) if (label_size.x > 0.0f) RenderText(label_pos, label); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); return pressed; } @@ -1126,11 +1131,11 @@ bool ImGui::CheckboxFlagsT(const char* label, T* flags, T flags_value) bool pressed; if (!all_on && any_on) { - ImGuiWindow* window = GetCurrentWindow(); - ImGuiItemFlags backup_item_flags = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_MixedValue; + ImGuiContext& g = *GImGui; + ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_MixedValue; pressed = Checkbox(label, &all_on); - window->DC.ItemFlags = backup_item_flags; + g.CurrentItemFlags = backup_item_flags; } else { @@ -1216,7 +1221,7 @@ bool ImGui::RadioButton(const char* label, bool active) if (label_size.x > 0.0f) RenderText(label_pos, label); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags); return pressed; } @@ -1428,10 +1433,10 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; + const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; bool item_add = ItemAdd(bb, id); - window->DC.ItemFlags = item_flags_backup; + g.CurrentItemFlags = item_flags_backup; if (!item_add) return false; @@ -1573,7 +1578,9 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF bool hovered, held; bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held); - bool popup_open = IsPopupOpen(id, ImGuiPopupFlags_None); + + const ImGuiID popup_id = ImHashStr("##ComboPopup", 0, id); + bool popup_open = IsPopupOpen(popup_id, ImGuiPopupFlags_None); const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); const float value_x2 = ImMax(frame_bb.Min.x, frame_bb.Max.x - arrow_size); @@ -1603,7 +1610,7 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF { if (window->DC.NavLayerCurrent == 0) window->NavLastIds[0] = id; - OpenPopupEx(id, ImGuiPopupFlags_None); + OpenPopupEx(popup_id, ImGuiPopupFlags_None); popup_open = true; } @@ -1790,7 +1797,7 @@ static const ImGuiDataTypeInfo GDataTypeInfo[] = { sizeof(ImS64), "S64", "%lld", "%lld" }, // ImGuiDataType_S64 { sizeof(ImU64), "U64", "%llu", "%llu" }, #endif - { sizeof(float), "float", "%f", "%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg) + { sizeof(float), "float", "%.3f","%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg) { sizeof(double), "double","%f", "%lf" }, // ImGuiDataType_Double }; IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); @@ -1977,13 +1984,15 @@ bool ImGui::DataTypeApplyOpFromText(const char* buf, const char* initial_value_b { // All other types assign constant // We don't bother handling support for legacy operators since they are a little too crappy. Instead we will later implement a proper expression evaluator in the future. - sscanf(buf, format, p_data); + if (sscanf(buf, format, p_data) < 1) + return false; } else { // Small types need a 32-bit buffer to receive the result from scanf() int v32; - sscanf(buf, format, &v32); + if (sscanf(buf, format, &v32) < 1) + return false; if (data_type == ImGuiDataType_S8) *(ImS8*)p_data = (ImS8)ImClamp(v32, (int)IM_S8_MIN, (int)IM_S8_MAX); else if (data_type == ImGuiDataType_U8) @@ -2082,6 +2091,7 @@ static const char* ImAtoi(const char* src, TYPE* output) // - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi. static void SanitizeFormatString(const char* fmt, char* fmt_out, size_t fmt_out_size) { + IM_UNUSED(fmt_out_size); const char* fmt_end = ImParseFormatFindEnd(fmt); IM_ASSERT((size_t)(fmt_end - fmt + 1) < fmt_out_size); // Format is too long, let us know if this happens to you! while (fmt < fmt_end) @@ -2269,7 +2279,7 @@ bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v } if (g.ActiveId != id) return false; - if ((g.CurrentWindow->DC.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) + if ((g.CurrentItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) return false; switch (data_type) @@ -2302,12 +2312,14 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); const float w = CalcItemWidth(); + const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) + if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemAddFlags_Focusable : 0)) return false; // Default format string when passing NULL @@ -2318,11 +2330,10 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, // Tabbing or CTRL-clicking on Drag turns it into an InputText const bool hovered = ItemHoverable(frame_bb, id); - const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { - const bool focus_requested = temp_input_allowed && FocusableItemRegister(window, id); + const bool focus_requested = temp_input_allowed && (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Focused) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); const bool double_clicked = (hovered && g.IO.MouseDoubleClicked[0]); if (focus_requested || clicked || double_clicked || g.NavActivateId == id || g.NavInputId == id) @@ -2332,10 +2343,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, FocusWindow(window); g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavInputId == id)) - { temp_input_is_active = true; - FocusableItemUnregister(window); - } } // Experimental: simple click (without moving) turns Drag into an InputText // FIXME: Currently polling ImGuiConfigFlags_IsTouchScreen, may either poll an hypothetical ImGuiBackendFlags_HasKeyboard and/or an explicit drag settings. @@ -2344,7 +2352,6 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, { g.NavInputId = id; temp_input_is_active = true; - FocusableItemUnregister(window); } } @@ -2375,7 +2382,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, if (label_size.x > 0.0f) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags); return value_changed; } @@ -2875,7 +2882,7 @@ bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flag! Has the 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead."); ImGuiContext& g = *GImGui; - if ((g.CurrentWindow->DC.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) + if ((g.CurrentItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) return false; switch (data_type) @@ -2925,8 +2932,9 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) + if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemAddFlags_Focusable : 0)) return false; // Default format string when passing NULL @@ -2937,11 +2945,10 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat // Tabbing or CTRL-clicking on Slider turns it into an input box const bool hovered = ItemHoverable(frame_bb, id); - const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { - const bool focus_requested = temp_input_allowed && FocusableItemRegister(window, id); + const bool focus_requested = temp_input_allowed && (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Focused) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); if (focus_requested || clicked || g.NavActivateId == id || g.NavInputId == id) { @@ -2950,10 +2957,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat FocusWindow(window); g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || g.NavInputId == id)) - { temp_input_is_active = true; - FocusableItemUnregister(window); - } } } @@ -2989,7 +2993,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat if (label_size.x > 0.0f) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags); return value_changed; } @@ -3280,7 +3284,7 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* ClearActiveID(); g.CurrentWindow->DC.CursorPos = bb.Min; - bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags); + bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem); if (init) { // First frame we started displaying the InputText widget, we expect it to take the active id. @@ -3577,12 +3581,12 @@ static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* t namespace ImStb { -static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; } -static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->TextW[idx]; } -static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *GImGui; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); } +static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->CurLenW; } +static ImWchar STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { return obj->TextW[idx]; } +static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *GImGui; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); } static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x200000 ? 0 : key; } static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; -static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx) +static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx) { const ImWchar* text = obj->TextW.Data; const ImWchar* text_remaining = NULL; @@ -3595,19 +3599,20 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* ob r->num_chars = (int)(text_remaining - (text + line_start_idx)); } +// When ImGuiInputTextFlags_Password is set, we don't want actions such as CTRL+Arrow to leak the fact that underlying data are blanks or separators. static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } -static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator(obj->TextW[idx - 1]) && !is_separator(obj->TextW[idx]) ) : 1; } -static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } +static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (is_separator(obj->TextW[idx - 1]) && !is_separator(obj->TextW[idx]) ) : 1; } +static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } #ifdef __APPLE__ // FIXME: Move setting to IO structure -static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx]) ) : 1; } -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } +static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx]) ) : 1; } +static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } #else -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } +static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } #endif #define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h #define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL -static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) +static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n) { ImWchar* dst = obj->TextW.Data + pos; @@ -3623,9 +3628,9 @@ static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) *dst = '\0'; } -static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len) +static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const ImWchar* new_text, int new_text_len) { - const bool is_resizable = (obj->UserFlags & ImGuiInputTextFlags_CallbackResize) != 0; + const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0; const int text_len = obj->CurLenW; IM_ASSERT(pos <= text_len); @@ -3679,7 +3684,7 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im // stb_textedit internally allows for a single undo record to do addition and deletion, but somehow, calling // the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?) -static void stb_textedit_replace(STB_TEXTEDIT_STRING* str, STB_TexteditState* state, const STB_TEXTEDIT_CHARTYPE* text, int text_len) +static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const STB_TEXTEDIT_CHARTYPE* text, int text_len) { stb_text_makeundo_replace(str, state, 0, str->CurLenW, text_len); ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenW); @@ -3762,8 +3767,9 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons } // Return false to discard a character. -static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source) { + IM_ASSERT(input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Clipboard); unsigned int c = *p_char; // Filter non-printable (NB: isprint is unreliable! see #2467) @@ -3776,15 +3782,18 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f return false; } - // We ignore Ascii representation of delete (emitted from Backspace on OSX, see #2578, #2817) - if (c == 127) - return false; + if (input_source != ImGuiInputSource_Clipboard) + { + // We ignore Ascii representation of delete (emitted from Backspace on OSX, see #2578, #2817) + if (c == 127) + return false; - // Filter private Unicode range. GLFW on OSX seems to send private characters for special keys like arrow keys (FIXME) - if (c >= 0xE000 && c <= 0xF8FF) - return false; + // Filter private Unicode range. GLFW on OSX seems to send private characters for special keys like arrow keys (FIXME) + if (c >= 0xE000 && c <= 0xF8FF) + return false; + } - // Filter Unicode ranges we are not handling in this build. + // Filter Unicode ranges we are not handling in this build if (c > IM_UNICODE_CODEPOINT_MAX) return false; @@ -3888,7 +3897,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ ImVec2 inner_size = frame_size; if (is_multiline) { - if (!ItemAdd(total_bb, id, &frame_bb)) + if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemAddFlags_Focusable)) { ItemSize(total_bb, style.FramePadding.y); EndGroup(); @@ -3909,15 +3918,17 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ return false; } draw_window = g.CurrentWindow; // Child window - draw_window->DC.NavLayerActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent); // This is to ensure that EndChild() will display a navigation highlight so we can "enter" into it. + draw_window->DC.NavLayersActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent); // This is to ensure that EndChild() will display a navigation highlight so we can "enter" into it. draw_window->DC.CursorPos += style.FramePadding; inner_size.x -= draw_window->ScrollbarSizes.x; } else { + // Support for internal ImGuiInputTextFlags_MergedItem flag, which could be redesigned as an ItemFlags if needed (with test performed in ItemAdd) ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) - return false; + if (!(flags & ImGuiInputTextFlags_MergedItem)) + if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemAddFlags_Focusable)) + return false; } const bool hovered = ItemHoverable(frame_bb, id); if (hovered) @@ -3926,9 +3937,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // We are only allowed to access the state if we are already the active widget. ImGuiInputTextState* state = GetInputTextState(id); - const bool focus_requested = FocusableItemRegister(window, id); - const bool focus_requested_by_code = focus_requested && (g.TabFocusRequestCurrWindow == window && g.TabFocusRequestCurrCounterRegular == window->DC.FocusCounterRegular); - const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; + const bool focus_requested_by_code = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_FocusedByCode) != 0; + const bool focus_requested_by_tabbing = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool user_clicked = hovered && io.MouseClicked[0]; const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard)); @@ -3941,7 +3951,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX; const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline); - const bool init_make_active = (focus_requested || user_clicked || user_scroll_finish || user_nav_input_start); + const bool init_make_active = (user_clicked || user_scroll_finish || user_nav_input_start || focus_requested_by_code || focus_requested_by_tabbing); const bool init_state = (init_make_active || user_scroll_active); if ((init_state && g.ActiveId != id) || init_changed_specs) { @@ -3982,7 +3992,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } if (flags & ImGuiInputTextFlags_AlwaysOverwrite) state->Stb.insert_mode = 1; // stb field name is indeed incorrect (see #2863) - if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl))) + if (!is_multiline && (focus_requested_by_tabbing || (user_clicked && io.KeyCtrl))) select_all = true; } @@ -4060,7 +4070,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ backup_current_text_length = state->CurLenA; state->Edited = false; state->BufCapacityA = buf_size; - state->UserFlags = flags; + state->Flags = flags; state->UserCallback = callback; state->UserCallbackData = callback_user_data; @@ -4109,7 +4119,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (!io.InputQueueCharacters.contains('\t')) { unsigned int c = '\t'; // Insert TAB - if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) + if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) state->OnKeyPressed((int)c); } @@ -4124,7 +4134,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ unsigned int c = (unsigned int)io.InputQueueCharacters[n]; if (c == '\t' && io.KeyShift) continue; - if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) + if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) state->OnKeyPressed((int)c); } @@ -4188,7 +4198,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ else if (!is_readonly) { unsigned int c = '\n'; // Insert new line - if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) + if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) state->OnKeyPressed((int)c); } } @@ -4241,7 +4251,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ s += ImTextCharFromUtf8(&c, s, NULL); if (c == 0) break; - if (!InputTextFilterCharacter(&c, flags, callback, callback_user_data)) + if (!InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Clipboard)) continue; clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; } @@ -4413,7 +4423,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } // Clear temporary user storage - state->UserFlags = 0; + state->Flags = ImGuiInputTextFlags_None; state->UserCallback = NULL; state->UserCallbackData = NULL; } @@ -4597,7 +4607,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { state->CursorAnim += io.DeltaTime; bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f; - ImVec2 cursor_screen_pos = draw_pos + cursor_offset - draw_scroll; + ImVec2 cursor_screen_pos = ImFloor(draw_pos + cursor_offset - draw_scroll); ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f); if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); @@ -4647,7 +4657,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (value_changed && !(flags & ImGuiInputTextFlags_NoMarkEdited)) MarkItemEdited(id); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags); if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) return enter_pressed; else @@ -4803,11 +4813,14 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag char* p = buf; while (*p == '#' || ImCharIsBlankA(*p)) p++; - i[0] = i[1] = i[2] = i[3] = 0; + i[0] = i[1] = i[2] = 0; + i[3] = 0xFF; // alpha default to 255 is not parsed by scanf (e.g. inputting #FFFFFF omitting alpha) + int r; if (alpha) - sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) + r = sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) else - sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); + r = sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); + IM_UNUSED(r); // Fixes C6031: Return value ignored: 'sscanf'. } if (!(flags & ImGuiColorEditFlags_NoOptions)) OpenPopupOnItemClick("context"); @@ -5745,7 +5758,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l { if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) TreePushOverrideID(id); - IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.LastItemStatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); return is_open; } @@ -5871,7 +5884,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) TreePushOverrideID(id); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); return is_open; } @@ -6054,10 +6067,10 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl bool item_add; if (flags & ImGuiSelectableFlags_Disabled) { - ImGuiItemFlags backup_item_flags = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNavDefaultFocus; + ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNavDefaultFocus; item_add = ItemAdd(bb, id); - window->DC.ItemFlags = backup_item_flags; + g.CurrentItemFlags = backup_item_flags; } else { @@ -6135,10 +6148,10 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor(); // Automatically close popups - if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup)) + if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(g.CurrentItemFlags & ImGuiItemFlags_SelectableDontClosePopup)) CloseCurrentPopup(); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags); return pressed; } @@ -6287,7 +6300,7 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v // Plot/Graph widgets are not very good. // Consider writing your own, or using a third-party one, see: // - ImPlot https://github.com/epezent/implot -// - others https://github.com/ocornut/imgui/wiki/Useful-Widgets +// - others https://github.com/ocornut/imgui/wiki/Useful-Extensions //------------------------------------------------------------------------- int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size) @@ -6579,7 +6592,7 @@ void ImGui::EndMenuBar() // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost) const ImGuiNavLayer layer = ImGuiNavLayer_Menu; - IM_ASSERT(window->DC.NavLayerActiveMaskNext & (1 << layer)); // Sanity check + IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check FocusWindow(window); SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. @@ -6589,11 +6602,12 @@ void ImGui::EndMenuBar() } } + IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); IM_ASSERT(window->DC.MenuBarAppending); PopClipRect(); PopID(); - window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->MenuBarRect().Min.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos. + window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->Pos.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos. g.GroupStack.back().EmitItem = false; EndGroup(); // Restore position on layer 0 window->DC.LayoutType = ImGuiLayoutType_Vertical; @@ -6601,46 +6615,63 @@ void ImGui::EndMenuBar() window->DC.MenuBarAppending = false; } +// Important: calling order matters! +// FIXME: Somehow overlapping with docking tech. +// FIXME: The "rect-cut" aspect of this could be formalized into a lower-level helper (rect-cut: https://halt.software/dead-simple-layouts) +bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, ImGuiDir dir, float axis_size, ImGuiWindowFlags window_flags) +{ + IM_ASSERT(dir != ImGuiDir_None); + + ImGuiWindow* bar_window = FindWindowByName(name); + if (bar_window == NULL || bar_window->BeginCount == 0) + { + // Calculate and set window size/position + ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)(viewport_p ? viewport_p : GetMainViewport()); + ImRect avail_rect = viewport->GetBuildWorkRect(); + ImGuiAxis axis = (dir == ImGuiDir_Up || dir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X; + ImVec2 pos = avail_rect.Min; + if (dir == ImGuiDir_Right || dir == ImGuiDir_Down) + pos[axis] = avail_rect.Max[axis] - axis_size; + ImVec2 size = avail_rect.GetSize(); + size[axis] = axis_size; + SetNextWindowPos(pos); + SetNextWindowSize(size); + + // Report our size into work area (for next frame) using actual window size + if (dir == ImGuiDir_Up || dir == ImGuiDir_Left) + viewport->BuildWorkOffsetMin[axis] += axis_size; + else if (dir == ImGuiDir_Down || dir == ImGuiDir_Right) + viewport->BuildWorkOffsetMax[axis] -= axis_size; + } + + window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; + PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint + bool is_open = Begin(name, NULL, window_flags); + PopStyleVar(2); + + return is_open; +} + bool ImGui::BeginMainMenuBar() { ImGuiContext& g = *GImGui; ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport(); - ImGuiWindow* menu_bar_window = FindWindowByName("##MainMenuBar"); // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. + // FIXME: This could be generalized as an opt-in way to clamp window->DC.CursorStartPos to avoid SafeArea? + // FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV calibration in OS settings. g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); - - // Get our rectangle at the top of the work area - if (menu_bar_window == NULL || menu_bar_window->BeginCount == 0) - { - // Set window position - // We don't attempt to calculate our height ahead, as it depends on the per-viewport font size. - // However menu-bar will affect the minimum window size so we'll get the right height. - ImVec2 menu_bar_pos = viewport->Pos + viewport->CurrWorkOffsetMin; - ImVec2 menu_bar_size = ImVec2(viewport->Size.x - viewport->CurrWorkOffsetMin.x + viewport->CurrWorkOffsetMax.x, 1.0f); - SetNextWindowPos(menu_bar_pos); - SetNextWindowSize(menu_bar_size); - } - - // Create window - PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint, however the presence of a menu-bar will give us the minimum height we want. - ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; - bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar(); - PopStyleVar(2); - - // Report our size into work area (for next frame) using actual window size - menu_bar_window = GetCurrentWindow(); - if (menu_bar_window->BeginCount == 1) - viewport->CurrWorkOffsetMin.y += menu_bar_window->Size.y; - + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; + float height = GetFrameHeight(); + bool is_open = BeginViewportSideBar("##MainMenuBar", viewport, ImGuiDir_Up, height, window_flags); g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); - if (!is_open) - { + + if (is_open) + BeginMenuBar(); + else End(); - return false; - } - return true; //-V1020 + return is_open; } void ImGui::EndMainMenuBar() @@ -6794,7 +6825,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) if (want_close && IsPopupOpen(id, ImGuiPopupFlags_None)) ClosePopupToLevel(g.BeginPopupStack.Size, true); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) { @@ -6854,11 +6885,11 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, boo if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful - // Note that in this situation we render neither the shortcut neither the selected tick mark + // Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark. float w = label_size.x; window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); - pressed = Selectable(label, false, flags, ImVec2(w, 0.0f)); + pressed = Selectable(label, selected, flags, ImVec2(w, 0.0f)); PopStyleVar(); window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). } @@ -6881,7 +6912,7 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, boo RenderCheckMark(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f); } - IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); return pressed; } @@ -6942,12 +6973,17 @@ ImGuiTabBar::ImGuiTabBar() LastTabItemIdx = -1; } +static inline int TabItemGetSectionIdx(const ImGuiTabItem* tab) +{ + return (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; +} + static int IMGUI_CDECL TabItemComparerBySection(const void* lhs, const void* rhs) { const ImGuiTabItem* a = (const ImGuiTabItem*)lhs; const ImGuiTabItem* b = (const ImGuiTabItem*)rhs; - const int a_section = (a->Flags & ImGuiTabItemFlags_Leading) ? 0 : (a->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; - const int b_section = (b->Flags & ImGuiTabItemFlags_Leading) ? 0 : (b->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; + const int a_section = TabItemGetSectionIdx(a); + const int b_section = TabItemGetSectionIdx(b); if (a_section != b_section) return a_section - b_section; return (int)(a->IndexDuringLayout - b->IndexDuringLayout); @@ -7116,11 +7152,11 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) tab->IndexDuringLayout = (ImS16)tab_dst_n; // We will need sorting if tabs have changed section (e.g. moved from one of Leading/Central/Trailing to another) - int curr_tab_section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; + int curr_tab_section_n = TabItemGetSectionIdx(tab); if (tab_dst_n > 0) { ImGuiTabItem* prev_tab = &tab_bar->Tabs[tab_dst_n - 1]; - int prev_tab_section_n = (prev_tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (prev_tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; + int prev_tab_section_n = TabItemGetSectionIdx(prev_tab); if (curr_tab_section_n == 0 && prev_tab_section_n != 0) need_sort_by_section = true; if (prev_tab_section_n == 2 && curr_tab_section_n != 2) @@ -7192,12 +7228,13 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) const bool has_close_button = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) ? false : true; tab->ContentWidth = TabItemCalcSize(tab_name, has_close_button).x; - int section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; + int section_n = TabItemGetSectionIdx(tab); ImGuiTabBarSection* section = §ions[section_n]; section->Width += tab->ContentWidth + (section_n == curr_section_n ? g.Style.ItemInnerSpacing.x : 0.0f); curr_section_n = section_n; // Store data so we can build an array sorted by width if we need to shrink tabs down + IM_MSVC_WARNING_SUPPRESS(6385); int shrink_buffer_index = shrink_buffer_indexes[section_n]++; g.ShrinkWidthBuffer[shrink_buffer_index].Index = tab_n; g.ShrinkWidthBuffer[shrink_buffer_index].Width = tab->ContentWidth; @@ -7247,7 +7284,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) if (shrinked_width < 0.0f) continue; - int section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; + int section_n = TabItemGetSectionIdx(tab); sections[section_n].Width -= (tab->Width - shrinked_width); tab->Width = shrinked_width; } @@ -7392,7 +7429,7 @@ static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGui ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id); if (tab == NULL) return; - if (tab->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) + if (tab->Flags & ImGuiTabItemFlags_SectionMask_) return; ImGuiContext& g = *GImGui; @@ -7421,12 +7458,48 @@ static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGui } } -void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir) +void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int offset) { - IM_ASSERT(dir == -1 || dir == +1); + IM_ASSERT(offset != 0); IM_ASSERT(tab_bar->ReorderRequestTabId == 0); tab_bar->ReorderRequestTabId = tab->ID; - tab_bar->ReorderRequestDir = (ImS8)dir; + tab_bar->ReorderRequestOffset = (ImS16)offset; +} + +void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* src_tab, ImVec2 mouse_pos) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(tab_bar->ReorderRequestTabId == 0); + if ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) == 0) + return; + + const bool is_central_section = (src_tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; + const float bar_offset = tab_bar->BarRect.Min.x - (is_central_section ? tab_bar->ScrollingTarget : 0); + + // Count number of contiguous tabs we are crossing over + const int dir = (bar_offset + src_tab->Offset) > mouse_pos.x ? -1 : +1; + const int src_idx = tab_bar->Tabs.index_from_ptr(src_tab); + int dst_idx = src_idx; + for (int i = src_idx; i >= 0 && i < tab_bar->Tabs.Size; i += dir) + { + // Reordered tabs must share the same section + const ImGuiTabItem* dst_tab = &tab_bar->Tabs[i]; + if (dst_tab->Flags & ImGuiTabItemFlags_NoReorder) + break; + if ((dst_tab->Flags & ImGuiTabItemFlags_SectionMask_) != (src_tab->Flags & ImGuiTabItemFlags_SectionMask_)) + break; + dst_idx = i; + + // Include spacing after tab, so when mouse cursor is between tabs we would not continue checking further tabs that are not hovered. + const float x1 = bar_offset + dst_tab->Offset - g.Style.ItemInnerSpacing.x; + const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width + g.Style.ItemInnerSpacing.x; + //GetForegroundDrawList()->AddRect(ImVec2(x1, tab_bar->BarRect.Min.y), ImVec2(x2, tab_bar->BarRect.Max.y), IM_COL32(255, 0, 0, 255)); + if ((dir < 0 && mouse_pos.x > x1) || (dir > 0 && mouse_pos.x < x2)) + break; + } + + if (dst_idx != src_idx) + TabBarQueueReorder(tab_bar, src_tab, dst_idx - src_idx); } bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar) @@ -7436,19 +7509,23 @@ bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar) return false; //IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools - int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestDir; + int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestOffset; if (tab2_order < 0 || tab2_order >= tab_bar->Tabs.Size) return false; - // Reordered TabItem must share the same position flags than target + // Reordered tabs must share the same section + // (Note: TabBarQueueReorderFromMousePos() also has a similar test but since we allow direct calls to TabBarQueueReorder() we do it here too) ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order]; if (tab2->Flags & ImGuiTabItemFlags_NoReorder) return false; - if ((tab1->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) != (tab2->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing))) + if ((tab1->Flags & ImGuiTabItemFlags_SectionMask_) != (tab2->Flags & ImGuiTabItemFlags_SectionMask_)) return false; ImGuiTabItem item_tmp = *tab1; - *tab1 = *tab2; + ImGuiTabItem* src_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 + 1 : tab2; + ImGuiTabItem* dst_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 : tab2 + 1; + const int move_count = (tab_bar->ReorderRequestOffset > 0) ? tab_bar->ReorderRequestOffset : -tab_bar->ReorderRequestOffset; + memmove(dst_tab, src_tab, move_count * sizeof(ImGuiTabItem)); *tab2 = item_tmp; if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings) @@ -7730,7 +7807,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, const ImVec2 backup_main_cursor_pos = window->DC.CursorPos; // Layout - const bool is_central_section = (tab->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) == 0; + const bool is_central_section = (tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; size.x = tab->Width; if (is_central_section) window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_FLOOR(tab->Offset - tab_bar->ScrollingAnim), 0.0f); @@ -7778,13 +7855,11 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x) { - if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) - TabBarQueueReorder(tab_bar, tab, -1); + TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); } else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x) { - if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) - TabBarQueueReorder(tab_bar, tab, +1); + TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); } } }