From c4c7880e7ccbd05cfe8692f3b51fdfbe734a638d Mon Sep 17 00:00:00 2001 From: Ignacio Sanchez Gines <863613+drhelius@users.noreply.github.com> Date: Sat, 8 Jun 2024 19:54:31 +0200 Subject: [PATCH] Update imgui to 1.90.8 --- platforms/desktop-shared/imgui/imgui.cpp | 1215 +++++++++++------ platforms/desktop-shared/imgui/imgui.h | 353 +++-- platforms/desktop-shared/imgui/imgui_demo.cpp | 471 +++++-- platforms/desktop-shared/imgui/imgui_draw.cpp | 353 ++++- .../imgui/imgui_impl_opengl2.cpp | 5 +- .../desktop-shared/imgui/imgui_impl_opengl2.h | 2 +- .../desktop-shared/imgui/imgui_impl_sdl2.cpp | 8 +- .../desktop-shared/imgui/imgui_impl_sdl2.h | 2 +- .../desktop-shared/imgui/imgui_internal.h | 383 +++--- .../desktop-shared/imgui/imgui_tables.cpp | 177 ++- .../desktop-shared/imgui/imgui_widgets.cpp | 266 ++-- 11 files changed, 2159 insertions(+), 1076 deletions(-) diff --git a/platforms/desktop-shared/imgui/imgui.cpp b/platforms/desktop-shared/imgui/imgui.cpp index 4f01703..0d00d68 100644 --- a/platforms/desktop-shared/imgui/imgui.cpp +++ b/platforms/desktop-shared/imgui/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.4 +// dear imgui, v1.90.8 // (main code and documentation) // Help: @@ -7,15 +7,19 @@ // - Read top of imgui.cpp for more details, links and comments. // Resources: -// - FAQ https://dearimgui.com/faq -// - Getting Started https://dearimgui.com/getting-started -// - Homepage https://github.com/ocornut/imgui -// - Releases & changelog https://github.com/ocornut/imgui/releases -// - Gallery https://github.com/ocornut/imgui/issues/6897 (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 -// - Issues & support https://github.com/ocornut/imgui/issues -// - Tests & Automation https://github.com/ocornut/imgui_test_engine +// - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md) +// - Homepage ................... https://github.com/ocornut/imgui +// - Releases & changelog ....... https://github.com/ocornut/imgui/releases +// - Gallery .................... https://github.com/ocornut/imgui/issues/7503 (please post your screenshots/video there!) +// - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there) +// - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code) +// - Third-party Extensions https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more) +// - Bindings/Backends https://github.com/ocornut/imgui/wiki/Bindings (language bindings, backends for various tech/engines) +// - Glossary https://github.com/ocornut/imgui/wiki/Glossary +// - Debug Tools https://github.com/ocornut/imgui/wiki/Debug-Tools +// - Software using Dear ImGui https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui +// - Issues & support ........... https://github.com/ocornut/imgui/issues +// - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps) // For first-time users having issues compiling/linking/running/loading fonts: // please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. @@ -26,7 +30,7 @@ // 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 B2B invoiced technical support, maintenance and sponsoring contracts. -// PLEASE reach out at omar AT dearimgui DOT com. See https://github.com/ocornut/imgui/wiki/Sponsors +// PLEASE reach out at omar AT dearimgui DOT com. See https://github.com/ocornut/imgui/wiki/Funding // Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine. // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library. @@ -73,6 +77,7 @@ CODE // [SECTION] RENDER HELPERS // [SECTION] INITIALIZATION, SHUTDOWN // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) +// [SECTION] ID STACK // [SECTION] INPUTS // [SECTION] ERROR CHECKING // [SECTION] ITEM SUBMISSION @@ -138,7 +143,7 @@ CODE - CTRL+X, CTRL+C, CTRL+V: Use OS clipboard. - CTRL+Z, CTRL+Y: Undo, Redo. - ESCAPE: Revert text to its original value. - - On OSX, controls are automatically adjusted to match standard OSX text editing shortcuts and behaviors. + - On OSX, controls are automatically adjusted to match standard OSX text editing 2ts and behaviors. - KEYBOARD CONTROLS - Basic: @@ -425,6 +430,38 @@ CODE 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. + - 2024/06/06 (1.90.8) - reordered ImGuiInputTextFlags values. This should not be breaking unless you are using generated headers that have values not matching the main library. + - 2024/06/06 (1.90.8) - removed 'ImGuiButtonFlags_MouseButtonDefault_ = ImGuiButtonFlags_MouseButtonLeft', was mostly unused and misleading. + - 2024/05/27 (1.90.7) - commented out obsolete symbols marked obsolete in 1.88 (May 2022): + - old: CaptureKeyboardFromApp(bool) + - new: SetNextFrameWantCaptureKeyboard(bool) + - old: CaptureMouseFromApp(bool) + - new: SetNextFrameWantCaptureMouse(bool) + - 2024/05/22 (1.90.7) - inputs (internals): renamed ImGuiKeyOwner_None to ImGuiKeyOwner_NoOwner, to make use more explicit and reduce confusion with the default it is a non-zero value and cannot be the default value (never made public, but disclosing as I expect a few users caught on owner-aware inputs). + - inputs (internals): renamed ImGuiInputFlags_RouteGlobalLow -> ImGuiInputFlags_RouteGlobal, ImGuiInputFlags_RouteGlobal -> ImGuiInputFlags_RouteGlobalOverFocused, ImGuiInputFlags_RouteGlobalHigh -> ImGuiInputFlags_RouteGlobalHighest. + - inputs (internals): Shortcut(), SetShortcutRouting(): swapped last two parameters order in function signatures: + - old: Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0); + - new: Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0, ImGuiID owner_id = 0); + - inputs (internals): owner-aware versions of IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(): swapped last two parameters order in function signatures. + - old: IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0); + - new: IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0); + - old: IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0); + - new: IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0); + for various reasons those changes makes sense. They are being made because making some of those API public. + only past users of imgui_internal.h with the extra parameters will be affected. Added asserts for valid flags in various functions to detect _some_ misuses, BUT NOT ALL. + - 2024/05/16 (1.90.7) - inputs: on macOS X, Cmd and Ctrl keys are now automatically swapped by io.AddKeyEvent() as this naturally align with how macOS X uses those keys. + - it shouldn't really affect you unless you had custom shortcut swapping in place for macOS X apps. + - removed ImGuiMod_Shortcut which was previously dynamically remapping to Ctrl or Cmd/Super. It is now unnecessary to specific cross-platform idiomatic shortcuts. (#2343, #4084, #5923, #456) + - 2024/05/14 (1.90.7) - backends: SDL_Renderer2 and SDL_Renderer3 backend now take a SDL_Renderer* in their RenderDrawData() functions. + - 2024/04/18 (1.90.6) - TreeNode: Fixed a layout inconsistency when using an empty/hidden label followed by a SameLine() call. (#7505, #282) + - old: TreeNode("##Hidden"); SameLine(); Text("Hello"); // <-- This was actually incorrect! BUT appeared to look ok with the default style where ItemSpacing.x == FramePadding.x * 2 (it didn't look aligned otherwise). + - new: TreeNode("##Hidden"); SameLine(0, 0); Text("Hello"); // <-- This is correct for all styles values. + with the fix, IF you were successfully using TreeNode("")+SameLine(); you will now have extra spacing between your TreeNode and the following item. + You'll need to change the SameLine() call to SameLine(0,0) to remove this extraneous spacing. This seemed like the more sensible fix that's not making things less consistent. + (Note: when using this idiom you are likely to also use ImGuiTreeNodeFlags_SpanAvailWidth). + - 2024/03/18 (1.90.5) - merged the radius_x/radius_y parameters in ImDrawList::AddEllipse(), AddEllipseFilled() and PathEllipticalArcTo() into a single ImVec2 parameter. Exceptionally, because those functions were added in 1.90, we are not adding inline redirection functions. The transition is easy and should affect few users. (#2743, #7417) + - 2024/03/08 (1.90.5) - inputs: more formally obsoleted GetKeyIndex() when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is set. It has been unnecessary and a no-op since 1.87 (it returns the same value as passed when used with a 1.87+ backend using io.AddKeyEvent() function). (#4921) + - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX) - 2024/01/15 (1.90.2) - commented out obsolete ImGuiIO::ImeWindowHandle marked obsolete in 1.87, favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. - 2023/12/19 (1.90.1) - commented out obsolete ImGuiKey_KeyPadEnter redirection to ImGuiKey_KeypadEnter. - 2023/11/06 (1.90.1) - removed CalcListClipping() marked obsolete in 1.86. Prefer using ImGuiListClipper which can return non-contiguous ranges. @@ -537,7 +574,7 @@ CODE - Backend writing to io.MouseHoveredViewport -> backend should call io.AddMouseViewportEvent() [Docking branch w/ multi-viewports only] note: for all calls to IO new functions, the Dear ImGui context should be bound/current. read https://github.com/ocornut/imgui/issues/4921 for details. - - 2022/01/10 (1.87) - inputs: reworked keyboard IO. Removed io.KeyMap[], io.KeysDown[] in favor of calling io.AddKeyEvent(). Removed GetKeyIndex(), now unecessary. All IsKeyXXX() functions now take ImGuiKey values. All features are still functional until IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Read Changelog and Release Notes for details. + - 2022/01/10 (1.87) - inputs: reworked keyboard IO. Removed io.KeyMap[], io.KeysDown[] in favor of calling io.AddKeyEvent(). Removed GetKeyIndex(), now unnecessary. All IsKeyXXX() functions now take ImGuiKey values. All features are still functional until IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Read Changelog and Release Notes for details. - IsKeyPressed(MY_NATIVE_KEY_XXX) -> use IsKeyPressed(ImGuiKey_XXX) - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX) - Backend writing to io.KeyMap[],io.KeysDown[] -> backend should call io.AddKeyEvent() (+ call io.SetKeyEventNativeData() if you want legacy user code to stil function with legacy key codes). @@ -935,7 +972,7 @@ CODE A: - Businesses: please reach out to "omar AT dearimgui DOT 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 sustain and grow work on this project. - Also see https://github.com/ocornut/imgui/wiki/Sponsors + >>> See https://github.com/ocornut/imgui/wiki/Funding - Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine. - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, 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. @@ -986,7 +1023,8 @@ CODE #else #include #endif -#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have all Win32 functions +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP || WINAPI_FAMILY == WINAPI_FAMILY_GAMES) +// The UWP and GDK Win32 API subsets don't support clipboard nor IME functions #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS #endif @@ -1026,6 +1064,7 @@ CODE #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 #pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access #elif defined(__GNUC__) // We disable -Wpragmas because GCC doesn't provide a has_warning equivalent and some forks/patches may not follow the warning/version association. #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind @@ -1062,7 +1101,6 @@ static const ImVec2 TOOLTIP_DEFAULT_OFFSET = ImVec2(16, 10); // Multi //------------------------------------------------------------------------- static void SetCurrentWindow(ImGuiWindow* window); -static void FindHoveredWindow(); static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags); static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window); @@ -1082,6 +1120,9 @@ static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport namespace ImGui { +// Item +static void ItemHandleShortcut(ImGuiID id); + // Navigation static void NavUpdate(); static void NavUpdateWindowing(); @@ -1124,6 +1165,7 @@ static void RenderWindowDecorations(ImGuiWindow* window, const ImRec static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col); static void RenderDimmedBackgrounds(); +static void SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect); // Viewports const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHashStr("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. @@ -1179,58 +1221,59 @@ static void* GImAllocatorUserData = NULL; ImGuiStyle::ImGuiStyle() { - Alpha = 1.0f; // Global alpha applies to everything in Dear ImGui. - DisabledAlpha = 0.60f; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha. - WindowPadding = ImVec2(8,8); // Padding within a window - WindowRounding = 0.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended. - WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested. - WindowMinSize = ImVec2(32,32); // Minimum window size - WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text - WindowMenuButtonPosition= ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left. - ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows - ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested. - PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows - PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested. - FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets) - FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets). - FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested. - ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines - ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) - CellPadding = ImVec2(4,2); // Padding within a table cell. CellPadding.y may be altered between different rows. - TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! - IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). - ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1). - ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar - ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar - GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar - GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. - LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero. - TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. - TabBorderSize = 0.0f; // Thickness of border around tabs. - TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. - TabBarBorderSize = 1.0f; // Thickness of tab-bar separator, which takes on the tab active color to denote focus. - TableAngledHeadersAngle = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees). - ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. - ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. - SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. - SeparatorTextBorderSize = 3.0f; // Thickkness of border in SeparatorText() - SeparatorTextAlign = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center). - SeparatorTextPadding = ImVec2(20.0f,3.f);// Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y. - DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows. - DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. - MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. - AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. - AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). - AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.). - CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. - CircleTessellationMaxError = 0.30f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. + Alpha = 1.0f; // Global alpha applies to everything in Dear ImGui. + DisabledAlpha = 0.60f; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha. + WindowPadding = ImVec2(8,8); // Padding within a window + WindowRounding = 0.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended. + WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested. + WindowMinSize = ImVec2(32,32); // Minimum window size + WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text + WindowMenuButtonPosition = ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left. + ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows + ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested. + PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows + PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested. + FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets) + FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets). + FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested. + ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines + ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) + CellPadding = ImVec2(4,2); // Padding within a table cell. Cellpadding.x is locked for entire table. CellPadding.y may be altered between different rows. + TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! + IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). + ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1). + ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar + ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar + GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar + GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. + LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero. + TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. + TabBorderSize = 0.0f; // Thickness of border around tabs. + TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. + TabBarBorderSize = 1.0f; // Thickness of tab-bar separator, which takes on the tab active color to denote focus. + TableAngledHeadersAngle = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees). + TableAngledHeadersTextAlign = ImVec2(0.5f,0.0f);// Alignment of angled headers within the cell + ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. + ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. + SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. + SeparatorTextBorderSize = 3.0f; // Thickkness of border in SeparatorText() + SeparatorTextAlign = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center). + SeparatorTextPadding = ImVec2(20.0f,3.f);// Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y. + DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows. + DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. + MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. + AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. + AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). + AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.). + CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. + CircleTessellationMaxError = 0.30f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. // Behaviors - HoverStationaryDelay = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary. - HoverDelayShort = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay. - HoverDelayNormal = 0.40f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). " - HoverFlagsForTooltipMouse = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse. - HoverFlagsForTooltipNav = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad. + HoverStationaryDelay = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary. + HoverDelayShort = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay. + HoverDelayNormal = 0.40f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). " + HoverFlagsForTooltipMouse = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse. + HoverFlagsForTooltipNav = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad. // Default theme ImGui::StyleColorsDark(this); @@ -1462,7 +1505,7 @@ static ImGuiInputEvent* FindLatestInputEvent(ImGuiContext* ctx, ImGuiInputEventT // - bool down: Is the key down? use false to signify a key release. // - float analog_value: 0.0f..1.0f // IMPORTANT: THIS FUNCTION AND OTHER "ADD" GRABS THE CONTEXT FROM OUR INSTANCE. -// WE NEED TO ENSURE THAT ALL FUNCTION CALLS ARE FULLFILLING THIS, WHICH IS WHY GetKeyData() HAS AN EXPLICIT CONTEXT. +// WE NEED TO ENSURE THAT ALL FUNCTION CALLS ARE FULFILLING THIS, WHICH IS WHY GetKeyData() HAS AN EXPLICIT CONTEXT. void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value) { //if (e->Down) { IMGUI_DEBUG_LOG_IO("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); } @@ -1470,9 +1513,19 @@ void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value) if (key == ImGuiKey_None || !AppAcceptingEvents) return; ImGuiContext& g = *Ctx; - IM_ASSERT(ImGui::IsNamedKeyOrModKey(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API. + IM_ASSERT(ImGui::IsNamedKeyOrMod(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API. IM_ASSERT(ImGui::IsAliasKey(key) == false); // Backend cannot submit ImGuiKey_MouseXXX values they are automatically inferred from AddMouseXXX() events. - IM_ASSERT(key != ImGuiMod_Shortcut); // We could easily support the translation here but it seems saner to not accept it (TestEngine perform a translation itself) + + // MacOS: swap Cmd(Super) and Ctrl + if (g.IO.ConfigMacOSXBehaviors) + { + if (key == ImGuiMod_Super) { key = ImGuiMod_Ctrl; } + else if (key == ImGuiMod_Ctrl) { key = ImGuiMod_Super; } + else if (key == ImGuiKey_LeftSuper) { key = ImGuiKey_LeftCtrl; } + else if (key == ImGuiKey_RightSuper){ key = ImGuiKey_RightCtrl; } + else if (key == ImGuiKey_LeftCtrl) { key = ImGuiKey_LeftSuper; } + else if (key == ImGuiKey_RightCtrl) { key = ImGuiKey_RightSuper; } + } // Verify that backend isn't mixing up using new io.AddKeyEvent() api and old io.KeysDown[] + io.KeyMap[] data. #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO @@ -1577,12 +1630,36 @@ void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down) if (!AppAcceptingEvents) return; + // On MacOS X: Convert Ctrl(Super)+Left click into Right-click: handle held button. + if (ConfigMacOSXBehaviors && mouse_button == 0 && MouseCtrlLeftAsRightClick) + { + // Order of both statements matterns: this event will still release mouse button 1 + mouse_button = 1; + if (!down) + MouseCtrlLeftAsRightClick = false; + } + // Filter duplicate const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseButton, (int)mouse_button); const bool latest_button_down = latest_event ? latest_event->MouseButton.Down : g.IO.MouseDown[mouse_button]; if (latest_button_down == down) return; + // On MacOS X: Convert Ctrl(Super)+Left click into Right-click. + // - Note that this is actual physical Ctrl which is ImGuiMod_Super for us. + // - At this point we want from !down to down, so this is handling the initial press. + if (ConfigMacOSXBehaviors && mouse_button == 0 && down) + { + const ImGuiInputEvent* latest_super_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)ImGuiMod_Super); + if (latest_super_event ? latest_super_event->Key.Down : g.IO.KeySuper) + { + IMGUI_DEBUG_LOG_IO("[io] Super+Left Click aliased into Right Click\n"); + MouseCtrlLeftAsRightClick = true; + AddMouseButtonEvent(1, true); // This is just quicker to write that passing through, as we need to filter duplicate again. + return; + } + } + ImGuiInputEvent e; e.Type = ImGuiInputEventType_MouseButton; e.Source = ImGuiInputSource_Mouse; @@ -2318,6 +2395,20 @@ const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const cha return in_text_start; } +int ImTextCountLines(const char* in_text, const char* in_text_end) +{ + if (in_text_end == NULL) + in_text_end = in_text + strlen(in_text); // FIXME-OPT: Not optimal approach, discourage use for now. + int count = 0; + while (in_text < in_text_end) + { + const char* line_end = (const char*)memchr(in_text, '\n', in_text_end - in_text); + in_text = line_end ? line_end + 1 : in_text_end; + count++; + } + return count; +} + IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- @@ -3100,35 +3191,38 @@ void ImGui::PopStyleColor(int count) static const ImGuiDataVarInfo GStyleVarInfo[] = { - { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha - { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha - { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding - { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding - { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize - { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize - { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign - { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding - { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize - { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding - { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize - { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding - { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding - { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize - { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing - { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing - { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing - { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding - { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize - { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding - { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize - { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding - { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding - { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) }, // ImGuiStyleVar_TabBarBorderSize - { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign - { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign - { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)},// ImGuiStyleVar_SeparatorTextBorderSize - { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign - { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) }, // ImGuiStyleVar_SeparatorTextPadding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBorderSize) }, // ImGuiStyleVar_TabBorderSize + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) }, // ImGuiStyleVar_TabBarBorderSize + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersAngle)}, // ImGuiStyleVar_TableAngledHeadersAngle + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)}, // ImGuiStyleVar_SeparatorTextBorderSize + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) }, // ImGuiStyleVar_SeparatorTextPadding }; const ImGuiDataVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx) @@ -3772,50 +3866,12 @@ ImGuiWindow::~ImGuiWindow() ColumnsStorage.clear_destruct(); } -ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) -{ - ImGuiID seed = IDStack.back(); - ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); - ImGuiContext& g = *Ctx; - if (g.DebugHookIdInfo == id) - ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); - return id; -} - -ImGuiID ImGuiWindow::GetID(const void* ptr) -{ - ImGuiID seed = IDStack.back(); - ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); - ImGuiContext& g = *Ctx; - if (g.DebugHookIdInfo == id) - ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL); - return id; -} - -ImGuiID ImGuiWindow::GetID(int n) -{ - ImGuiID seed = IDStack.back(); - ImGuiID id = ImHashData(&n, sizeof(n), seed); - ImGuiContext& g = *Ctx; - if (g.DebugHookIdInfo == id) - ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL); - return id; -} - -// This is only used in rare/specific situations to manufacture an ID out of nowhere. -ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) -{ - ImGuiID seed = IDStack.back(); - ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs); - ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed); - return id; -} - static void SetCurrentWindow(ImGuiWindow* window) { ImGuiContext& g = *GImGui; g.CurrentWindow = window; g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL; + g.CurrentDpiScale = 1.0f; // FIXME-DPI: WIP this is modified in docking if (window) { g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); @@ -4138,6 +4194,11 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flag if (g.HoveredIdPreviousFrame != id) return false; } + + // Display shortcut (only works with mouse) + if (id == g.LastItemData.ID && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasShortcut)) + if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal)) + SetTooltip("%s", GetKeyChordName(g.LastItemData.Shortcut)); } // When disabled we'll return false but still set HoveredId @@ -4178,7 +4239,7 @@ bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id) ImGuiWindow* window = g.CurrentWindow; if (!bb.Overlaps(window->ClipRect)) if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId)) - if (!g.LogEnabled) + if (!g.ItemUnclipByLog) return true; return false; } @@ -4482,6 +4543,9 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() { ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; + + // FIXME-DPI: This storage was added on 2021/03/31 for test engine, but if we want to multiply WINDOWS_HOVER_PADDING + // by DpiScale, we need to make this window-agnostic anyhow, maybe need storing inside ImGuiWindow. g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING)); // Find the window hovered by mouse: @@ -4489,7 +4553,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame. // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms. bool clear_hovered_windows = false; - FindHoveredWindow(); + FindHoveredWindowEx(g.IO.MousePos, false, &g.HoveredWindow, &g.HoveredWindowUnderMovingWindow); // Modal windows prevents mouse from hovering behind them. ImGuiWindow* modal_window = GetTopMostPopupModal(); @@ -4553,6 +4617,27 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; } +// Calling SetupDrawListSharedData() is followed by SetCurrentFont() which sets up the remaining data. +static void SetupDrawListSharedData() +{ + ImGuiContext& g = *GImGui; + ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); + for (ImGuiViewportP* viewport : g.Viewports) + virtual_space.Add(viewport->GetMainRect()); + g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4(); + g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; + g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError); + g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; + if (g.Style.AntiAliasedLines) + g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines; + if (g.Style.AntiAliasedLinesUseTex && !(g.IO.Fonts->Flags & ImFontAtlasFlags_NoBakedLines)) + g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex; + if (g.Style.AntiAliasedFill) + g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill; + if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) + g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset; +} + void ImGui::NewFrame() { IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); @@ -4595,23 +4680,9 @@ void ImGui::NewFrame() // Setup current font and draw list shared data g.IO.Fonts->Locked = true; + SetupDrawListSharedData(); SetCurrentFont(GetDefaultFont()); IM_ASSERT(g.Font->IsLoaded()); - ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); - for (ImGuiViewportP* viewport : g.Viewports) - virtual_space.Add(viewport->GetMainRect()); - g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4(); - g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; - g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError); - g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; - if (g.Style.AntiAliasedLines) - g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines; - if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines)) - g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex; - if (g.Style.AntiAliasedFill) - g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill; - if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) - g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset; // Mark rendering data as invalid to prevent user who may have a handle on it to use it. for (ImGuiViewportP* viewport : g.Viewports) @@ -5209,16 +5280,18 @@ ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_tex } // Find window given position, search front-to-back -// FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically -// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is -// called, aka before the next Begin(). Moving window isn't affected. -static void FindHoveredWindow() +// - Typically write output back to g.HoveredWindow and g.HoveredWindowUnderMovingWindow. +// - FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically +// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is +// called, aka before the next Begin(). Moving window isn't affected. +// - The 'find_first_and_in_any_viewport = true' mode is only used by TestEngine. It is simpler to maintain here. +void ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window) { ImGuiContext& g = *GImGui; - ImGuiWindow* hovered_window = NULL; - ImGuiWindow* hovered_window_ignoring_moving_window = NULL; - if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs)) + ImGuiWindow* hovered_window_under_moving_window = NULL; + + if (find_first_and_in_any_viewport == false && g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs)) hovered_window = g.MovingWindow; ImVec2 padding_regular = g.Style.TouchExtraPadding; @@ -5234,7 +5307,7 @@ static void FindHoveredWindow() // Using the clipped AABB, a child window will typically be clipped by its parent (not always) ImVec2 hit_padding = (window->Flags & (ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) ? padding_regular : padding_for_resize; - if (!window->OuterRectClipped.ContainsWithPad(g.IO.MousePos, hit_padding)) + if (!window->OuterRectClipped.ContainsWithPad(pos, hit_padding)) continue; // Support for one rectangular hole in any given window @@ -5243,21 +5316,30 @@ static void FindHoveredWindow() { ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y); ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y); - if (ImRect(hole_pos, hole_pos + hole_size).Contains(g.IO.MousePos)) + if (ImRect(hole_pos, hole_pos + hole_size).Contains(pos)) continue; } - if (hovered_window == NULL) + if (find_first_and_in_any_viewport) + { 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) break; + } + else + { + if (hovered_window == NULL) + hovered_window = window; + IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer. + if (hovered_window_under_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow)) + hovered_window_under_moving_window = window; + if (hovered_window && hovered_window_under_moving_window) + break; + } } - g.HoveredWindow = hovered_window; - g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window; + *out_hovered_window = hovered_window; + if (out_hovered_window_under_moving_window != NULL) + *out_hovered_window_under_moving_window = hovered_window_under_moving_window; } bool ImGui::IsItemActive() @@ -5557,7 +5639,9 @@ void ImGui::EndChild() else { // Not navigable into - ItemAdd(bb, 0); + // - This is a bit of a fringe use case, mostly useful for undecorated, non-scrolling contents childs, or empty childs. + // - We could later decide to not apply this path if ImGuiChildFlags_FrameStyle or ImGuiChildFlags_Borders is set. + ItemAdd(bb, child_window->ChildId, NULL, ImGuiItemFlags_NoNav); // But when flattened we directly reach items, adjust active layer mask accordingly if (child_window->Flags & ImGuiWindowFlags_NavFlattened) @@ -5695,7 +5779,7 @@ static inline ImVec2 CalcWindowMinSize(ImGuiWindow* window) // Reduce artifacts with very small windows ImGuiWindow* window_for_height = window; - size_min.y = ImMax(size_min.y, window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); + size_min.y = ImMax(size_min.y, window_for_height->TitleBarHeight + window_for_height->MenuBarHeight + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); return size_min; } @@ -5768,10 +5852,18 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont ImVec2 size_max = ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup)) ? ImVec2(FLT_MAX, FLT_MAX) : ImGui::GetMainViewport()->WorkSize - style.DisplaySafeAreaPadding * 2.0f; ImVec2 size_auto_fit = ImClamp(size_desired, size_min, size_max); + // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one axis may be auto-fit when calculating scrollbars, + // we may need to compute/store three variants of size_auto_fit, for x/y/xy. + // Here we implement a workaround for child windows only, but a full solution would apply to normal windows as well: + if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && !(window->ChildFlags & ImGuiChildFlags_ResizeY)) + size_auto_fit.y = window->SizeFull.y; + else if (!(window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->ChildFlags & ImGuiChildFlags_ResizeY)) + size_auto_fit.x = window->SizeFull.x; + // 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 - decoration_w_without_scrollbars < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); + bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - decoration_w_without_scrollbars < 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_h_without_scrollbars < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); if (will_have_scrollbar_x) size_auto_fit.y += style.ScrollbarSize; @@ -5892,13 +5984,13 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si int ret_auto_fit_mask = 0x00; const float grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); - const float grip_hover_inner_size = IM_TRUNC(grip_draw_size * 0.75f); + const float grip_hover_inner_size = (resize_grip_count > 0) ? IM_TRUNC(grip_draw_size * 0.75f) : 0.0f; const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_HOVER_PADDING : 0.0f; ImRect clamp_rect = visibility_rect; const bool window_move_from_title_bar = g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar); if (window_move_from_title_bar) - clamp_rect.Min.y -= window->TitleBarHeight(); + clamp_rect.Min.y -= window->TitleBarHeight; ImVec2 pos_target(FLT_MAX, FLT_MAX); ImVec2 size_target(FLT_MAX, FLT_MAX); @@ -5973,6 +6065,7 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si if (held && g.IO.MouseDoubleClicked[0]) { // Double-clicking bottom or right border auto-fit on this axis + // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one side may be auto-fit when calculating scrollbars. // FIXME: Support top and right borders: rework CalcResizePosSizeFromAnyCorner() to be reusable in both cases. if (border_n == 1 || border_n == 3) // Right and bottom border { @@ -6020,10 +6113,13 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si border_target = ImClamp(border_target, clamp_min, clamp_max); if (flags & ImGuiWindowFlags_ChildWindow) // Clamp resizing of childs within parent { - if ((flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (flags & ImGuiWindowFlags_NoScrollbar)) - border_target.x = ImClamp(border_target.x, window->ParentWindow->InnerClipRect.Min.x, window->ParentWindow->InnerClipRect.Max.x); - if (flags & ImGuiWindowFlags_NoScrollbar) - border_target.y = ImClamp(border_target.y, window->ParentWindow->InnerClipRect.Min.y, window->ParentWindow->InnerClipRect.Max.y); + ImGuiWindowFlags parent_flags = window->ParentWindow->Flags; + ImRect border_limit_rect = window->ParentWindow->InnerRect; + border_limit_rect.Expand(ImVec2(-ImMax(window->WindowPadding.x, window->WindowBorderSize), -ImMax(window->WindowPadding.y, window->WindowBorderSize))); + if ((parent_flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (parent_flags & ImGuiWindowFlags_NoScrollbar)) + border_target.x = ImClamp(border_target.x, border_limit_rect.Min.x, border_limit_rect.Max.x); + if (parent_flags & ImGuiWindowFlags_NoScrollbar) + border_target.y = ImClamp(border_target.y, border_limit_rect.Min.y, border_limit_rect.Max.y); } if (!ignore_resize) CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target); @@ -6093,7 +6189,7 @@ static inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_ ImGuiContext& g = *GImGui; ImVec2 size_for_clamping = window->Size; if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) - size_for_clamping.y = window->TitleBarHeight(); + size_for_clamping.y = window->TitleBarHeight; window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max); } @@ -6129,7 +6225,7 @@ static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) } if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) { - float y = window->Pos.y + window->TitleBarHeight() - 1; + float y = window->Pos.y + window->TitleBarHeight - 1; window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), border_col, g.Style.FrameBorderSize); } } @@ -6174,7 +6270,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar } if (override_alpha) bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT); - window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom); + window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom); } // Title bar @@ -6329,6 +6425,30 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags } } +// [EXPERIMENTAL] Called by Begin(). NextWindowData is valid at this point. +// This is designed as a toy/test-bed for +void ImGui::UpdateWindowSkipRefresh(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + window->SkipRefresh = false; + if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasRefreshPolicy) == 0) + return; + if (g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_TryToAvoidRefresh) + { + // FIXME-IDLE: Tests for e.g. mouse clicks or keyboard while focused. + if (window->Appearing) // If currently appearing + return; + if (window->Hidden) // If was hidden (previous frame) + return; + if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnHover) && g.HoveredWindow && window->RootWindow == g.HoveredWindow->RootWindow) + return; + if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnFocus) && g.NavWindow && window->RootWindow == g.NavWindow->RootWindow) + return; + window->DrawList = NULL; + window->SkipRefresh = true; + } +} + // When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing) // should be positioned behind that modal window, unless the window was created inside the modal begin-stack. // In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent. @@ -6341,7 +6461,7 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags // - WindowE // .. returns NULL // Notes: // - FindBlockingModal(NULL) == NULL is generally equivalent to GetTopMostPopupModal() == NULL. -// Only difference is here we check for ->Active/WasActive but it may be unecessary. +// Only difference is here we check for ->Active/WasActive but it may be unnecessary. ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window) { ImGuiContext& g = *GImGui; @@ -6444,6 +6564,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window_stack_data.Window = window; window_stack_data.ParentLastItemDataBackup = g.LastItemData; window_stack_data.StackSizesOnBegin.SetToContextState(&g); + window_stack_data.DisabledOverrideReenable = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; g.CurrentWindowStack.push_back(window_stack_data); if (flags & ImGuiWindowFlags_ChildMenu) g.BeginMenuDepth++; @@ -6463,7 +6584,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) PushFocusScope((flags & ImGuiWindowFlags_NavFlattened) ? g.CurrentFocusScopeId : window->ID); window->NavRootFocusScopeId = g.CurrentFocusScopeId; - // Add to popup stack + // Add to popup stacks: update OpenPopupStack[] data, push to BeginPopupStack[] if (flags & ImGuiWindowFlags_Popup) { ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; @@ -6527,11 +6648,18 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->Appearing) SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false); + // [EXPERIMENTAL] Skip Refresh mode + UpdateWindowSkipRefresh(window); + + // Nested root windows (typically tooltips) override disabled state + if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window) + BeginDisabledOverrideReenable(); + // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() g.CurrentWindow = NULL; // When reusing window again multiple times a frame, just append content (don't need to setup again) - if (first_begin_of_the_frame) + if (first_begin_of_the_frame && !window->SkipRefresh) { // Initialize const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345) @@ -6609,9 +6737,17 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size. window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x); window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y; + window->TitleBarHeight = (flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : g.FontSize + g.Style.FramePadding.y * 2.0f; + window->MenuBarHeight = (flags & ImGuiWindowFlags_MenuBar) ? window->DC.MenuBarOffset.y + g.FontSize + g.Style.FramePadding.y * 2.0f : 0.0f; + // Depending on condition we use previous or current window size to compare against contents size to decide if a scrollbar should be visible. + // Those flags will be altered further down in the function depending on more conditions. bool use_current_size_for_scrollbar_x = window_just_created; bool use_current_size_for_scrollbar_y = window_just_created; + if (window_size_x_set_by_api && window->ContentSizeExplicit.x != 0.0f) + use_current_size_for_scrollbar_x = true; + if (window_size_y_set_by_api && window->ContentSizeExplicit.y != 0.0f) // #7252 + use_current_size_for_scrollbar_y = true; // Collapse window by double-clicking on title bar // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing @@ -6619,8 +6755,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), so verify that we don't have items over the title bar. ImRect title_bar_rect = window->TitleBarRect(); - if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseClickedCount[0] == 2) - window->WantCollapseToggle = true; + if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max)) + if (g.IO.MouseClickedCount[0] == 2 && GetKeyOwner(ImGuiKey_MouseLeft) == ImGuiKeyOwner_NoOwner) + window->WantCollapseToggle = true; if (window->WantCollapseToggle) { window->Collapsed = !window->Collapsed; @@ -6638,11 +6775,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // SIZE // Outer Decoration Sizes - // (we need to clear ScrollbarSize immediatly as CalcWindowAutoFitSize() needs it and can be called from other locations). + // (we need to clear ScrollbarSize immediately as CalcWindowAutoFitSize() needs it and can be called from other locations). const ImVec2 scrollbar_sizes_from_last_frame = window->ScrollbarSizes; window->DecoOuterSizeX1 = 0.0f; window->DecoOuterSizeX2 = 0.0f; - window->DecoOuterSizeY1 = window->TitleBarHeight() + window->MenuBarHeight(); + window->DecoOuterSizeY1 = window->TitleBarHeight + window->MenuBarHeight; window->DecoOuterSizeY2 = 0.0f; window->ScrollbarSizes = ImVec2(0.0f, 0.0f); @@ -6791,7 +6928,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar)); window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)); if (window->ScrollbarX && !window->ScrollbarY) - window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar); + window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar); window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); // Amend the partially filled window->DecorationXXX values. @@ -6826,17 +6963,19 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->DecoOuterSizeY2; // Inner clipping rectangle. - // Will extend a little bit outside the normal work region. - // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space. - // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. + // - Extend a outside of normal work region up to borders. + // - This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space. + // - It also makes clipped items be more noticeable. + // - And is consistent on both axis (prior to 2024/05/03 ClipRect used WindowPadding.x * 0.5f on left and right edge), see #3312 + // - Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior. // Affected by window/frame border size. Used by: // - Begin() initial clip rect float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize); - window->InnerClipRect.Min.x = ImTrunc(0.5f + window->InnerRect.Min.x + ImMax(ImTrunc(window->WindowPadding.x * 0.5f), window->WindowBorderSize)); - window->InnerClipRect.Min.y = ImTrunc(0.5f + window->InnerRect.Min.y + top_border_size); - window->InnerClipRect.Max.x = ImTrunc(0.5f + window->InnerRect.Max.x - ImMax(ImTrunc(window->WindowPadding.x * 0.5f), window->WindowBorderSize)); - window->InnerClipRect.Max.y = ImTrunc(0.5f + window->InnerRect.Max.y - window->WindowBorderSize); + window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + window->WindowBorderSize); + window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size); + window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - window->WindowBorderSize); + window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize); window->InnerClipRect.ClipWithFull(host_rect); // Default item width. Make it proportional to window size if window manually resizes @@ -6997,7 +7136,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin(). // This is useful to allow creating context menus on title bar only, etc. - SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect); + SetLastItemDataForWindow(window, title_bar_rect); // [DEBUG] #ifndef IMGUI_DISABLE_DEBUG_TOOLS @@ -7013,11 +7152,17 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } else { + // Skip refresh always mark active + if (window->SkipRefresh) + window->Active = true; + // Append SetCurrentWindow(window); + SetLastItemDataForWindow(window, window->TitleBarRect()); } - PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); + if (!window->SkipRefresh) + PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); // Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused) window->WriteAccessed = false; @@ -7025,7 +7170,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) g.NextWindowData.ClearFlags(); // Update visibility - if (first_begin_of_the_frame) + if (first_begin_of_the_frame && !window->SkipRefresh) { if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu)) { @@ -7071,6 +7216,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) skip_items = true; window->SkipItems = skip_items; } + else if (first_begin_of_the_frame) + { + // Skip refresh mode + window->SkipItems = true; + } // [DEBUG] io.ConfigDebugBeginReturnValue override return value to test Begin/End and BeginChild/EndChild behaviors. // (The implicit fallback window is NOT automatically ended allowing it to always be able to receive commands without crashing) @@ -7087,6 +7237,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) return !window->SkipItems; } +static void ImGui::SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect) +{ + ImGuiContext& g = *GImGui; + SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(rect.Min, rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, rect); +} + void ImGui::End() { ImGuiContext& g = *GImGui; @@ -7098,7 +7254,7 @@ void ImGui::End() IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!"); return; } - IM_ASSERT(g.CurrentWindowStack.Size > 0); + ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back(); // Error checking: verify that user doesn't directly call End() on a child window. if (window->Flags & ImGuiWindowFlags_ChildWindow) @@ -7107,8 +7263,17 @@ void ImGui::End() // Close anything that is open if (window->DC.CurrentColumns) EndColumns(); - PopClipRect(); // Inner window clip rectangle + if (!window->SkipRefresh) + PopClipRect(); // Inner window clip rectangle PopFocusScope(); + if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window) + EndDisabledOverrideReenable(); + + if (window->SkipRefresh) + { + IM_ASSERT(window->DrawList == NULL); + window->DrawList = &window->DrawListInst; + } // Stop logging if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging @@ -7118,12 +7283,12 @@ void ImGui::End() ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); // Pop from window stack - g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup; + g.LastItemData = window_stack_data.ParentLastItemDataBackup; if (window->Flags & ImGuiWindowFlags_ChildMenu) g.BeginMenuDepth--; if (window->Flags & ImGuiWindowFlags_Popup) g.BeginPopupStack.pop_back(); - g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithContextState(&g); + window_stack_data.StackSizesOnBegin.CompareWithContextState(&g); g.CurrentWindowStack.pop_back(); SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window); } @@ -7215,9 +7380,13 @@ void ImGui::FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags) if ((flags & ImGuiFocusRequestFlags_UnlessBelowModal) && (g.NavWindow != window)) // Early out in common case. if (ImGuiWindow* blocking_modal = FindBlockingModal(window)) { + // This block would typically be reached in two situations: + // - API call to FocusWindow() with a window under a modal and ImGuiFocusRequestFlags_UnlessBelowModal flag. + // - User clicking on void or anything behind a modal while a modal is open (window == NULL) IMGUI_DEBUG_LOG_FOCUS("[focus] FocusWindow(\"%s\", UnlessBelowModal): prevented by \"%s\".\n", window ? window->Name : "", blocking_modal->Name); if (window && window == window->RootWindow && (window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0) - BringWindowToDisplayBehind(window, blocking_modal); // Still bring to right below modal. + BringWindowToDisplayBehind(window, blocking_modal); // Still bring right under modal. (FIXME: Could move in focus list too?) + ClosePopupsOverWindow(GetTopMostPopupModal(), false); // Note how we need to use GetTopMostPopupModal() aad NOT blocking_modal, to handle nested modals return; } @@ -7367,7 +7536,7 @@ void ImGui::BeginDisabled(bool disabled) } if (was_disabled || disabled) g.CurrentItemFlags |= ImGuiItemFlags_Disabled; - g.ItemFlagsStack.push_back(g.CurrentItemFlags); + g.ItemFlagsStack.push_back(g.CurrentItemFlags); // FIXME-OPT: can we simply skip this and use DisabledStackSize? g.DisabledStackSize++; } @@ -7384,6 +7553,29 @@ void ImGui::EndDisabled() g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar(); } +// Could have been called BeginDisabledDisable() but it didn't want to be award nominated for most awkward function name. +// Ideally we would use a shared e.g. BeginDisabled()->BeginDisabledEx() but earlier needs to be optimal. +// The whole code for this is awkward, will reevaluate if we find a way to implement SetNextItemDisabled(). +void ImGui::BeginDisabledOverrideReenable() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.CurrentItemFlags & ImGuiItemFlags_Disabled); + g.Style.Alpha = g.DisabledAlphaBackup; + g.CurrentItemFlags &= ~ImGuiItemFlags_Disabled; + g.ItemFlagsStack.push_back(g.CurrentItemFlags); + g.DisabledStackSize++; +} + +void ImGui::EndDisabledOverrideReenable() +{ + ImGuiContext& g = *GImGui; + g.DisabledStackSize--; + IM_ASSERT(g.DisabledStackSize > 0); + g.ItemFlagsStack.pop_back(); + g.CurrentItemFlags = g.ItemFlagsStack.back(); + g.Style.Alpha = g.DisabledAlphaBackup * g.Style.DisabledAlpha; +} + void ImGui::PushTabStop(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); @@ -7518,7 +7710,7 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) // When changing hovered window we requires a bit of stationary delay before activating hover timer. // FIXME: We don't support delay other than stationary one for now, other delay would need a way - // to fullfill the possibility that multiple IsWindowHovered() with varying flag could return true + // to fulfill the possibility that multiple IsWindowHovered() with varying flag could return true // for different windows of the hierarchy. Possibly need a Hash(Current+Flags) ==> (Timer) cache. // We can implement this for _Stationary because the data is linked to HoveredWindow rather than CurrentWindow. if (flags & ImGuiHoveredFlags_ForTooltip) @@ -7794,6 +7986,14 @@ void ImGui::SetNextWindowBgAlpha(float alpha) g.NextWindowData.BgAlphaVal = alpha; } +// This is experimental and meant to be a toy for exploring a future/wider range of features. +void ImGui::SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags) +{ + ImGuiContext& g = *GImGui; + g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasRefreshPolicy; + g.NextWindowData.RefreshFlagsVal = flags; +} + ImDrawList* ImGui::GetWindowDrawList() { ImGuiWindow* window = GetCurrentWindow(); @@ -7964,6 +8164,69 @@ ImGuiStorage* ImGui::GetStateStorage() return window->DC.StateStorage; } +bool ImGui::IsRectVisible(const ImVec2& size) +{ + ImGuiWindow* window = GImGui->CurrentWindow; + return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size)); +} + +bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) +{ + ImGuiWindow* window = GImGui->CurrentWindow; + return window->ClipRect.Overlaps(ImRect(rect_min, rect_max)); +} + +//----------------------------------------------------------------------------- +// [SECTION] ID STACK +//----------------------------------------------------------------------------- + +// This is one of the very rare legacy case where we use ImGuiWindow methods, +// it should ideally be flattened at some point but it's been used a lots by widgets. +ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) +{ + ImGuiID seed = IDStack.back(); + ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + ImGuiContext& g = *Ctx; + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); +#endif + return id; +} + +ImGuiID ImGuiWindow::GetID(const void* ptr) +{ + ImGuiID seed = IDStack.back(); + ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + ImGuiContext& g = *Ctx; + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL); +#endif + return id; +} + +ImGuiID ImGuiWindow::GetID(int n) +{ + ImGuiID seed = IDStack.back(); + ImGuiID id = ImHashData(&n, sizeof(n), seed); +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + ImGuiContext& g = *Ctx; + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL); +#endif + return id; +} + +// This is only used in rare/specific situations to manufacture an ID out of nowhere. +ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) +{ + ImGuiID seed = IDStack.back(); + ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs); + ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed); + return id; +} + void ImGui::PushID(const char* str_id) { ImGuiContext& g = *GImGui; @@ -8001,8 +8264,10 @@ void ImGui::PushOverrideID(ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; +#ifndef IMGUI_DISABLE_DEBUG_TOOLS if (g.DebugHookIdInfo == id) DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL); +#endif window->IDStack.push_back(id); } @@ -8012,18 +8277,22 @@ void ImGui::PushOverrideID(ImGuiID id) ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed) { ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); +#ifndef IMGUI_DISABLE_DEBUG_TOOLS ImGuiContext& g = *GImGui; if (g.DebugHookIdInfo == id) DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); +#endif return id; } ImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed) { ImGuiID id = ImHashData(&n, sizeof(n), seed); +#ifndef IMGUI_DISABLE_DEBUG_TOOLS ImGuiContext& g = *GImGui; if (g.DebugHookIdInfo == id) DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL); +#endif return id; } @@ -8052,22 +8321,11 @@ ImGuiID ImGui::GetID(const void* ptr_id) return window->GetID(ptr_id); } -bool ImGui::IsRectVisible(const ImVec2& size) -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size)); -} - -bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->ClipRect.Overlaps(ImRect(rect_min, rect_max)); -} - - //----------------------------------------------------------------------------- // [SECTION] INPUTS //----------------------------------------------------------------------------- +// - GetModForModKey() [Internal] +// - FixupKeyChord() [Internal] // - GetKeyData() [Internal] // - GetKeyIndex() [Internal] // - GetKeyName() @@ -8129,23 +8387,25 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) // - Shortcut() [Internal] //----------------------------------------------------------------------------- -ImGuiKeyChord ImGui::FixupKeyChord(ImGuiContext* ctx, ImGuiKeyChord key_chord) +static ImGuiKeyChord GetModForModKey(ImGuiKey key) +{ + if (key == ImGuiKey_LeftCtrl || key == ImGuiKey_RightCtrl) + return ImGuiMod_Ctrl; + if (key == ImGuiKey_LeftShift || key == ImGuiKey_RightShift) + return ImGuiMod_Shift; + if (key == ImGuiKey_LeftAlt || key == ImGuiKey_RightAlt) + return ImGuiMod_Alt; + if (key == ImGuiKey_LeftSuper || key == ImGuiKey_RightSuper) + return ImGuiMod_Super; + return ImGuiMod_None; +} + +ImGuiKeyChord ImGui::FixupKeyChord(ImGuiKeyChord key_chord) { - // Convert ImGuiMod_Shortcut and add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified. + // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified. ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); if (IsModKey(key)) - { - if (key == ImGuiKey_LeftCtrl || key == ImGuiKey_RightCtrl) - key_chord |= ImGuiMod_Ctrl; - if (key == ImGuiKey_LeftShift || key == ImGuiKey_RightShift) - key_chord |= ImGuiMod_Shift; - if (key == ImGuiKey_LeftAlt || key == ImGuiKey_RightAlt) - key_chord |= ImGuiMod_Alt; - if (key == ImGuiKey_LeftSuper || key == ImGuiKey_RightSuper) - key_chord |= ImGuiMod_Super; - } - if (key_chord & ImGuiMod_Shortcut) - return (key_chord & ~ImGuiMod_Shortcut) | (ctx->IO.ConfigMacOSXBehaviors ? ImGuiMod_Super : ImGuiMod_Ctrl); + key_chord |= GetModForModKey(key); return key_chord; } @@ -8155,7 +8415,7 @@ ImGuiKeyData* ImGui::GetKeyData(ImGuiContext* ctx, ImGuiKey key) // Special storage location for mods if (key & ImGuiMod_Mask_) - key = ConvertSingleModFlagToKey(ctx, key); + key = ConvertSingleModFlagToKey(key); #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO IM_ASSERT(key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_NamedKey_END); @@ -8168,6 +8428,7 @@ ImGuiKeyData* ImGui::GetKeyData(ImGuiContext* ctx, ImGuiKey key) } #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO +// Formally moved to obsolete section in 1.90.5 in spite of documented as obsolete since 1.87 ImGuiKey ImGui::GetKeyIndex(ImGuiKey key) { ImGuiContext& g = *GImGui; @@ -8206,10 +8467,12 @@ IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames)); const char* ImGui::GetKeyName(ImGuiKey key) { - ImGuiContext& g = *GImGui; + if (key == ImGuiKey_None) + return "None"; #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO - IM_ASSERT((IsNamedKeyOrModKey(key) || key == ImGuiKey_None) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code."); + IM_ASSERT(IsNamedKeyOrMod(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code."); #else + ImGuiContext& g = *GImGui; if (IsLegacyKey(key)) { if (g.IO.KeyMap[key] == -1) @@ -8218,27 +8481,33 @@ const char* ImGui::GetKeyName(ImGuiKey key) key = (ImGuiKey)g.IO.KeyMap[key]; } #endif - if (key == ImGuiKey_None) - return "None"; if (key & ImGuiMod_Mask_) - key = ConvertSingleModFlagToKey(&g, key); + key = ConvertSingleModFlagToKey(key); if (!IsNamedKey(key)) return "Unknown"; return GKeyNames[key - ImGuiKey_NamedKey_BEGIN]; } -// ImGuiMod_Shortcut is translated to either Ctrl or Super. +// Return untranslated names: on macOS, Cmd key will show as Ctrl, Ctrl key will show as super. +// Lifetime of return value: valid until next call to same function. const char* ImGui::GetKeyChordName(ImGuiKeyChord key_chord) { ImGuiContext& g = *GImGui; - key_chord = FixupKeyChord(&g, key_chord); + + const ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); + if (IsModKey(key)) + key_chord &= ~GetModForModKey(key); // Return "Ctrl+LeftShift" instead of "Ctrl+Shift+LeftShift" ImFormatString(g.TempKeychordName, IM_ARRAYSIZE(g.TempKeychordName), "%s%s%s%s%s", (key_chord & ImGuiMod_Ctrl) ? "Ctrl+" : "", (key_chord & ImGuiMod_Shift) ? "Shift+" : "", (key_chord & ImGuiMod_Alt) ? "Alt+" : "", - (key_chord & ImGuiMod_Super) ? (g.IO.ConfigMacOSXBehaviors ? "Cmd+" : "Super+") : "", - GetKeyName((ImGuiKey)(key_chord & ~ImGuiMod_Mask_))); + (key_chord & ImGuiMod_Super) ? "Super+" : "", + (key != ImGuiKey_None || key_chord == ImGuiKey_None) ? GetKeyName(key) : ""); + size_t len; + if (key == ImGuiKey_None && key_chord != 0) + if ((len = strlen(g.TempKeychordName)) != 0) // Remove trailing '+' + g.TempKeychordName[len - 1] = 0; return g.TempKeychordName; } @@ -8308,9 +8577,9 @@ static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt) routing_entry = &rt->Entries[old_routing_idx]; routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore; routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry - routing_entry->RoutingNext = ImGuiKeyOwner_None; + routing_entry->RoutingNext = ImGuiKeyOwner_NoOwner; routing_entry->RoutingNextScore = 255; - if (routing_entry->RoutingCurr == ImGuiKeyOwner_None) + if (routing_entry->RoutingCurr == ImGuiKeyOwner_NoOwner) continue; rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer @@ -8319,7 +8588,7 @@ static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt) if (routing_entry->Mods == g.IO.KeyMods) { ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key); - if (owner_data->OwnerCurr == ImGuiKeyOwner_None) + if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner) { owner_data->OwnerCurr = routing_entry->RoutingCurr; //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X) via Routing\n", GetKeyName(key), routing_entry->RoutingCurr); @@ -8339,7 +8608,7 @@ static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt) static inline ImGuiID GetRoutingIdFromOwnerId(ImGuiID owner_id) { ImGuiContext& g = *GImGui; - return (owner_id != ImGuiKeyOwner_None && owner_id != ImGuiKeyOwner_Any) ? owner_id : g.CurrentFocusScopeId; + return (owner_id != ImGuiKeyOwner_NoOwner && owner_id != ImGuiKeyOwner_Any) ? owner_id : g.CurrentFocusScopeId; } ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord) @@ -8356,8 +8625,8 @@ ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord) ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_); if (key == ImGuiKey_None) - key = ConvertSingleModFlagToKey(&g, mods); - IM_ASSERT(IsNamedKey(key) && (key_chord & ImGuiMod_Shortcut) == 0); // Please call ConvertShortcutMod() in calling function. + key = ConvertSingleModFlagToKey(mods); + IM_ASSERT(IsNamedKey(key)); // Get (in the majority of case, the linked list will have one element so this should be 2 reads. // Subsequent elements will be contiguous in memory as list is sorted/rebuilt in NewFrame). @@ -8379,19 +8648,18 @@ ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord) } // Current score encoding (lower is highest priority): -// - 0: ImGuiInputFlags_RouteGlobalHigh -// - 1: ImGuiInputFlags_RouteFocused (if item active) -// - 2: ImGuiInputFlags_RouteGlobal +// - 0: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive +// - 1: ImGuiInputFlags_ActiveItem or ImGuiInputFlags_RouteFocused (if item active) +// - 2: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused // - 3+: ImGuiInputFlags_RouteFocused (if window in focus-stack) -// - 254: ImGuiInputFlags_RouteGlobalLow +// - 254: ImGuiInputFlags_RouteGlobal // - 255: never route // 'flags' should include an explicit routing policy static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags) { + ImGuiContext& g = *GImGui; if (flags & ImGuiInputFlags_RouteFocused) { - ImGuiContext& g = *GImGui; - // ActiveID gets top priority // (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it) if (owner_id != 0 && g.ActiveId == owner_id) @@ -8409,15 +8677,23 @@ static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInput for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++) if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id) return 3 + index_in_focus_path; - return 255; } - - // ImGuiInputFlags_RouteGlobalHigh is default, so calls without flags are not conditional - if (flags & ImGuiInputFlags_RouteGlobal) - return 2; - if (flags & ImGuiInputFlags_RouteGlobalLow) + else if (flags & ImGuiInputFlags_RouteActive) + { + if (owner_id != 0 && g.ActiveId == owner_id) + return 1; + return 255; + } + else if (flags & ImGuiInputFlags_RouteGlobal) + { + if (flags & ImGuiInputFlags_RouteOverActive) + return 0; + if (flags & ImGuiInputFlags_RouteOverFocused) + return 2; return 254; + } + IM_ASSERT(0); return 0; } @@ -8430,7 +8706,7 @@ static bool IsKeyChordPotentiallyCharInput(ImGuiKeyChord key_chord) // When the right mods are pressed it cannot be a char input so we won't filter the shortcut out. ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_); - const bool ignore_char_inputs = ((mods & ImGuiMod_Ctrl) && !(mods & ImGuiMod_Alt)) || (g.IO.ConfigMacOSXBehaviors && (mods & ImGuiMod_Super)); + const bool ignore_char_inputs = ((mods & ImGuiMod_Ctrl) && !(mods & ImGuiMod_Alt)) || (g.IO.ConfigMacOSXBehaviors && (mods & ImGuiMod_Ctrl)); if (ignore_char_inputs) return false; @@ -8444,17 +8720,19 @@ static bool IsKeyChordPotentiallyCharInput(ImGuiKeyChord key_chord) // - Routes and key ownership are attributed at the beginning of next frame based on best score and mod state. // (Conceptually this does a "Submit for next frame" + "Test for current frame". // As such, it could be called TrySetXXX or SubmitXXX, or the Submit and Test operations should be separate.) -bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags) +bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id) { ImGuiContext& g = *GImGui; - if ((flags & ImGuiInputFlags_RouteMask_) == 0) - flags |= ImGuiInputFlags_RouteGlobalHigh; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut() + if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0) + flags |= ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut() else - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteMask_)); // Check that only 1 routing flag is used - IM_ASSERT(owner_id != ImGuiKeyOwner_Any && owner_id != ImGuiKeyOwner_None); + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteTypeMask_)); // Check that only 1 routing flag is used + IM_ASSERT(owner_id != ImGuiKeyOwner_Any && owner_id != ImGuiKeyOwner_NoOwner); + if (flags & (ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused)) + IM_ASSERT(flags & ImGuiInputFlags_RouteGlobal); - // Convert ImGuiMod_Shortcut and add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified. - key_chord = FixupKeyChord(&g, key_chord); + // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified. + key_chord = FixupKeyChord(key_chord); // [DEBUG] Debug break requested by user if (g.DebugBreakInShortcutRouting == key_chord) @@ -8467,38 +8745,45 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiI // Note how ImGuiInputFlags_RouteAlways won't set routing and thus won't set owner. May want to rework this? if (flags & ImGuiInputFlags_RouteAlways) { - IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, owner_id=0x%08X, flags=%04X) -> always\n", GetKeyChordName(key_chord), owner_id, flags); + IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> always, no register\n", GetKeyChordName(key_chord), flags, owner_id); return true; } - // Specific culling when there's an active. + // Specific culling when there's an active item. if (g.ActiveId != 0 && g.ActiveId != owner_id) { + if (flags & ImGuiInputFlags_RouteActive) + return false; + // Cull shortcuts with no modifiers when it could generate a character. // e.g. Shortcut(ImGuiKey_G) also generates 'g' character, should not trigger when InputText() is active. // but Shortcut(Ctrl+G) should generally trigger when InputText() is active. // TL;DR: lettered shortcut with no mods or with only Alt mod will not trigger while an item reading text input is active. // (We cannot filter based on io.InputQueueCharacters[] contents because of trickling and key<>chars submission order are undefined) - if ((flags & ImGuiInputFlags_RouteFocused) && g.IO.WantTextInput && IsKeyChordPotentiallyCharInput(key_chord)) + if (g.IO.WantTextInput && IsKeyChordPotentiallyCharInput(key_chord)) { - IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, owner_id=0x%08X, flags=%04X) -> filtered as potential char input\n", GetKeyChordName(key_chord), owner_id, flags); + IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> filtered as potential char input\n", GetKeyChordName(key_chord), flags, owner_id); return false; } // ActiveIdUsingAllKeyboardKeys trumps all for ActiveId - if ((flags & ImGuiInputFlags_RouteGlobalHigh) == 0 && g.ActiveIdUsingAllKeyboardKeys) + if ((flags & ImGuiInputFlags_RouteOverActive) == 0 && g.ActiveIdUsingAllKeyboardKeys) { ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); if (key == ImGuiKey_None) - key = ConvertSingleModFlagToKey(&g, (ImGuiKey)(key_chord & ImGuiMod_Mask_)); + key = ConvertSingleModFlagToKey((ImGuiKey)(key_chord & ImGuiMod_Mask_)); if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END) return false; } } - // FIXME-SHORTCUT: A way to configure the location/focus-scope to test would render this more flexible. - const int score = CalcRoutingScore(g.CurrentFocusScopeId, owner_id, flags); - IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, owner_id=0x%08X, flags=%04X) -> score %d\n", GetKeyChordName(key_chord), owner_id, flags, score); + // Where do we evaluate route for? + ImGuiID focus_scope_id = g.CurrentFocusScopeId; + if (flags & ImGuiInputFlags_RouteFromRootWindow) + focus_scope_id = g.CurrentWindow->RootWindow->ID; // See PushFocusScope() call in Begin() + + const int score = CalcRoutingScore(focus_scope_id, owner_id, flags); + IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> score %d\n", GetKeyChordName(key_chord), flags, owner_id, score); if (score == 255) return false; @@ -8522,9 +8807,8 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiI // Note: this cannot be turned into GetShortcutRouting() because we do the owner_id->routing_id translation, name would be more misleading. bool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id) { - ImGuiContext& g = *GImGui; const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id); - key_chord = FixupKeyChord(&g, key_chord); + key_chord = FixupKeyChord(key_chord); ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); // FIXME: Could avoid creating entry. return routing_data->RoutingCurr == routing_id; } @@ -8548,11 +8832,11 @@ bool ImGui::IsKeyDown(ImGuiKey key, ImGuiID owner_id) bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat) { - return IsKeyPressed(key, ImGuiKeyOwner_Any, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None); + return IsKeyPressed(key, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any); } // Important: unless legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat. -bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags) +bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id) { const ImGuiKeyData* key_data = GetKeyData(key); if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership) @@ -8622,10 +8906,10 @@ bool ImGui::IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id) bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat) { - return IsMouseClicked(button, ImGuiKeyOwner_Any, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None); + return IsMouseClicked(button, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any); } -bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags) +bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); @@ -8951,7 +9235,7 @@ static void ImGui::UpdateKeyboardInputs() ImGuiKeyOwnerData* owner_data = &g.KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; owner_data->OwnerCurr = owner_data->OwnerNext; if (!key_data->Down) // Important: ownership is released on the frame after a release. Ensure a 'MouseDown -> CloseWindow -> MouseUp' chain doesn't lead to someone else seeing the MouseUp. - owner_data->OwnerNext = ImGuiKeyOwner_None; + owner_data->OwnerNext = ImGuiKeyOwner_NoOwner; owner_data->LockThisFrame = owner_data->LockUntilRelease = owner_data->LockUntilRelease && key_data->Down; // Clear LockUntilRelease when key is not Down anymore } @@ -9107,8 +9391,8 @@ void ImGui::UpdateMouseWheel() } ImVec2 wheel; - wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_None) ? g.IO.MouseWheelH : 0.0f; - wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_None) ? g.IO.MouseWheel : 0.0f; + wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheelH : 0.0f; + wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheel : 0.0f; //IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y); ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow; @@ -9195,7 +9479,7 @@ void ImGui::SetNextFrameWantCaptureMouse(bool want_capture_mouse) #ifndef IMGUI_DISABLE_DEBUG_TOOLS static const char* GetInputSourceName(ImGuiInputSource source) { - const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad", "Clipboard" }; + const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT && source >= 0 && source < ImGuiInputSource_COUNT); return input_source_names[source]; } @@ -9346,8 +9630,8 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) ImGuiID ImGui::GetKeyOwner(ImGuiKey key) { - if (!IsNamedKeyOrModKey(key)) - return ImGuiKeyOwner_None; + if (!IsNamedKeyOrMod(key)) + return ImGuiKeyOwner_NoOwner; ImGuiContext& g = *GImGui; ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key); @@ -9355,7 +9639,7 @@ ImGuiID ImGui::GetKeyOwner(ImGuiKey key) if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any) if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END) - return ImGuiKeyOwner_None; + return ImGuiKeyOwner_NoOwner; return owner_id; } @@ -9366,7 +9650,7 @@ ImGuiID ImGui::GetKeyOwner(ImGuiKey key) // All paths are also testing for key not being locked, for the rare cases that key have been locked with using ImGuiInputFlags_LockXXX flags. bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id) { - if (!IsNamedKeyOrModKey(key)) + if (!IsNamedKeyOrMod(key)) return true; ImGuiContext& g = *GImGui; @@ -9385,7 +9669,7 @@ bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id) { if (owner_data->LockThisFrame) return false; - if (owner_data->OwnerCurr != ImGuiKeyOwner_None) + if (owner_data->OwnerCurr != ImGuiKeyOwner_NoOwner) return false; } @@ -9400,7 +9684,7 @@ bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id) void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags) { ImGuiContext& g = *GImGui; - IM_ASSERT(IsNamedKeyOrModKey(key) && (owner_id != ImGuiKeyOwner_Any || (flags & (ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease)))); // Can only use _Any with _LockXXX flags (to eat a key away without an ID to retrieve it) + IM_ASSERT(IsNamedKeyOrMod(key) && (owner_id != ImGuiKeyOwner_Any || (flags & (ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease)))); // Can only use _Any with _LockXXX flags (to eat a key away without an ID to retrieve it) IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetKeyOwner) == 0); // Passing flags not supported by this function! //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X, flags=%08X)\n", GetKeyName(key), owner_id, flags); @@ -9420,7 +9704,6 @@ void ImGui::SetKeyOwnersForKeyChord(ImGuiKeyChord key_chord, ImGuiID owner_id, I if (key_chord & ImGuiMod_Shift) { SetKeyOwner(ImGuiMod_Shift, owner_id, flags); } if (key_chord & ImGuiMod_Alt) { SetKeyOwner(ImGuiMod_Alt, owner_id, flags); } if (key_chord & ImGuiMod_Super) { SetKeyOwner(ImGuiMod_Super, owner_id, flags); } - if (key_chord & ImGuiMod_Shortcut) { SetKeyOwner(ImGuiMod_Shortcut, owner_id, flags); } if (key_chord & ~ImGuiMod_Mask_) { SetKeyOwner((ImGuiKey)(key_chord & ~ImGuiMod_Mask_), owner_id, flags); } } @@ -9448,14 +9731,14 @@ void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags) // This is the only public API until we expose owner_id versions of the API as replacements. bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord) { - return IsKeyChordPressed(key_chord, 0, ImGuiInputFlags_None); + return IsKeyChordPressed(key_chord, ImGuiInputFlags_None, ImGuiKeyOwner_Any); } // This is equivalent to comparing KeyMods + doing a IsKeyPressed() -bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags) +bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id) { ImGuiContext& g = *GImGui; - key_chord = FixupKeyChord(&g, key_chord); + key_chord = FixupKeyChord(key_chord); ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_); if (g.IO.KeyMods != mods) return false; @@ -9463,35 +9746,63 @@ bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiIn // Special storage location for mods ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); if (key == ImGuiKey_None) - key = ConvertSingleModFlagToKey(&g, mods); - if (!IsKeyPressed(key, owner_id, (flags & ImGuiInputFlags_RepeatMask_))) + key = ConvertSingleModFlagToKey(mods); + if (!IsKeyPressed(key, (flags & ImGuiInputFlags_RepeatMask_), owner_id)) return false; return true; } -void ImGui::SetNextItemShortcut(ImGuiKeyChord key_chord) +void ImGui::SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags) { ImGuiContext& g = *GImGui; g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasShortcut; g.NextItemData.Shortcut = key_chord; + g.NextItemData.ShortcutFlags = flags; } -bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags) +void ImGui::ItemHandleShortcut(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + ImGuiInputFlags flags = g.NextItemData.ShortcutFlags; + IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetNextItemShortcut) == 0); // Passing flags not supported by SetNextItemShortcut()! + + if (flags & ImGuiInputFlags_Tooltip) + { + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasShortcut; + g.LastItemData.Shortcut = g.NextItemData.Shortcut; + } + if (!Shortcut(g.NextItemData.Shortcut, flags & ImGuiInputFlags_SupportedByShortcut, id) || g.NavActivateId != 0) + return; + + // FIXME: Generalize Activation queue? + g.NavActivateId = id; // Will effectively disable clipping. + g.NavActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_FromShortcut; + //if (g.ActiveId == 0 || g.ActiveId == id) + g.NavActivateDownId = g.NavActivatePressedId = id; + NavHighlightActivated(id); +} + +bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags) +{ + return Shortcut(key_chord, flags, ImGuiKeyOwner_Any); +} + +bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id) { //ImGuiContext& g = *GImGui; - //IMGUI_DEBUG_LOG("Shortcut(%s, owner_id=0x%08X, flags=%X)\n", GetKeyChordName(key_chord, g.TempBuffer.Data, g.TempBuffer.Size), owner_id, flags); + //IMGUI_DEBUG_LOG("Shortcut(%s, flags=%X, owner_id=0x%08X)\n", GetKeyChordName(key_chord, g.TempBuffer.Data, g.TempBuffer.Size), flags, owner_id); // When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter with this, so IsKeyPressed() is fine with he 0/Any. - if ((flags & ImGuiInputFlags_RouteMask_) == 0) + if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0) flags |= ImGuiInputFlags_RouteFocused; // Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default) // Effectively makes Shortcut() always input-owner aware. - if (owner_id == ImGuiKeyOwner_Any || owner_id == ImGuiKeyOwner_None) + if (owner_id == ImGuiKeyOwner_Any || owner_id == ImGuiKeyOwner_NoOwner) owner_id = GetRoutingIdFromOwnerId(owner_id); // Submit route - if (!SetShortcutRouting(key_chord, owner_id, flags)) + if (!SetShortcutRouting(key_chord, flags, owner_id)) return false; // Default repeat behavior for Shortcut() @@ -9499,8 +9810,12 @@ bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags if ((flags & ImGuiInputFlags_Repeat) != 0 && (flags & ImGuiInputFlags_RepeatUntilMask_) == 0) flags |= ImGuiInputFlags_RepeatUntilKeyModsChange; - if (!IsKeyChordPressed(key_chord, owner_id, flags)) + if (!IsKeyChordPressed(key_chord, flags, owner_id)) return false; + + // Claim mods during the press + SetKeyOwnersForKeyChord(key_chord & ImGuiMod_Mask_, owner_id); + IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByShortcut) == 0); // Passing flags not supported by this function! return true; } @@ -9510,24 +9825,26 @@ bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags // [SECTION] ERROR CHECKING //----------------------------------------------------------------------------- -// Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui. +// Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues. +// Called by IMGUI_CHECKVERSION(). // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit -// If this triggers you have an issue: -// - Most commonly: mismatched headers and compiled code version. -// - Or: mismatched configuration #define, compilation settings, packing pragma etc. -// The configuration settings mentioned in imconfig.h must be set for all compilation units involved with Dear ImGui, -// which is way it is required you put them in your imconfig file (and not just before including imgui.h). -// Otherwise it is possible that different compilation units would see different structure layout +// If this triggers you have mismatched headers and compiled code versions. +// - It could be because of a build issue (using new headers with old compiled code) +// - It could be because of mismatched configuration #define, compilation settings, packing pragma etc. +// THE CONFIGURATION SETTINGS MENTIONED IN imconfig.h MUST BE SET FOR ALL COMPILATION UNITS INVOLVED WITH DEAR IMGUI. +// Which is why it is required you put them in your imconfig file (and NOT only before including imgui.h). +// Otherwise it is possible that different compilation units would see different structure layout. +// If you don't want to modify imconfig.h you can use the IMGUI_USER_CONFIG define to change filename. bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx) { bool error = false; if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); } - if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); } + if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); } if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); } - if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); } - if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); } - if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); } - if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); } + if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); } + if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); } + if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); } + if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); } return !error; } @@ -9714,7 +10031,13 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) //-V1044 { if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name); - EndDisabled(); + if (g.CurrentItemFlags & ImGuiItemFlags_Disabled) + EndDisabled(); + else + { + EndDisabledOverrideReenable(); + g.CurrentWindowStack.back().DisabledOverrideReenable = false; + } } while (g.ColorStack.Size > stack_sizes->SizeOfColorStack) { @@ -9786,7 +10109,6 @@ void ImGuiStackSizes::CompareWithContextState(ImGuiContext* ctx) // [SECTION] ITEM SUBMISSION //----------------------------------------------------------------------------- // - KeepAliveID() -// - ItemHandleShortcut() [Internal] // - ItemAdd() //----------------------------------------------------------------------------- @@ -9800,24 +10122,11 @@ void ImGui::KeepAliveID(ImGuiID id) g.ActiveIdPreviousFrameIsAlive = true; } -static void ItemHandleShortcut(ImGuiID id) -{ - // FIXME: Generalize Activation queue? - ImGuiContext& g = *GImGui; - if (ImGui::Shortcut(g.NextItemData.Shortcut, id, ImGuiInputFlags_None) && g.NavActivateId == 0) - { - g.NavActivateId = id; // Will effectively disable clipping. - g.NavActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_FromShortcut; - if (g.ActiveId == 0 || g.ActiveId == id) - g.NavActivateDownId = g.NavActivatePressedId = id; - ImGui::NavHighlightActivated(id); - } -} - // 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. // THIS IS IN THE PERFORMANCE CRITICAL PATH (UNTIL THE CLIPPING TEST AND EARLY-RETURN) +IM_MSVC_RUNTIME_CHECKS_OFF bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags) { ImGuiContext& g = *GImGui; @@ -9870,15 +10179,12 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu #endif // Clipping test - // (this is a modified copy of IsClippedEx() so we can reuse the is_rect_visible value) - //const bool is_clipped = IsClippedEx(bb, id); - //if (is_clipped) - // return false; + // (this is an inline copy of IsClippedEx() so we can reuse the is_rect_visible value, otherwise we'd do 'if (IsClippedEx(bb, id)) return false') // g.NavActivateId is not necessarily == g.NavId, in the case of remote activation (e.g. shortcuts) const bool is_rect_visible = bb.Overlaps(window->ClipRect); if (!is_rect_visible) if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId)) - if (!g.LogEnabled) + if (!g.ItemUnclipByLog) return false; // [DEBUG] @@ -9905,7 +10211,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; return true; } - +IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] LAYOUT @@ -9942,6 +10248,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu // Register minimum needed size so it can extend the bounding box used for auto-fit calculation. // See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different. // THIS IS IN THE PERFORMANCE CRITICAL PATH. +IM_MSVC_RUNTIME_CHECKS_OFF void ImGui::ItemSize(const ImVec2& size, float text_baseline_y) { ImGuiContext& g = *GImGui; @@ -9977,6 +10284,7 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y) if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) SameLine(); } +IM_MSVC_RUNTIME_CHECKS_RESTORE // Gets back to previous line and continue with horizontal layout // offset_from_start_x == 0 : follow right after previous item @@ -10774,7 +11082,7 @@ void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. popup_ref.PopupId = id; popup_ref.Window = NULL; - popup_ref.BackupNavWindow = g.NavWindow; // When popup closes focus may be restored to NavWindow (depend on window type). + popup_ref.RestoreNavWindow = g.NavWindow; // When popup closes focus may be restored to NavWindow (depend on window type). popup_ref.OpenFrameCount = g.FrameCount; popup_ref.OpenParentId = parent_window->IDStack.back(); popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); @@ -10823,6 +11131,7 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to return; // Don't close our own child popup windows. + //IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\") restore_under=%d\n", ref_window ? ref_window->Name : "", restore_focus_to_window_under_popup); int popup_count_to_keep = 0; if (ref_window) { @@ -10879,18 +11188,19 @@ void ImGui::ClosePopupsExceptModals() void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup) { ImGuiContext& g = *GImGui; - IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\n", remaining, restore_focus_to_window_under_popup); + IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupToLevel(%d), restore_under=%d\n", remaining, restore_focus_to_window_under_popup); IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size); // Trim open popup stack - ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window; - ImGuiWindow* popup_backup_nav_window = g.OpenPopupStack[remaining].BackupNavWindow; + ImGuiPopupData prev_popup = g.OpenPopupStack[remaining]; g.OpenPopupStack.resize(remaining); - if (restore_focus_to_window_under_popup) + // Restore focus (unless popup window was not yet submitted, and didn't have a chance to take focus anyhow. See #7325 for an edge case) + if (restore_focus_to_window_under_popup && prev_popup.Window) { - ImGuiWindow* focus_window = (popup_window && popup_window->Flags & ImGuiWindowFlags_ChildMenu) ? popup_window->ParentWindow : popup_backup_nav_window; - if (focus_window && !focus_window->WasActive && popup_window) + ImGuiWindow* popup_window = prev_popup.Window; + ImGuiWindow* focus_window = (popup_window->Flags & ImGuiWindowFlags_ChildMenu) ? popup_window->ParentWindow : prev_popup.RestoreNavWindow; + if (focus_window && !focus_window->WasActive) FocusTopMostWindowUnderOne(popup_window, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild); // Fallback else FocusWindow(focus_window, (g.NavLayer == ImGuiNavLayer_Main) ? ImGuiFocusRequestFlags_RestoreFocusedChild : ImGuiFocusRequestFlags_None); @@ -11812,7 +12122,10 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.NavWindow; - if (g.NavDisableHighlight || !g.NavDisableMouseHover || !window) + const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID; + + // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag. + if ((g.NavDisableHighlight || !g.NavDisableMouseHover || !window) && !activated_shortcut) { // Mouse (we need a fallback in case the mouse becomes invalid after being used) // The +1.0f offset when stored by OpenPopupEx() allows reopening this or another popup (same or another mouse button) while not moving the mouse, it is pretty standard. @@ -11823,14 +12136,19 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() else { // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item + ImRect ref_rect; + if (activated_shortcut) + ref_rect = g.LastItemData.NavRect; + else + ref_rect = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]); + // Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?) - ImRect rect_rel = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]); if (window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX)) { ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); - rect_rel.Translate(window->Scroll - next_scroll); + ref_rect.Translate(window->Scroll - next_scroll); } - ImVec2 pos = ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); + ImVec2 pos = ImVec2(ref_rect.Min.x + ImMin(g.Style.FramePadding.x * 4, ref_rect.GetWidth()), ref_rect.Max.y - ImMin(g.Style.FramePadding.y, ref_rect.GetHeight())); ImGuiViewport* viewport = GetMainViewport(); return ImTrunc(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImTrunc() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. } @@ -11925,10 +12243,10 @@ static void ImGui::NavUpdate() g.NavActivateFlags = ImGuiActivateFlags_None; if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { - const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space, ImGuiKeyOwner_None)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_None)); - const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, ImGuiKeyOwner_None)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_None))); - const bool input_down = (nav_keyboard_active && (IsKeyDown(ImGuiKey_Enter, ImGuiKeyOwner_None) || IsKeyDown(ImGuiKey_KeypadEnter, ImGuiKeyOwner_None))) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadInput, ImGuiKeyOwner_None)); - const bool input_pressed = input_down && ((nav_keyboard_active && (IsKeyPressed(ImGuiKey_Enter, ImGuiKeyOwner_None) || IsKeyPressed(ImGuiKey_KeypadEnter, ImGuiKeyOwner_None))) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadInput, ImGuiKeyOwner_None))); + const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_NoOwner)); + const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, 0, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, 0, ImGuiKeyOwner_NoOwner))); + const bool input_down = (nav_keyboard_active && (IsKeyDown(ImGuiKey_Enter, ImGuiKeyOwner_NoOwner) || IsKeyDown(ImGuiKey_KeypadEnter, ImGuiKeyOwner_NoOwner))) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadInput, ImGuiKeyOwner_NoOwner)); + const bool input_pressed = input_down && ((nav_keyboard_active && (IsKeyPressed(ImGuiKey_Enter, 0, ImGuiKeyOwner_NoOwner) || IsKeyPressed(ImGuiKey_KeypadEnter, 0, ImGuiKeyOwner_NoOwner))) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadInput, 0, ImGuiKeyOwner_NoOwner))); if (g.ActiveId == 0 && activate_pressed) { g.NavActivateId = g.NavId; @@ -12101,11 +12419,11 @@ void ImGui::NavUpdateCreateMoveRequest() g.NavMoveScrollFlags = ImGuiScrollFlags_None; if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs)) { - const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateNavMove; - if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadLeft, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_LeftArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Left; } - if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadRight, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_RightArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Right; } - if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadUp, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_UpArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Up; } - if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadDown, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_DownArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Down; } + const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateNavMove; + if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadLeft, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_LeftArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Left; } + if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadRight, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_RightArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Right; } + if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadUp, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_UpArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Up; } + if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadDown, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_DownArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Down; } } g.NavMoveClipDir = g.NavMoveDir; g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); @@ -12201,7 +12519,7 @@ void ImGui::NavUpdateCreateTabbingRequest() if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs)) return; - const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat) && !g.IO.KeyCtrl && !g.IO.KeyAlt; + const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner) && !g.IO.KeyCtrl && !g.IO.KeyAlt; if (!tab_pressed) return; @@ -12282,8 +12600,10 @@ void ImGui::NavMoveRequestApplyResult() g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid; } - // FIXME: Could become optional e.g. ImGuiNavMoveFlags_NoClearActiveId if we later want to apply navigation requests without altering active input. - if (g.ActiveId != result->ID) + // Clear active id unless requested not to + // FIXME: ImGuiNavMoveFlags_NoClearActiveId is currently unused as we don't have a clear strategy to preserve active id after interaction, + // so this is mostly provided as a gateway for further experiments (see #1418, #2890) + if (g.ActiveId != result->ID && (g.NavMoveFlags & ImGuiNavMoveFlags_NoClearActiveId) == 0) ClearActiveID(); // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) @@ -12337,7 +12657,7 @@ static void ImGui::NavUpdateCancelRequest() ImGuiContext& g = *GImGui; const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; - if (!(nav_keyboard_active && IsKeyPressed(ImGuiKey_Escape, ImGuiKeyOwner_None)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, ImGuiKeyOwner_None))) + if (!(nav_keyboard_active && IsKeyPressed(ImGuiKey_Escape, 0, ImGuiKeyOwner_NoOwner)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, 0, ImGuiKeyOwner_NoOwner))) return; IMGUI_DEBUG_LOG_NAV("[nav] NavUpdateCancelRequest()\n"); @@ -12386,10 +12706,10 @@ static float ImGui::NavUpdatePageUpPageDown() if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL) return 0.0f; - const bool page_up_held = IsKeyDown(ImGuiKey_PageUp, ImGuiKeyOwner_None); - const bool page_down_held = IsKeyDown(ImGuiKey_PageDown, ImGuiKeyOwner_None); - const bool home_pressed = IsKeyPressed(ImGuiKey_Home, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat); - const bool end_pressed = IsKeyPressed(ImGuiKey_End, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat); + const bool page_up_held = IsKeyDown(ImGuiKey_PageUp, ImGuiKeyOwner_NoOwner); + const bool page_down_held = IsKeyDown(ImGuiKey_PageDown, ImGuiKeyOwner_NoOwner); + const bool home_pressed = IsKeyPressed(ImGuiKey_Home, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner); + const bool end_pressed = IsKeyPressed(ImGuiKey_End, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner); if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out return 0.0f; @@ -12399,9 +12719,9 @@ static float ImGui::NavUpdatePageUpPageDown() if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY) { // Fallback manual-scroll when window has no navigable item - if (IsKeyPressed(ImGuiKey_PageUp, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat)) + if (IsKeyPressed(ImGuiKey_PageUp, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner)) SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); - else if (IsKeyPressed(ImGuiKey_PageDown, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat)) + else if (IsKeyPressed(ImGuiKey_PageDown, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner)) SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); else if (home_pressed) SetScrollY(window, 0.0f); @@ -12594,9 +12914,9 @@ static void ImGui::NavUpdateWindowing() const ImGuiID owner_id = ImHashStr("###NavUpdateWindowing"); const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; - const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, owner_id, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways); - const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, owner_id, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways); - const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, 0, ImGuiInputFlags_None); + const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id); + const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id); + const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, ImGuiInputFlags_None); const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && (keyboard_next_window || keyboard_prev_window); // Note: enabled even without NavEnableKeyboard! if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) @@ -12607,7 +12927,7 @@ static void ImGui::NavUpdateWindowing() g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad; - // Register ownership of our mods. Using ImGuiInputFlags_RouteGlobalHigh in the Shortcut() calls instead would probably be correct but may have more side-effects. + // Manually register ownership of our mods. Using a global route in the Shortcut() calls instead would probably be correct but may have more side-effects. if (keyboard_next_window || keyboard_prev_window) SetKeyOwnersForKeyChord((g.ConfigNavWindowingKeyNext | g.ConfigNavWindowingKeyPrev) & ImGuiMod_Mask_, owner_id); } @@ -12655,7 +12975,7 @@ static void ImGui::NavUpdateWindowing() // Keyboard: Press and Release ALT to toggle menu layer const ImGuiKey windowing_toggle_keys[] = { ImGuiKey_LeftAlt, ImGuiKey_RightAlt }; for (ImGuiKey windowing_toggle_key : windowing_toggle_keys) - if (nav_keyboard_active && IsKeyPressed(windowing_toggle_key, ImGuiKeyOwner_None)) + if (nav_keyboard_active && IsKeyPressed(windowing_toggle_key, 0, ImGuiKeyOwner_NoOwner)) { g.NavWindowingToggleLayer = true; g.NavWindowingToggleKey = windowing_toggle_key; @@ -12670,7 +12990,7 @@ static void ImGui::NavUpdateWindowing() // We cancel toggling nav layer if an owner has claimed the key. if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper) g.NavWindowingToggleLayer = false; - if (TestKeyOwner(g.NavWindowingToggleKey, ImGuiKeyOwner_None) == false || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_None) == false) + if (TestKeyOwner(g.NavWindowingToggleKey, ImGuiKeyOwner_NoOwner) == false || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_NoOwner) == false) g.NavWindowingToggleLayer = false; // Apply layer toggle on Alt release @@ -12818,6 +13138,8 @@ bool ImGui::IsDragDropActive() void ImGui::ClearDragDrop() { ImGuiContext& g = *GImGui; + if (g.DragDropActive) + IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] ClearDragDrop()\n"); g.DragDropActive = false; g.DragDropPayload.Clear(); g.DragDropAcceptFlags = ImGuiDragDropFlags_None; @@ -12856,7 +13178,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) bool source_drag_active = false; ImGuiID source_id = 0; ImGuiID source_parent_id = 0; - if (!(flags & ImGuiDragDropFlags_SourceExtern)) + if ((flags & ImGuiDragDropFlags_SourceExtern) == 0) { source_id = g.LastItemData.ID; if (source_id != 0) @@ -12917,43 +13239,45 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) source_drag_active = true; } - if (source_drag_active) - { - if (!g.DragDropActive) - { - IM_ASSERT(source_id != 0); - ClearDragDrop(); - ImGuiPayload& payload = g.DragDropPayload; - payload.SourceId = source_id; - payload.SourceParentId = source_parent_id; - g.DragDropActive = true; - g.DragDropSourceFlags = flags; - g.DragDropMouseButton = mouse_button; - if (payload.SourceId == g.ActiveId) - g.ActiveIdNoClearOnFocusLoss = true; - } - g.DragDropSourceFrameCount = g.FrameCount; - g.DragDropWithinSource = true; + IM_ASSERT(g.DragDropWithinTarget == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget() + if (!source_drag_active) + return false; - if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) - { - // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit) - // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents. - bool ret; - if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) - ret = BeginTooltipHidden(); - else - ret = BeginTooltip(); - IM_ASSERT(ret); // FIXME-NEWBEGIN: If this ever becomes false, we need to Begin("##Hidden", NULL, ImGuiWindowFlags_NoSavedSettings) + SetWindowHiddendAndSkipItemsForCurrentFrame(). - IM_UNUSED(ret); - } + // Activate drag and drop + if (!g.DragDropActive) + { + IM_ASSERT(source_id != 0); + ClearDragDrop(); + IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] BeginDragDropSource() DragDropActive = true, source_id = %08X\n", source_id); + ImGuiPayload& payload = g.DragDropPayload; + payload.SourceId = source_id; + payload.SourceParentId = source_parent_id; + g.DragDropActive = true; + g.DragDropSourceFlags = flags; + g.DragDropMouseButton = mouse_button; + if (payload.SourceId == g.ActiveId) + g.ActiveIdNoClearOnFocusLoss = true; + } + g.DragDropSourceFrameCount = g.FrameCount; + g.DragDropWithinSource = true; + + if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) + { + // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit) + // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents. + bool ret; + if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) + ret = BeginTooltipHidden(); + else + ret = BeginTooltip(); + IM_ASSERT(ret); // FIXME-NEWBEGIN: If this ever becomes false, we need to Begin("##Hidden", NULL, ImGuiWindowFlags_NoSavedSettings) + SetWindowHiddendAndSkipItemsForCurrentFrame(). + IM_UNUSED(ret); + } - if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern)) - g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect; + if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern)) + g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect; - return true; - } - return false; + return true; } void ImGui::EndDragDropSource() @@ -13032,7 +13356,7 @@ bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id) if (window->SkipItems) return false; - IM_ASSERT(g.DragDropWithinTarget == false); + IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget() g.DragDropTargetRect = bb; g.DragDropTargetClipRect = window->ClipRect; // May want to be overriden by user depending on use case? g.DragDropTargetId = id; @@ -13067,7 +13391,7 @@ bool ImGui::BeginDragDropTarget() if (g.DragDropPayload.SourceId == id) return false; - IM_ASSERT(g.DragDropWithinTarget == false); + IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget() g.DragDropTargetRect = display_rect; g.DragDropTargetClipRect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect : window->ClipRect; g.DragDropTargetId = id; @@ -13264,7 +13588,7 @@ void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth) IM_ASSERT(g.LogEnabled == false); IM_ASSERT(g.LogFile == NULL); IM_ASSERT(g.LogBuffer.empty()); - g.LogEnabled = true; + g.LogEnabled = g.ItemUnclipByLog = true; g.LogType = type; g.LogNextPrefix = g.LogNextSuffix = NULL; g.LogDepthRef = window->DC.TreeDepth; @@ -13363,7 +13687,7 @@ void ImGui::LogFinish() break; } - g.LogEnabled = false; + g.LogEnabled = g.ItemUnclipByLog = false; g.LogType = ImGuiLogType_None; g.LogFile = NULL; g.LogBuffer.clear(); @@ -14454,9 +14778,9 @@ void ImGui::ShowMetricsWindow(bool* p_open) { // As it's difficult to interact with tree nodes while popups are open, we display everything inline. ImGuiWindow* window = popup_data.Window; - BulletText("PopupID: %08x, Window: '%s' (%s%s), BackupNavWindow '%s', ParentWindow '%s'", + BulletText("PopupID: %08x, Window: '%s' (%s%s), RestoreNavWindow '%s', ParentWindow '%s'", popup_data.PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? "Child;" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? "Menu;" : "", - popup_data.BackupNavWindow ? popup_data.BackupNavWindow->Name : "NULL", window && window->ParentWindow ? window->ParentWindow->Name : "NULL"); + popup_data.RestoreNavWindow ? popup_data.RestoreNavWindow->Name : "NULL", window && window->ParentWindow ? window->ParentWindow->Name : "NULL"); } TreePop(); } @@ -14635,7 +14959,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key); - if (owner_data->OwnerCurr == ImGuiKeyOwner_None) + if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner) continue; Text("%s: 0x%08X%s", GetKeyName(key), owner_data->OwnerCurr, owner_data->LockUntilRelease ? " LockUntilRelease" : owner_data->LockThisFrame ? " LockThisFrame" : ""); @@ -15289,8 +15613,11 @@ void ImGui::DebugLogV(const char* fmt, va_list args) if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTTY) IMGUI_DEBUG_PRINTF("%s", g.DebugLogBuf.begin() + old_size); #ifdef IMGUI_ENABLE_TEST_ENGINE + // IMGUI_TEST_ENGINE_LOG() adds a trailing \n automatically + const int new_size = g.DebugLogBuf.size(); + const bool trailing_carriage_return = (g.DebugLogBuf[new_size - 1] == '\n'); if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTestEngine) - IMGUI_TEST_ENGINE_LOG("%s", g.DebugLogBuf.begin() + old_size); + IMGUI_TEST_ENGINE_LOG("%.*s", new_size - old_size - (trailing_carriage_return ? 1 : 0), g.DebugLogBuf.begin() + old_size); #endif } @@ -15358,25 +15685,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open) clipper.Begin(g.DebugLogIndex.size()); while (clipper.Step()) for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++) - { - const char* line_begin = g.DebugLogIndex.get_line_begin(g.DebugLogBuf.c_str(), line_no); - const char* line_end = g.DebugLogIndex.get_line_end(g.DebugLogBuf.c_str(), line_no); - TextUnformatted(line_begin, line_end); // Display line - ImRect text_rect = g.LastItemData.Rect; - if (IsItemHovered()) - for (const char* p = line_begin; p <= line_end - 10; p++) // Search for 0x???????? identifiers - { - ImGuiID id = 0; - if (p[0] != '0' || (p[1] != 'x' && p[1] != 'X') || sscanf(p + 2, "%X", &id) != 1) - continue; - ImVec2 p0 = CalcTextSize(line_begin, p); - ImVec2 p1 = CalcTextSize(p, p + 10); - g.LastItemData.Rect = ImRect(text_rect.Min + ImVec2(p0.x, 0.0f), text_rect.Min + ImVec2(p0.x + p1.x, p1.y)); - if (IsMouseHoveringRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, true)) - DebugLocateItemOnHover(id); - p += 10; - } - } + DebugTextUnformattedWithLocateItem(g.DebugLogIndex.get_line_begin(g.DebugLogBuf.c_str(), line_no), g.DebugLogIndex.get_line_end(g.DebugLogBuf.c_str(), line_no)); g.DebugLogFlags = backup_log_flags; if (GetScrollY() >= GetScrollMaxY()) SetScrollHereY(1.0f); @@ -15385,6 +15694,28 @@ void ImGui::ShowDebugLogWindow(bool* p_open) End(); } +// Display line, search for 0xXXXXXXXX identifiers and call DebugLocateItemOnHover() when hovered. +void ImGui::DebugTextUnformattedWithLocateItem(const char* line_begin, const char* line_end) +{ + TextUnformatted(line_begin, line_end); + if (!IsItemHovered()) + return; + ImGuiContext& g = *GImGui; + ImRect text_rect = g.LastItemData.Rect; + for (const char* p = line_begin; p <= line_end - 10; p++) + { + ImGuiID id = 0; + if (p[0] != '0' || (p[1] != 'x' && p[1] != 'X') || sscanf(p + 2, "%X", &id) != 1) + continue; + ImVec2 p0 = CalcTextSize(line_begin, p); + ImVec2 p1 = CalcTextSize(p, p + 10); + g.LastItemData.Rect = ImRect(text_rect.Min + ImVec2(p0.x, 0.0f), text_rect.Min + ImVec2(p0.x + p1.x, p1.y)); + if (IsMouseHoveringRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, true)) + DebugLocateItemOnHover(id); + p += 10; + } +} + //----------------------------------------------------------------------------- // [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL) //----------------------------------------------------------------------------- @@ -15642,7 +15973,7 @@ void ImGui::ShowIDStackToolWindow(bool* p_open) Checkbox("Ctrl+C: copy path to clipboard", &tool->CopyToClipboardOnCtrlC); SameLine(); TextColored((time_since_copy >= 0.0f && time_since_copy < 0.75f && ImFmod(time_since_copy, 0.25f) < 0.25f * 0.5f) ? ImVec4(1.f, 1.f, 0.3f, 1.f) : ImVec4(), "*COPIED*"); - if (tool->CopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, 0, ImGuiInputFlags_RouteGlobal)) + if (tool->CopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused)) { tool->CopyToClipboardLastTime = (float)g.Time; char* p = g.TempBuffer.Data; @@ -15724,4 +16055,4 @@ void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {} //----------------------------------------------------------------------------- -#endif // #ifndef IMGUI_DISABLE +#endif // #ifndef IMGUI_DISABLE \ No newline at end of file diff --git a/platforms/desktop-shared/imgui/imgui.h b/platforms/desktop-shared/imgui/imgui.h index 386fb58..d8abf83 100644 --- a/platforms/desktop-shared/imgui/imgui.h +++ b/platforms/desktop-shared/imgui/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.90.4 +// dear imgui, v1.90.8 // (headers) // Help: @@ -7,15 +7,19 @@ // - Read top of imgui.cpp for more details, links and comments. // Resources: -// - FAQ https://dearimgui.com/faq -// - Getting Started https://dearimgui.com/getting-started -// - Homepage https://github.com/ocornut/imgui -// - Releases & changelog https://github.com/ocornut/imgui/releases -// - Gallery https://github.com/ocornut/imgui/issues/6897 (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 -// - Issues & support https://github.com/ocornut/imgui/issues -// - Tests & Automation https://github.com/ocornut/imgui_test_engine +// - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md) +// - Homepage ................... https://github.com/ocornut/imgui +// - Releases & changelog ....... https://github.com/ocornut/imgui/releases +// - Gallery .................... https://github.com/ocornut/imgui/issues/7503 (please post your screenshots/video there!) +// - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there) +// - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code) +// - Third-party Extensions https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more) +// - Bindings/Backends https://github.com/ocornut/imgui/wiki/Bindings (language bindings, backends for various tech/engines) +// - Glossary https://github.com/ocornut/imgui/wiki/Glossary +// - Debug Tools https://github.com/ocornut/imgui/wiki/Debug-Tools +// - Software using Dear ImGui https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui +// - Issues & support ........... https://github.com/ocornut/imgui/issues +// - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps) // For first-time users having issues compiling/linking/running/loading fonts: // please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. @@ -23,8 +27,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.90.4" -#define IMGUI_VERSION_NUM 19040 +#define IMGUI_VERSION "1.90.8" +#define IMGUI_VERSION_NUM 19080 #define IMGUI_HAS_TABLE /* @@ -86,6 +90,8 @@ Index of this file: #endif #define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR) / sizeof(*(_ARR)))) // Size of a static C-style array. Don't use on pointers! #define IM_UNUSED(_VAR) ((void)(_VAR)) // Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds. + +// Check that version and structures layouts are matching between compiled imgui code and caller. Read comments above DebugCheckVersionAndDataLayout() for details. #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) // Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions. @@ -126,6 +132,7 @@ Index of this file: #pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" #pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind @@ -136,6 +143,17 @@ Index of this file: // [SECTION] Forward declarations and basic types //----------------------------------------------------------------------------- +// Scalar data types +typedef unsigned int ImGuiID;// A unique ID used by widgets (typically the result of hashing a stack of string) +typedef signed char ImS8; // 8-bit signed integer +typedef unsigned char ImU8; // 8-bit unsigned integer +typedef signed short ImS16; // 16-bit signed integer +typedef unsigned short ImU16; // 16-bit unsigned integer +typedef signed int ImS32; // 32-bit signed integer == int +typedef unsigned int ImU32; // 32-bit unsigned integer (often used to store packed colors) +typedef signed long long ImS64; // 64-bit signed integer +typedef unsigned long long ImU64; // 64-bit unsigned integer + // Forward declarations struct ImDrawChannel; // Temporary storage to output draw commands out of order, used by ImDrawListSplitter and ImDrawList::ChannelsSplit() struct ImDrawCmd; // A single draw command within a parent ImDrawList (generally maps to 1 GPU draw call, unless it is a callback) @@ -171,24 +189,26 @@ struct ImGuiViewport; // A Platform Window (always only one in 'ma // Enumerations // - We don't use strongly typed enums much because they add constraints (can't extend in private code, can't store typed in bit fields, extra casting on iteration) // - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists! -// In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. -// With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. +// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. +// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. +// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments. +enum ImGuiDir : int; // -> enum ImGuiDir // Enum: A cardinal direction (Left, Right, Up, Down) enum ImGuiKey : int; // -> enum ImGuiKey // Enum: A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value) enum ImGuiMouseSource : int; // -> enum ImGuiMouseSource // Enum; A mouse input source identifier (Mouse, TouchScreen, Pen) +enum ImGuiSortDirection : ImU8; // -> enum ImGuiSortDirection // Enum: A sorting direction (ascending or descending) typedef int ImGuiCol; // -> enum ImGuiCol_ // Enum: A color identifier for styling typedef int ImGuiCond; // -> enum ImGuiCond_ // Enum: A condition for many Set*() functions typedef int ImGuiDataType; // -> enum ImGuiDataType_ // Enum: A primary data type -typedef int ImGuiDir; // -> enum ImGuiDir_ // Enum: A cardinal direction typedef int ImGuiMouseButton; // -> enum ImGuiMouseButton_ // Enum: A mouse button identifier (0=left, 1=right, 2=middle) typedef int ImGuiMouseCursor; // -> enum ImGuiMouseCursor_ // Enum: A mouse cursor shape -typedef int ImGuiSortDirection; // -> enum ImGuiSortDirection_ // Enum: A sorting direction (ascending or descending) typedef int ImGuiStyleVar; // -> enum ImGuiStyleVar_ // Enum: A variable identifier for styling typedef int ImGuiTableBgTarget; // -> enum ImGuiTableBgTarget_ // Enum: A color target for TableSetBgColor() // Flags (declared as int to allow using as flags without overhead, and to not pollute the top of this file) // - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists! -// In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. -// With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. +// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. +// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. +// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments. typedef int ImDrawFlags; // -> enum ImDrawFlags_ // Flags: for ImDrawList functions typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: for ImDrawList instance typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas build @@ -201,6 +221,7 @@ typedef int ImGuiComboFlags; // -> enum ImGuiComboFlags_ // Flags: f typedef int ImGuiDragDropFlags; // -> enum ImGuiDragDropFlags_ // Flags: for BeginDragDropSource(), AcceptDragDropPayload() typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: for IsWindowFocused() typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc. +typedef int ImGuiInputFlags; // -> enum ImGuiInputFlags_ // Flags: for Shortcut(), SetNextItemShortcut() typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText(), InputTextMultiline() typedef int ImGuiKeyChord; // -> ImGuiKey | ImGuiMod_XXX // Flags: for IsKeyChordPressed(), Shortcut() etc. an ImGuiKey optionally OR-ed with one or more ImGuiMod_XXX values. typedef int ImGuiPopupFlags; // -> enum ImGuiPopupFlags_ // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() @@ -229,17 +250,6 @@ typedef void* ImTextureID; // Default: store a pointer or an integer fi typedef unsigned short ImDrawIdx; // Default: 16-bit (for maximum compatibility with renderer backends) #endif -// Scalar data types -typedef unsigned int ImGuiID;// A unique ID used by widgets (typically the result of hashing a stack of string) -typedef signed char ImS8; // 8-bit signed integer -typedef unsigned char ImU8; // 8-bit unsigned integer -typedef signed short ImS16; // 16-bit signed integer -typedef unsigned short ImU16; // 16-bit unsigned integer -typedef signed int ImS32; // 32-bit signed integer == int -typedef unsigned int ImU32; // 32-bit unsigned integer (often used to store packed colors) -typedef signed long long ImS64; // 64-bit signed integer -typedef unsigned long long ImU64; // 64-bit unsigned integer - // Character types // (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display) typedef unsigned int ImWchar32; // A single decoded U32 character/code point. We encode them as multi bytes UTF-8 when used in strings. @@ -690,7 +700,8 @@ namespace ImGui // Tooltips // - Tooltips are windows following the mouse. They do not take focus away. - // - A tooltip window can contain items of any types. SetTooltip() is a shortcut for the 'if (BeginTooltip()) { Text(...); EndTooltip(); }' idiom. + // - A tooltip window can contain items of any types. + // - SetTooltip() is more or less a shortcut for the 'if (BeginTooltip()) { Text(...); EndTooltip(); }' idiom (with a subtlety that it discard any previously submitted tooltip) IMGUI_API bool BeginTooltip(); // begin/append a tooltip window. IMGUI_API void EndTooltip(); // only call EndTooltip() if BeginTooltip()/BeginItemTooltip() returns true! IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip. Often used after a ImGui::IsItemHovered() check. Override any previous call to SetTooltip(). @@ -701,7 +712,7 @@ namespace ImGui // - SetItemTooltip() is a shortcut for the 'if (IsItemHovered(ImGuiHoveredFlags_ForTooltip)) { SetTooltip(...); }' idiom. // - Where 'ImGuiHoveredFlags_ForTooltip' itself is a shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on active input type. For mouse it defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'. IMGUI_API bool BeginItemTooltip(); // begin/append a tooltip window if preceding item was hovered. - IMGUI_API void SetItemTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip if preceeding item was hovered. override any previous call to SetTooltip(). + IMGUI_API void SetItemTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip if preceding item was hovered. override any previous call to SetTooltip(). IMGUI_API void SetItemTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); // Popups, Modals @@ -928,6 +939,24 @@ namespace ImGui IMGUI_API const char* GetKeyName(ImGuiKey key); // [DEBUG] returns English name of the key. Those names a provided for debugging purpose and are not meant to be saved persistently not compared. IMGUI_API void SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard); // Override io.WantCaptureKeyboard flag next frame (said flag is left for your application to handle, typically when true it instructs your app to ignore inputs). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting "io.WantCaptureKeyboard = want_capture_keyboard"; after the next NewFrame() call. + // Inputs Utilities: Shortcut Testing & Routing [BETA] + // - ImGuiKeyChord = a ImGuiKey + optional ImGuiMod_Alt/ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Super. + // ImGuiKey_C // Accepted by functions taking ImGuiKey or ImGuiKeyChord arguments) + // ImGuiMod_Ctrl | ImGuiKey_C // Accepted by functions taking ImGuiKeyChord arguments) + // only ImGuiMod_XXX values are legal to combine with an ImGuiKey. You CANNOT combine two ImGuiKey values. + // - The general idea is that several callers may register interest in a shortcut, and only one owner gets it. + // Parent -> call Shortcut(Ctrl+S) // When Parent is focused, Parent gets the shortcut. + // Child1 -> call Shortcut(Ctrl+S) // When Child1 is focused, Child1 gets the shortcut (Child1 overrides Parent shortcuts) + // Child2 -> no call // When Child2 is focused, Parent gets the shortcut. + // The whole system is order independent, so if Child1 makes its calls before Parent, results will be identical. + // This is an important property as it facilitate working with foreign code or larger codebase. + // - To understand the difference: + // - IsKeyChordPressed() compares mods and call IsKeyPressed() -> function has no side-effect. + // - Shortcut() submits a route, routes are resolved, if it currently can be routed it calls IsKeyChordPressed() -> function has (desirable) side-effects as it can prevents another call from getting the route. + // - Visualize registered routes in 'Metrics/Debugger->Inputs'. + IMGUI_API bool Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0); + IMGUI_API void SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0); + // Inputs Utilities: Mouse specific // - To refer to a mouse button, you may use named enums in your code e.g. ImGuiMouseButton_Left, ImGuiMouseButton_Right. // - You can also use regular integer: it is forever guaranteed that 0=Left, 1=Right, 2=Middle. @@ -942,8 +971,8 @@ namespace ImGui IMGUI_API bool IsAnyMouseDown(); // [WILL OBSOLETE] is any mouse button held? This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid. IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls IMGUI_API ImVec2 GetMousePosOnOpeningCurrentPopup(); // retrieve mouse position at the time of opening popup we have BeginPopup() into (helper to avoid user backing that value themselves) - IMGUI_API bool IsMouseDragging(ImGuiMouseButton button, float lock_threshold = -1.0f); // is mouse dragging? (if lock_threshold < -1.0f, uses io.MouseDraggingThreshold) - IMGUI_API ImVec2 GetMouseDragDelta(ImGuiMouseButton button = 0, float lock_threshold = -1.0f); // return the delta from the initial clicking position while the mouse button is pressed or was just released. This is locked and return 0.0f until the mouse moves past a distance threshold at least once (if lock_threshold < -1.0f, uses io.MouseDraggingThreshold) + IMGUI_API bool IsMouseDragging(ImGuiMouseButton button, float lock_threshold = -1.0f); // is mouse dragging? (uses io.MouseDraggingThreshold if lock_threshold < 0.0f) + IMGUI_API ImVec2 GetMouseDragDelta(ImGuiMouseButton button = 0, float lock_threshold = -1.0f); // return the delta from the initial clicking position while the mouse button is pressed or was just released. This is locked and return 0.0f until the mouse moves past a distance threshold at least once (uses io.MouseDraggingThreshold if lock_threshold < 0.0f) IMGUI_API void ResetMouseDragDelta(ImGuiMouseButton button = 0); // IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired mouse cursor shape. Important: reset in ImGui::NewFrame(), this is updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you IMGUI_API void SetMouseCursor(ImGuiMouseCursor cursor_type); // set desired mouse cursor shape @@ -1053,28 +1082,37 @@ enum ImGuiChildFlags_ // (Those are per-item flags. There are shared flags in ImGuiIO: io.ConfigInputTextCursorBlink and io.ConfigInputTextEnterKeepActive) enum ImGuiInputTextFlags_ { + // Basic filters (also see ImGuiInputTextFlags_CallbackCharFilter) ImGuiInputTextFlags_None = 0, ImGuiInputTextFlags_CharsDecimal = 1 << 0, // Allow 0123456789.+-*/ ImGuiInputTextFlags_CharsHexadecimal = 1 << 1, // Allow 0123456789ABCDEFabcdef - ImGuiInputTextFlags_CharsUppercase = 1 << 2, // Turn a..z into A..Z - ImGuiInputTextFlags_CharsNoBlank = 1 << 3, // Filter out spaces, tabs - ImGuiInputTextFlags_AutoSelectAll = 1 << 4, // Select entire text when first taking mouse focus - ImGuiInputTextFlags_EnterReturnsTrue = 1 << 5, // Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider looking at the IsItemDeactivatedAfterEdit() function. - ImGuiInputTextFlags_CallbackCompletion = 1 << 6, // Callback on pressing TAB (for completion handling) - ImGuiInputTextFlags_CallbackHistory = 1 << 7, // Callback on pressing Up/Down arrows (for history handling) - ImGuiInputTextFlags_CallbackAlways = 1 << 8, // Callback on each iteration. User code may query cursor position, modify text buffer. - ImGuiInputTextFlags_CallbackCharFilter = 1 << 9, // Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard. - ImGuiInputTextFlags_AllowTabInput = 1 << 10, // Pressing TAB input a '\t' character into the text field - ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 11, // In multi-line mode, unfocus with Enter, add new line with Ctrl+Enter (default is opposite: unfocus with Ctrl+Enter, add line with Enter). - ImGuiInputTextFlags_NoHorizontalScroll = 1 << 12, // Disable following the cursor horizontally - ImGuiInputTextFlags_AlwaysOverwrite = 1 << 13, // Overwrite mode - ImGuiInputTextFlags_ReadOnly = 1 << 14, // Read-only mode - ImGuiInputTextFlags_Password = 1 << 15, // Password mode, display all characters as '*' + ImGuiInputTextFlags_CharsScientific = 1 << 2, // Allow 0123456789.+-*/eE (Scientific notation input) + ImGuiInputTextFlags_CharsUppercase = 1 << 3, // Turn a..z into A..Z + ImGuiInputTextFlags_CharsNoBlank = 1 << 4, // Filter out spaces, tabs + + // Inputs + ImGuiInputTextFlags_AllowTabInput = 1 << 5, // Pressing TAB input a '\t' character into the text field + ImGuiInputTextFlags_EnterReturnsTrue = 1 << 6, // Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider looking at the IsItemDeactivatedAfterEdit() function. + ImGuiInputTextFlags_EscapeClearsAll = 1 << 7, // Escape key clears content if not empty, and deactivate otherwise (contrast to default behavior of Escape to revert) + ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 8, // In multi-line mode, validate with Enter, add new line with Ctrl+Enter (default is opposite: validate with Ctrl+Enter, add line with Enter). + + // Other options + ImGuiInputTextFlags_ReadOnly = 1 << 9, // Read-only mode + ImGuiInputTextFlags_Password = 1 << 10, // Password mode, display all characters as '*', disable copy + ImGuiInputTextFlags_AlwaysOverwrite = 1 << 11, // Overwrite mode + ImGuiInputTextFlags_AutoSelectAll = 1 << 12, // Select entire text when first taking mouse focus + ImGuiInputTextFlags_ParseEmptyRefVal = 1 << 13, // InputFloat(), InputInt(), InputScalar() etc. only: parse empty string as zero value. + ImGuiInputTextFlags_DisplayEmptyRefVal = 1 << 14, // InputFloat(), InputInt(), InputScalar() etc. only: when value is zero, do not display it. Generally used with ImGuiInputTextFlags_ParseEmptyRefVal. + ImGuiInputTextFlags_NoHorizontalScroll = 1 << 15, // Disable following the cursor horizontally 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) - ImGuiInputTextFlags_EscapeClearsAll = 1 << 20, // Escape key clears content if not empty, and deactivate otherwise (contrast to default behavior of Escape to revert) + + // Callback features + ImGuiInputTextFlags_CallbackCompletion = 1 << 17, // Callback on pressing TAB (for completion handling) + ImGuiInputTextFlags_CallbackHistory = 1 << 18, // Callback on pressing Up/Down arrows (for history handling) + ImGuiInputTextFlags_CallbackAlways = 1 << 19, // Callback on each iteration. User code may query cursor position, modify text buffer. + ImGuiInputTextFlags_CallbackCharFilter = 1 << 20, // Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard. + ImGuiInputTextFlags_CallbackResize = 1 << 21, // 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 << 22, // 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 //ImGuiInputTextFlags_AlwaysInsertMode = ImGuiInputTextFlags_AlwaysOverwrite // [renamed in 1.82] name was not matching behavior @@ -1094,12 +1132,13 @@ enum ImGuiTreeNodeFlags_ ImGuiTreeNodeFlags_OpenOnArrow = 1 << 7, // Only open when clicking on the arrow part. If ImGuiTreeNodeFlags_OpenOnDoubleClick is also set, single-click arrow or double-click all box to open. ImGuiTreeNodeFlags_Leaf = 1 << 8, // No collapsing, no arrow (use as a convenience for leaf nodes). ImGuiTreeNodeFlags_Bullet = 1 << 9, // Display a bullet instead of arrow. IMPORTANT: node can still be marked open/close if you don't set the _Leaf flag! - ImGuiTreeNodeFlags_FramePadding = 1 << 10, // Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding(). - ImGuiTreeNodeFlags_SpanAvailWidth = 1 << 11, // Extend hit box to the right-most edge, even if not framed. This is not the default in order to allow adding other items on the same line. In the future we may refactor the hit system to be front-to-back, allowing natural overlaps and then this can become the default. - ImGuiTreeNodeFlags_SpanFullWidth = 1 << 12, // Extend hit box to the left-most and right-most edges (bypass the indented area). - ImGuiTreeNodeFlags_SpanAllColumns = 1 << 13, // Frame will span all columns of its container table (text will still fit in current column) - ImGuiTreeNodeFlags_NavLeftJumpsBackHere = 1 << 14, // (WIP) Nav: left direction may move to this TreeNode() from any of its child (items submitted between TreeNode and TreePop) - //ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 15, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible + ImGuiTreeNodeFlags_FramePadding = 1 << 10, // Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding() before the node. + ImGuiTreeNodeFlags_SpanAvailWidth = 1 << 11, // Extend hit box to the right-most edge, even if not framed. This is not the default in order to allow adding other items on the same line without using AllowOverlap mode. + ImGuiTreeNodeFlags_SpanFullWidth = 1 << 12, // Extend hit box to the left-most and right-most edges (cover the indent area). + ImGuiTreeNodeFlags_SpanTextWidth = 1 << 13, // Narrow hit box + narrow hovering highlight, will only cover the label text. + ImGuiTreeNodeFlags_SpanAllColumns = 1 << 14, // Frame will span all columns of its container table (text will still fit in current column) + ImGuiTreeNodeFlags_NavLeftJumpsBackHere = 1 << 15, // (WIP) Nav: left direction may move to this TreeNode() from any of its child (items submitted between TreeNode and TreePop) + //ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 16, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog, #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -1284,7 +1323,7 @@ enum ImGuiDataType_ }; // A cardinal direction -enum ImGuiDir_ +enum ImGuiDir : int { ImGuiDir_None = -1, ImGuiDir_Left = 0, @@ -1295,7 +1334,7 @@ enum ImGuiDir_ }; // A sorting direction -enum ImGuiSortDirection_ +enum ImGuiSortDirection : ImU8 { ImGuiSortDirection_None = 0, ImGuiSortDirection_Ascending = 1, // Ascending = 0->9, A->Z etc. @@ -1414,13 +1453,13 @@ enum ImGuiKey : int // - In theory the value of keyboard modifiers should be roughly equivalent to a logical or of the equivalent left/right keys. // In practice: it's complicated; mods are often provided from different sources. Keyboard layout, IME, sticky keys and // backends tend to interfere and break that equivalence. The safer decision is to relay that ambiguity down to the end-user... + // - On macOS, we swap Cmd(Super) and Ctrl keys at the time of the io.AddKeyEvent() call. ImGuiMod_None = 0, - ImGuiMod_Ctrl = 1 << 12, // Ctrl + ImGuiMod_Ctrl = 1 << 12, // Ctrl (non-macOS), Cmd (macOS) ImGuiMod_Shift = 1 << 13, // Shift ImGuiMod_Alt = 1 << 14, // Option/Menu - ImGuiMod_Super = 1 << 15, // Cmd/Super/Windows - ImGuiMod_Shortcut = 1 << 11, // Alias for Ctrl (non-macOS) _or_ Super (macOS). - ImGuiMod_Mask_ = 0xF800, // 5-bits + ImGuiMod_Super = 1 << 15, // Windows/Super (non-macOS), Ctrl (macOS) + ImGuiMod_Mask_ = 0xF000, // 4-bits // [Internal] Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index + the io.KeyMap[] array. // We are ditching this method but keeping a legacy path for user code doing e.g. IsKeyPressed(MY_NATIVE_KEY_CODE) @@ -1437,11 +1476,37 @@ enum ImGuiKey : int #endif #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGuiMod_Shortcut = ImGuiMod_Ctrl, // Removed in 1.90.7, you can now simply use ImGuiMod_Ctrl ImGuiKey_ModCtrl = ImGuiMod_Ctrl, ImGuiKey_ModShift = ImGuiMod_Shift, ImGuiKey_ModAlt = ImGuiMod_Alt, ImGuiKey_ModSuper = ImGuiMod_Super, // Renamed in 1.89 //ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter, // Renamed in 1.87 #endif }; +// Flags for Shortcut(), SetNextItemShortcut(), +// (and for upcoming extended versions of IsKeyPressed(), IsMouseClicked(), Shortcut(), SetKeyOwner(), SetItemKeyOwner() that are still in imgui_internal.h) +// Don't mistake with ImGuiInputTextFlags! (which is for ImGui::InputText() function) +enum ImGuiInputFlags_ +{ + ImGuiInputFlags_None = 0, + ImGuiInputFlags_Repeat = 1 << 0, // Enable repeat. Return true on successive repeats. Default for legacy IsKeyPressed(). NOT Default for legacy IsMouseClicked(). MUST BE == 1. + + // Flags for Shortcut(), SetNextItemShortcut() + // - Routing policies: RouteGlobal+OverActive >> RouteActive or RouteFocused (if owner is active item) >> RouteGlobal+OverFocused >> RouteFocused (if in focused window stack) >> RouteGlobal. + // - Default policy is RouteFocused. Can select only 1 policy among all available. + ImGuiInputFlags_RouteActive = 1 << 10, // Route to active item only. + ImGuiInputFlags_RouteFocused = 1 << 11, // Route to windows in the focus stack (DEFAULT). Deep-most focused window takes inputs. Active item takes inputs over deep-most focused window. + ImGuiInputFlags_RouteGlobal = 1 << 12, // Global route (unless a focused window or active item registered the route). + ImGuiInputFlags_RouteAlways = 1 << 13, // Do not register route, poll keys directly. + // - Routing options + ImGuiInputFlags_RouteOverFocused = 1 << 14, // Option: global route: higher priority than focused route (unless active item in focused route). + ImGuiInputFlags_RouteOverActive = 1 << 15, // Option: global route: higher priority than active item. Unlikely you need to use that: will interfere with every active items, e.g. CTRL+A registered by InputText will be overridden by this. May not be fully honored as user/internal code is likely to always assume they can access keys when active. + ImGuiInputFlags_RouteUnlessBgFocused = 1 << 16, // Option: global route: will not be applied if underlying background/void is focused (== no Dear ImGui windows are focused). Useful for overlay applications. + ImGuiInputFlags_RouteFromRootWindow = 1 << 17, // Option: route evaluated from the point of view of root window rather than current window. + + // Flags for SetNextItemShortcut() + ImGuiInputFlags_Tooltip = 1 << 18, // Automatically display a tooltip when hovering item [BETA] Unsure of right api (opt-in/opt-out) +}; + #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO // OBSOLETED in 1.88 (from July 2022): ImGuiNavInput and io.NavInputs[]. // Official backends between 1.60 and 1.86: will keep working and feed gamepad inputs as long as IMGUI_DISABLE_OBSOLETE_KEYIO is not set. @@ -1543,41 +1608,45 @@ enum ImGuiCol_ // - The enum only refers to fields of ImGuiStyle which makes sense to be pushed/popped inside UI code. // During initialization or between frames, feel free to just poke into ImGuiStyle directly. // - Tip: Use your programming IDE navigation facilities on the names in the _second column_ below to find the actual members and their description. -// In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. -// With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. +// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. +// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. +// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments. // - When changing this enum, you need to update the associated internal table GStyleVarInfo[] accordingly. This is where we link enum values to members offset/type. enum ImGuiStyleVar_ { - // Enum name --------------------- // Member in ImGuiStyle structure (see ImGuiStyle for descriptions) - ImGuiStyleVar_Alpha, // float Alpha - ImGuiStyleVar_DisabledAlpha, // float DisabledAlpha - ImGuiStyleVar_WindowPadding, // ImVec2 WindowPadding - ImGuiStyleVar_WindowRounding, // float WindowRounding - ImGuiStyleVar_WindowBorderSize, // float WindowBorderSize - ImGuiStyleVar_WindowMinSize, // ImVec2 WindowMinSize - ImGuiStyleVar_WindowTitleAlign, // ImVec2 WindowTitleAlign - ImGuiStyleVar_ChildRounding, // float ChildRounding - ImGuiStyleVar_ChildBorderSize, // float ChildBorderSize - ImGuiStyleVar_PopupRounding, // float PopupRounding - ImGuiStyleVar_PopupBorderSize, // float PopupBorderSize - ImGuiStyleVar_FramePadding, // ImVec2 FramePadding - ImGuiStyleVar_FrameRounding, // float FrameRounding - ImGuiStyleVar_FrameBorderSize, // float FrameBorderSize - ImGuiStyleVar_ItemSpacing, // ImVec2 ItemSpacing - ImGuiStyleVar_ItemInnerSpacing, // ImVec2 ItemInnerSpacing - ImGuiStyleVar_IndentSpacing, // float IndentSpacing - ImGuiStyleVar_CellPadding, // ImVec2 CellPadding - ImGuiStyleVar_ScrollbarSize, // float ScrollbarSize - ImGuiStyleVar_ScrollbarRounding, // float ScrollbarRounding - ImGuiStyleVar_GrabMinSize, // float GrabMinSize - ImGuiStyleVar_GrabRounding, // float GrabRounding - ImGuiStyleVar_TabRounding, // float TabRounding - ImGuiStyleVar_TabBarBorderSize, // float TabBarBorderSize - ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign - ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign - ImGuiStyleVar_SeparatorTextBorderSize,// float SeparatorTextBorderSize - ImGuiStyleVar_SeparatorTextAlign, // ImVec2 SeparatorTextAlign - ImGuiStyleVar_SeparatorTextPadding,// ImVec2 SeparatorTextPadding + // Enum name -------------------------- // Member in ImGuiStyle structure (see ImGuiStyle for descriptions) + ImGuiStyleVar_Alpha, // float Alpha + ImGuiStyleVar_DisabledAlpha, // float DisabledAlpha + ImGuiStyleVar_WindowPadding, // ImVec2 WindowPadding + ImGuiStyleVar_WindowRounding, // float WindowRounding + ImGuiStyleVar_WindowBorderSize, // float WindowBorderSize + ImGuiStyleVar_WindowMinSize, // ImVec2 WindowMinSize + ImGuiStyleVar_WindowTitleAlign, // ImVec2 WindowTitleAlign + ImGuiStyleVar_ChildRounding, // float ChildRounding + ImGuiStyleVar_ChildBorderSize, // float ChildBorderSize + ImGuiStyleVar_PopupRounding, // float PopupRounding + ImGuiStyleVar_PopupBorderSize, // float PopupBorderSize + ImGuiStyleVar_FramePadding, // ImVec2 FramePadding + ImGuiStyleVar_FrameRounding, // float FrameRounding + ImGuiStyleVar_FrameBorderSize, // float FrameBorderSize + ImGuiStyleVar_ItemSpacing, // ImVec2 ItemSpacing + ImGuiStyleVar_ItemInnerSpacing, // ImVec2 ItemInnerSpacing + ImGuiStyleVar_IndentSpacing, // float IndentSpacing + ImGuiStyleVar_CellPadding, // ImVec2 CellPadding + ImGuiStyleVar_ScrollbarSize, // float ScrollbarSize + ImGuiStyleVar_ScrollbarRounding, // float ScrollbarRounding + ImGuiStyleVar_GrabMinSize, // float GrabMinSize + ImGuiStyleVar_GrabRounding, // float GrabRounding + ImGuiStyleVar_TabRounding, // float TabRounding + ImGuiStyleVar_TabBorderSize, // float TabBorderSize + ImGuiStyleVar_TabBarBorderSize, // float TabBarBorderSize + ImGuiStyleVar_TableAngledHeadersAngle, // float TableAngledHeadersAngle + ImGuiStyleVar_TableAngledHeadersTextAlign,// ImVec2 TableAngledHeadersTextAlign + ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign + ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign + ImGuiStyleVar_SeparatorTextBorderSize, // float SeparatorTextBorderSize + ImGuiStyleVar_SeparatorTextAlign, // ImVec2 SeparatorTextAlign + ImGuiStyleVar_SeparatorTextPadding, // ImVec2 SeparatorTextPadding ImGuiStyleVar_COUNT }; @@ -1588,10 +1657,8 @@ enum ImGuiButtonFlags_ ImGuiButtonFlags_MouseButtonLeft = 1 << 0, // React on left mouse button (default) ImGuiButtonFlags_MouseButtonRight = 1 << 1, // React on right mouse button ImGuiButtonFlags_MouseButtonMiddle = 1 << 2, // React on center mouse button - - // [Internal] - ImGuiButtonFlags_MouseButtonMask_ = ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle, - ImGuiButtonFlags_MouseButtonDefault_ = ImGuiButtonFlags_MouseButtonLeft, + ImGuiButtonFlags_MouseButtonMask_ = ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle, // [Internal] + //ImGuiButtonFlags_MouseButtonDefault_ = ImGuiButtonFlags_MouseButtonLeft, }; // Flags for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4() / ColorButton() @@ -1864,7 +1931,7 @@ struct ImGuiTableColumnSortSpecs ImGuiID ColumnUserID; // User id of the column (if specified by a TableSetupColumn() call) ImS16 ColumnIndex; // Index of the column ImS16 SortOrder; // Index within parent ImGuiTableSortSpecs (always stored in order starting from 0, tables sorted on a single criteria will always have a 0 here) - ImGuiSortDirection SortDirection : 8; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending + ImGuiSortDirection SortDirection; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending ImGuiTableColumnSortSpecs() { memset(this, 0, sizeof(*this)); } }; @@ -1992,7 +2059,7 @@ struct ImGuiStyle float FrameBorderSize; // Thickness of border around frames. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). ImVec2 ItemSpacing; // Horizontal and vertical spacing between widgets/lines. ImVec2 ItemInnerSpacing; // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label). - ImVec2 CellPadding; // Padding within a table cell. CellPadding.y may be altered between different rows. + ImVec2 CellPadding; // Padding within a table cell. Cellpadding.x is locked for entire table. CellPadding.y may be altered between different rows. ImVec2 TouchExtraPadding; // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! float IndentSpacing; // Horizontal indentation when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). float ColumnsMinSpacing; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1). @@ -2006,15 +2073,16 @@ struct ImGuiStyle float TabMinWidthForCloseButton; // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. float TabBarBorderSize; // Thickness of tab-bar separator, which takes on the tab active color to denote focus. float TableAngledHeadersAngle; // Angle of angled headers (supported values range from -50.0f degrees to +50.0f degrees). + ImVec2 TableAngledHeadersTextAlign;// Alignment of angled headers within the cell ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered). ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. float SeparatorTextBorderSize; // Thickkness of border in SeparatorText() ImVec2 SeparatorTextAlign; // Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center). ImVec2 SeparatorTextPadding; // Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y. - ImVec2 DisplayWindowPadding; // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows. - ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! - float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. + ImVec2 DisplayWindowPadding; // Apply to regular windows: amount which we enforce to keep visible when moving near edges of your screen. + ImVec2 DisplaySafeAreaPadding; // Apply to every windows, menus, popups, tooltips: amount where we avoid displaying contents. Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured). + float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). We apply per-monitor DPI scaling over this scale. May be removed later. bool AntiAliasedLines; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList). bool AntiAliasedLinesUseTex; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). Latched at the beginning of the frame (copied to ImDrawList). bool AntiAliasedFill; // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList). @@ -2039,6 +2107,9 @@ struct ImGuiStyle //----------------------------------------------------------------------------- // Communicate most settings and inputs/outputs to Dear ImGui using this structure. // Access via ImGui::GetIO(). Read 'Programmer guide' section in .cpp file for general usage. +// It is generally expected that: +// - initialization: backends and user code writes to ImGuiIO. +// - main loop: backends writes to ImGuiIO, user code and imgui code reads from ImGuiIO. //----------------------------------------------------------------------------- // [Internal] Storage used by IsKeyDown(), IsKeyPressed() etc functions. @@ -2074,7 +2145,7 @@ struct ImGuiIO // Miscellaneous options bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations. - bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl. + bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // Swap Cmd<>Ctrl keys + OS X style text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl. bool ConfigInputTrickleEventQueue; // = true // Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates. bool ConfigInputTextCursorBlink; // = true // Enable blinking cursor (optional as some users consider it to be distracting). bool ConfigInputTextEnterKeepActive; // = false // [BETA] Pressing Enter will keep item active and select contents (single-line only). @@ -2185,16 +2256,6 @@ struct ImGuiIO int MetricsActiveWindows; // Number of active windows ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta. - // Legacy: before 1.87, we required backend to fill io.KeyMap[] (imgui->native map) during initialization and io.KeysDown[] (native indices) every frame. - // This is still temporarily supported as a legacy feature. However the new preferred scheme is for backend to call io.AddKeyEvent(). - // Old (<1.87): ImGui::IsKeyPressed(ImGui::GetIO().KeyMap[ImGuiKey_Space]) --> New (1.87+) ImGui::IsKeyPressed(ImGuiKey_Space) -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - int KeyMap[ImGuiKey_COUNT]; // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512. - bool KeysDown[ImGuiKey_COUNT]; // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). This used to be [512] sized. It is now ImGuiKey_COUNT to allow legacy io.KeysDown[GetKeyIndex(...)] to work without an overflow. - float NavInputs[ImGuiNavInput_COUNT]; // [LEGACY] Since 1.88, NavInputs[] was removed. Backends from 1.60 to 1.86 won't build. Feed gamepad inputs via io.AddKeyEvent() and ImGuiKey_GamepadXXX enums. - //void* ImeWindowHandle; // [Obsoleted in 1.87] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning. -#endif - //------------------------------------------------------------------ // [Internal] Dear ImGui will maintain those fields. Forward compatibility not guaranteed! //------------------------------------------------------------------ @@ -2215,7 +2276,7 @@ struct ImGuiIO bool KeySuper; // Keyboard modifier down: Cmd/Super/Windows // Other state maintained from data above + IO function calls - ImGuiKeyChord KeyMods; // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags. DOES NOT CONTAINS ImGuiMod_Shortcut which is pretranslated). Read-only, updated by NewFrame() + ImGuiKeyChord KeyMods; // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags. Read-only, updated by NewFrame() ImGuiKeyData KeysData[ImGuiKey_KeysData_SIZE]; // Key state for all known keys. Use IsKeyXXX() functions to access this. bool WantCaptureMouseUnlessPopupClose; // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup. ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) @@ -2229,6 +2290,7 @@ struct ImGuiIO bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds. bool MouseDownOwnedUnlessPopupClose[5]; // Track if button was clicked inside a dear imgui window. bool MouseWheelRequestAxisSwap; // On a non-Mac system, holding SHIFT requests WheelY to perform the equivalent of a WheelX event. On a Mac system this is already enforced by the system. + bool MouseCtrlLeftAsRightClick; // (OSX) Set to true when the current click was a ctrl-click that spawned a simulated right click float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) float MouseDownDurationPrev[5]; // Previous time the mouse button has been down float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point (used for moving thresholds) @@ -2240,6 +2302,16 @@ struct ImGuiIO ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16() ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper. + // Legacy: before 1.87, we required backend to fill io.KeyMap[] (imgui->native map) during initialization and io.KeysDown[] (native indices) every frame. + // This is still temporarily supported as a legacy feature. However the new preferred scheme is for backend to call io.AddKeyEvent(). + // Old (<1.87): ImGui::IsKeyPressed(ImGui::GetIO().KeyMap[ImGuiKey_Space]) --> New (1.87+) ImGui::IsKeyPressed(ImGuiKey_Space) +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + int KeyMap[ImGuiKey_COUNT]; // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512. + bool KeysDown[ImGuiKey_COUNT]; // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). This used to be [512] sized. It is now ImGuiKey_COUNT to allow legacy io.KeysDown[GetKeyIndex(...)] to work without an overflow. + float NavInputs[ImGuiNavInput_COUNT]; // [LEGACY] Since 1.88, NavInputs[] was removed. Backends from 1.60 to 1.86 won't build. Feed gamepad inputs via io.AddKeyEvent() and ImGuiKey_GamepadXXX enums. + //void* ImeWindowHandle; // [Obsoleted in 1.87] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning. +#endif + IMGUI_API ImGuiIO(); }; @@ -2264,6 +2336,8 @@ struct ImGuiInputTextCallbackData void* UserData; // What user passed to InputText() // Read-only // Arguments for the different callback events + // - During Resize callback, Buf will be same as your input buffer. + // - However, during Completion/History/Always callback, Buf always points to our own internal data (it is not the same as your buffer)! Changes to it will be reflected into your own buffer shortly after the callback. // - To modify the text buffer in a callback, prefer using the InsertChars() / DeleteChars() function. InsertChars() will take care of calling the resize callback if necessary. // - If you know your edits are not going to resize the underlying buffer allocation, you may modify the contents of 'Buf[]' directly. You need to update 'BufTextLen' accordingly (0 <= BufTextLen < BufSize) and set 'BufDirty'' to true so InputText can update its internal state. ImWchar EventChar; // Character input // Read-write // [CharFilter] Replace character with another one, or set to zero to drop. return 1 is equivalent to setting EventChar=0; @@ -2707,15 +2781,15 @@ struct ImDrawList // [Internal, used while building lists] unsigned int _VtxCurrentIdx; // [Internal] generally == VtxBuffer.Size unless we are past 64K vertices, in which case this gets reset to 0. ImDrawListSharedData* _Data; // Pointer to shared draw data (you can use ImGui::GetDrawListSharedData() to get the one from current ImGui context) - const char* _OwnerName; // Pointer to owner window's name for debugging ImDrawVert* _VtxWritePtr; // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) ImDrawIdx* _IdxWritePtr; // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) - ImVector _ClipRectStack; // [Internal] - ImVector _TextureIdStack; // [Internal] ImVector _Path; // [Internal] current path building ImDrawCmdHeader _CmdHeader; // [Internal] template of active commands. Fields should match those of CmdBuffer.back(). ImDrawListSplitter _Splitter; // [Internal] for channels api (note: prefer using your own persistent instance of ImDrawListSplitter!) + ImVector _ClipRectStack; // [Internal] + ImVector _TextureIdStack; // [Internal] float _FringeScale; // [Internal] anti-alias fringe is scaled by this value, this helps to keep things sharp while zooming at vertex buffer content + const char* _OwnerName; // Pointer to owner window's name for debugging // If you want to create ImDrawList instances, pass them ImGui::GetDrawListSharedData() or create and use your own ImDrawListSharedData (so you can use ImDrawList without ImGui) ImDrawList(ImDrawListSharedData* shared_data) { memset(this, 0, sizeof(*this)); _Data = shared_data; } @@ -2748,15 +2822,20 @@ struct ImDrawList IMGUI_API void AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments = 0); IMGUI_API void AddNgon(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness = 1.0f); IMGUI_API void AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments); - IMGUI_API void AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f); - IMGUI_API void AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0); + IMGUI_API void AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f); + IMGUI_API void AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0); IMGUI_API void AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL); IMGUI_API void AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL); - IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness); - IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col); IMGUI_API void AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points) IMGUI_API void AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments = 0); // Quadratic Bezier (3 control points) + // General polygon + // - Only simple polygons are supported by filling functions (no self-intersections, no holes). + // - Concave polygon fill is more expensive than convex one: it has O(N^2) complexity. Provided as a convenience fo user but not used by main library. + IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness); + IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col); + IMGUI_API void AddConcavePolyFilled(const ImVec2* points, int num_points, ImU32 col); + // Image primitives // - Read FAQ to understand what ImTextureID is. // - "p_min" and "p_max" represent the upper-left and lower-right corners of the rectangle. @@ -2772,10 +2851,11 @@ struct ImDrawList inline void PathLineTo(const ImVec2& pos) { _Path.push_back(pos); } inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path.Data[_Path.Size - 1], &pos, 8) != 0) _Path.push_back(pos); } inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; } + inline void PathFillConcave(ImU32 col) { AddConcavePolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; } inline void PathStroke(ImU32 col, ImDrawFlags flags = 0, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, flags, thickness); _Path.Size = 0; } IMGUI_API void PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 0); IMGUI_API void PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle - IMGUI_API void PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments = 0); // Ellipse + IMGUI_API void PathEllipticalArcTo(const ImVec2& center, const ImVec2& radius, float rot, float a_min, float a_max, int num_segments = 0); // Ellipse IMGUI_API void PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0); // Cubic Bezier (4 control points) IMGUI_API void PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments = 0); // Quadratic Bezier (3 control points) IMGUI_API void PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, ImDrawFlags flags = 0); @@ -2808,6 +2888,9 @@ struct ImDrawList inline void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } // Write vertex with unique index // Obsolete names + //inline void AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f) { AddEllipse(center, ImVec2(radius_x, radius_y), col, rot, num_segments, thickness); } // OBSOLETED in 1.90.5 (Mar 2024) + //inline void AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0) { AddEllipseFilled(center, ImVec2(radius_x, radius_y), col, rot, num_segments); } // OBSOLETED in 1.90.5 (Mar 2024) + //inline void PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments = 0) { PathEllipticalArcTo(center, ImVec2(radius_x, radius_y), rot, a_min, a_max, num_segments); } // OBSOLETED in 1.90.5 (Mar 2024) //inline void AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0) { AddBezierCubic(p1, p2, p3, p4, col, thickness, num_segments); } // OBSOLETED in 1.80 (Jan 2021) //inline void PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0) { PathBezierCubicCurveTo(p2, p3, p4, num_segments); } // OBSOLETED in 1.80 (Jan 2021) @@ -2996,7 +3079,7 @@ struct ImFontAtlas // You can request arbitrary rectangles to be packed into the atlas, for your own purposes. // - After calling Build(), you can query the rectangle position and render your pixels. - // - If you render colored output, set 'atlas->TexPixelsUseColors = true' as this may help some backends decide of prefered texture format. + // - If you render colored output, set 'atlas->TexPixelsUseColors = true' as this may help some backends decide of preferred texture format. // - You can also request your rectangles to be mapped as font glyph (given a font + Unicode point), // so you can render e.g. custom colorful icons and use them as regular glyphs. // - Read docs/FONTS.md for more details about using colorful icons. @@ -3162,15 +3245,6 @@ struct ImGuiPlatformImeData // Please keep your copy of dear imgui up to date! Occasionally set '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in imconfig.h to stay ahead. //----------------------------------------------------------------------------- -namespace ImGui -{ -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - IMGUI_API ImGuiKey GetKeyIndex(ImGuiKey key); // map ImGuiKey_* values into legacy native key index. == io.KeyMap[key] -#else - static inline ImGuiKey GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "ImGuiKey and native_index was merged together and native_index is disabled by IMGUI_DISABLE_OBSOLETE_KEYIO. Please switch to ImGuiKey."); return key; } -#endif -} - #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS namespace ImGui { @@ -3189,11 +3263,14 @@ namespace ImGui static inline void PopAllowKeyboardFocus() { PopTabStop(); } // OBSOLETED in 1.89 (from August 2022) IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); // Use new ImageButton() signature (explicit item id, regular FramePadding) - // OBSOLETED in 1.88 (from May 2022) - static inline void CaptureKeyboardFromApp(bool want_capture_keyboard = true) { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value. - static inline void CaptureMouseFromApp(bool want_capture_mouse = true) { SetNextFrameWantCaptureMouse(want_capture_mouse); } // Renamed as name was misleading + removed default value. + // OBSOLETED in 1.87 (from February 2022 but more formally obsoleted April 2024) + IMGUI_API ImGuiKey GetKeyIndex(ImGuiKey key); // Map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]. When using a 1.87+ backend using io.AddKeyEvent(), calling GetKeyIndex() with ANY ImGuiKey_XXXX values will return the same value! + //static inline ImGuiKey GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); return key; } // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE) + //-- OBSOLETED in 1.88 (from May 2022) + //static inline void CaptureKeyboardFromApp(bool want_capture_keyboard = true) { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value. + //static inline void CaptureMouseFromApp(bool want_capture_mouse = true) { SetNextFrameWantCaptureMouse(want_capture_mouse); } // Renamed as name was misleading + removed default value. //-- OBSOLETED in 1.86 (from November 2021) //IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Code removed, see 1.90 for last version of the code. Calculate range of visible items for large list of evenly sized items. Prefer using ImGuiListClipper. //-- OBSOLETED in 1.85 (from August 2021) @@ -3305,4 +3382,4 @@ enum ImGuiModFlags_ { ImGuiModFlags_None = 0, ImGuiModFlags_Ctrl = ImGuiMod_Ctrl #endif #endif -#endif // #ifndef IMGUI_DISABLE +#endif // #ifndef IMGUI_DISABLE \ No newline at end of file diff --git a/platforms/desktop-shared/imgui/imgui_demo.cpp b/platforms/desktop-shared/imgui/imgui_demo.cpp index 707c14d..5644c19 100644 --- a/platforms/desktop-shared/imgui/imgui_demo.cpp +++ b/platforms/desktop-shared/imgui/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.4 +// dear imgui, v1.90.8 // (demo code) // Help: @@ -7,9 +7,14 @@ // - Need help integrating Dear ImGui in your codebase? // - Read Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started // - Read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase. -// Read imgui.cpp for more details, documentation and comments. +// Read top of imgui.cpp and imgui.h for many details, documentation, comments, links. // Get the latest version at https://github.com/ocornut/imgui +// How to easily locate code? +// - Use the Item Picker to debug break in code by clicking any widgets: https://github.com/ocornut/imgui/wiki/Debug-Tools +// - Browse an online version the demo with code linked to hovered widgets: https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html +// - Find a visible string and search for it in the code! + //--------------------------------------------------- // PLEASE DO NOT REMOVE THIS FILE FROM YOUR PROJECT! //--------------------------------------------------- @@ -54,8 +59,9 @@ // Because we can't assume anything about your support of maths operators, we cannot use them in imgui_demo.cpp. // Navigating this file: -// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. -// - With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. +// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. +// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. +// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments. /* @@ -130,6 +136,7 @@ Index of this file: #pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. #pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access #elif defined(__GNUC__) #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size @@ -259,6 +266,9 @@ void ImGui::ShowDemoWindow(bool* p_open) // Most functions would normally just assert/crash if the context is missing. IM_ASSERT(ImGui::GetCurrentContext() != NULL && "Missing Dear ImGui context. Refer to examples app!"); + // Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues. + IMGUI_CHECKVERSION(); + // Examples Apps (accessible from the "Examples" menu) static bool show_app_main_menu_bar = false; static bool show_app_console = false; @@ -481,6 +491,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback."); ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly); ImGui::Checkbox("io.ConfigMacOSXBehaviors", &io.ConfigMacOSXBehaviors); + ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors."); ImGui::Text("Also see Style->Rendering for rendering options."); ImGui::SeparatorText("Debug"); @@ -899,13 +910,18 @@ static void ShowDemoWindowWidgets() if (i == 0) ImGui::SetNextItemOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode((void*)(intptr_t)i, "Child %d", i)) + // Here we use PushID() to generate a unique base ID, and then the "" used as TreeNode id won't conflict. + // An alternative to using 'PushID() + TreeNode("", ...)' to generate a unique ID is to use 'TreeNode((void*)(intptr_t)i, ...)', + // aka generate a dummy pointer-sized value to be hashed. The demo below uses that technique. Both are fine. + ImGui::PushID(i); + if (ImGui::TreeNode("", "Child %d", i)) { ImGui::Text("blah blah"); ImGui::SameLine(); if (ImGui::SmallButton("button")) {} ImGui::TreePop(); } + ImGui::PopID(); } ImGui::TreePop(); } @@ -923,7 +939,10 @@ static void ShowDemoWindowWidgets() ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnDoubleClick", &base_flags, ImGuiTreeNodeFlags_OpenOnDoubleClick); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAvailWidth", &base_flags, ImGuiTreeNodeFlags_SpanAvailWidth); ImGui::SameLine(); HelpMarker("Extend hit area to all available width instead of allowing more items to be laid out after the node."); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", &base_flags, ImGuiTreeNodeFlags_SpanFullWidth); + ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanTextWidth", &base_flags, ImGuiTreeNodeFlags_SpanTextWidth); ImGui::SameLine(); HelpMarker("Reduce hit area to the text label and a bit of margin."); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &base_flags, ImGuiTreeNodeFlags_SpanAllColumns); ImGui::SameLine(); HelpMarker("For use in Tables only."); + ImGui::CheckboxFlags("ImGuiTreeNodeFlags_AllowOverlap", &base_flags, ImGuiTreeNodeFlags_AllowOverlap); + ImGui::CheckboxFlags("ImGuiTreeNodeFlags_Framed", &base_flags, ImGuiTreeNodeFlags_Framed); ImGui::SameLine(); HelpMarker("Draw frame with background (e.g. for CollapsingHeader)"); ImGui::Checkbox("Align label with current X position", &align_label_with_current_x_position); ImGui::Checkbox("Test tree node as drag source", &test_drag_and_drop); ImGui::Text("Hello!"); @@ -956,6 +975,12 @@ static void ShowDemoWindowWidgets() ImGui::Text("This is a drag and drop source"); ImGui::EndDragDropSource(); } + if (i == 2) + { + // Item 2 has an additional inline button to help demonstrate SpanTextWidth. + ImGui::SameLine(); + if (ImGui::SmallButton("button")) {} + } if (node_open) { ImGui::BulletText("Blah blah\nBlah Blah"); @@ -1288,6 +1313,7 @@ static void ShowDemoWindowWidgets() } ImGui::EndListBox(); } + ImGui::SameLine(); HelpMarker("Here we are sharing selection state between both boxes."); // Custom size: use all width, 5 items tall ImGui::Text("Full-width:"); @@ -1818,10 +1844,10 @@ static void ShowDemoWindowWidgets() ImGui::Checkbox("Animate", &animate); // Plot as lines and plot as histogram - IMGUI_DEMO_MARKER("Widgets/Plotting/PlotLines, PlotHistogram"); static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr)); ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f)); + //ImGui::SameLine(); HelpMarker("Consider using ImPlot instead!"); // Fill an array of contiguous float values to plot // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float @@ -1871,15 +1897,17 @@ static void ShowDemoWindowWidgets() ImGui::PlotHistogram("Histogram", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80)); ImGui::Separator(); + ImGui::TreePop(); + } + + IMGUI_DEMO_MARKER("Widgets/Progress Bars"); + if (ImGui::TreeNode("Progress Bars")) + { // Animate a simple progress bar - IMGUI_DEMO_MARKER("Widgets/Plotting/ProgressBar"); static float progress = 0.0f, progress_dir = 1.0f; - if (animate) - { - progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime; - if (progress >= +1.1f) { progress = +1.1f; progress_dir *= -1.0f; } - if (progress <= -0.1f) { progress = -0.1f; progress_dir *= -1.0f; } - } + progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime; + if (progress >= +1.1f) { progress = +1.1f; progress_dir *= -1.0f; } + if (progress <= -0.1f) { progress = -0.1f; progress_dir *= -1.0f; } // Typically we would use ImVec2(-1.0f,0.0f) or ImVec2(-FLT_MIN,0.0f) to use all available width, // or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth. @@ -1891,6 +1919,13 @@ static void ShowDemoWindowWidgets() char buf[32]; sprintf(buf, "%d/%d", (int)(progress_saturated * 1753), 1753); ImGui::ProgressBar(progress, ImVec2(0.f, 0.f), buf); + + // Pass an animated negative value, e.g. -1.0f * (float)ImGui::GetTime() is the recommended value. + // Adjust the factor if you want to adjust the animation speed. + ImGui::ProgressBar(-1.0f * (float)ImGui::GetTime(), ImVec2(0.0f, 0.0f), "Searching.."); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::Text("Indeterminate"); + ImGui::TreePop(); } @@ -2238,20 +2273,24 @@ static void ShowDemoWindowWidgets() IMGUI_DEMO_MARKER("Widgets/Data Types/Inputs"); static bool inputs_step = true; + static ImGuiInputTextFlags flags = ImGuiInputTextFlags_None; ImGui::SeparatorText("Inputs"); ImGui::Checkbox("Show step buttons", &inputs_step); - ImGui::InputScalar("input s8", ImGuiDataType_S8, &s8_v, inputs_step ? &s8_one : NULL, NULL, "%d"); - ImGui::InputScalar("input u8", ImGuiDataType_U8, &u8_v, inputs_step ? &u8_one : NULL, NULL, "%u"); - ImGui::InputScalar("input s16", ImGuiDataType_S16, &s16_v, inputs_step ? &s16_one : NULL, NULL, "%d"); - ImGui::InputScalar("input u16", ImGuiDataType_U16, &u16_v, inputs_step ? &u16_one : NULL, NULL, "%u"); - ImGui::InputScalar("input s32", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%d"); - ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%04X"); - ImGui::InputScalar("input u32", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%u"); - ImGui::InputScalar("input u32 hex", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%08X"); - ImGui::InputScalar("input s64", ImGuiDataType_S64, &s64_v, inputs_step ? &s64_one : NULL); - ImGui::InputScalar("input u64", ImGuiDataType_U64, &u64_v, inputs_step ? &u64_one : NULL); - ImGui::InputScalar("input float", ImGuiDataType_Float, &f32_v, inputs_step ? &f32_one : NULL); - ImGui::InputScalar("input double", ImGuiDataType_Double, &f64_v, inputs_step ? &f64_one : NULL); + ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly); + ImGui::CheckboxFlags("ImGuiInputTextFlags_ParseEmptyRefVal", &flags, ImGuiInputTextFlags_ParseEmptyRefVal); + ImGui::CheckboxFlags("ImGuiInputTextFlags_DisplayEmptyRefVal", &flags, ImGuiInputTextFlags_DisplayEmptyRefVal); + ImGui::InputScalar("input s8", ImGuiDataType_S8, &s8_v, inputs_step ? &s8_one : NULL, NULL, "%d", flags); + ImGui::InputScalar("input u8", ImGuiDataType_U8, &u8_v, inputs_step ? &u8_one : NULL, NULL, "%u", flags); + ImGui::InputScalar("input s16", ImGuiDataType_S16, &s16_v, inputs_step ? &s16_one : NULL, NULL, "%d", flags); + ImGui::InputScalar("input u16", ImGuiDataType_U16, &u16_v, inputs_step ? &u16_one : NULL, NULL, "%u", flags); + ImGui::InputScalar("input s32", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%d", flags); + ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%04X", flags); + ImGui::InputScalar("input u32", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%u", flags); + ImGui::InputScalar("input u32 hex", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%08X", flags); + ImGui::InputScalar("input s64", ImGuiDataType_S64, &s64_v, inputs_step ? &s64_one : NULL, NULL, NULL, flags); + ImGui::InputScalar("input u64", ImGuiDataType_U64, &u64_v, inputs_step ? &u64_one : NULL, NULL, NULL, flags); + ImGui::InputScalar("input float", ImGuiDataType_Float, &f32_v, inputs_step ? &f32_one : NULL, NULL, NULL, flags); + ImGui::InputScalar("input double", ImGuiDataType_Double, &f64_v, inputs_step ? &f64_one : NULL, NULL, NULL, flags); ImGui::TreePop(); } @@ -2481,9 +2520,7 @@ static void ShowDemoWindowWidgets() { IM_UNUSED(payload); ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); - ImGui::BeginTooltip(); - ImGui::Text("Cannot drop here!"); - ImGui::EndTooltip(); + ImGui::SetTooltip("Cannot drop here!"); } ImGui::EndDragDropTarget(); } @@ -3842,7 +3879,7 @@ static void ShowDemoWindowPopups() static int item = 1; static float color[4] = { 0.4f, 0.7f, 0.0f, 0.5f }; ImGui::Combo("Combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); - ImGui::ColorEdit4("color", color); + ImGui::ColorEdit4("Color", color); if (ImGui::Button("Add another modal..")) ImGui::OpenPopup("Stacked 2"); @@ -3854,6 +3891,7 @@ static void ShowDemoWindowPopups() if (ImGui::BeginPopupModal("Stacked 2", &unused_open)) { ImGui::Text("Hello from Stacked The Second!"); + ImGui::ColorEdit4("Color", color); // Allow opening another nested popup if (ImGui::Button("Close")) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); @@ -5139,7 +5177,8 @@ static void ShowDemoWindowTables() static ImGuiTableFlags flags = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody; static ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAllColumns; - ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", &tree_node_flags, ImGuiTreeNodeFlags_SpanFullWidth); + ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", &tree_node_flags, ImGuiTreeNodeFlags_SpanFullWidth); + ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanTextWidth", &tree_node_flags, ImGuiTreeNodeFlags_SpanTextWidth); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &tree_node_flags, ImGuiTreeNodeFlags_SpanAllColumns); HelpMarker("See \"Columns flags\" section to configure how indentation is applied to individual columns."); @@ -5328,6 +5367,17 @@ static void ShowDemoWindowTables() ImGui::SliderInt("Frozen rows", &frozen_rows, 0, 2); ImGui::CheckboxFlags("Disable header contributing to column width", &column_flags, ImGuiTableColumnFlags_NoHeaderWidth); + if (ImGui::TreeNode("Style settings")) + { + ImGui::SameLine(); + HelpMarker("Giving access to some ImGuiStyle value in this demo for convenience."); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); + ImGui::SliderAngle("style.TableAngledHeadersAngle", &ImGui::GetStyle().TableAngledHeadersAngle, -50.0f, +50.0f); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); + ImGui::SliderFloat2("style.TableAngledHeadersTextAlign", (float*)&ImGui::GetStyle().TableAngledHeadersTextAlign, 0.0f, 1.0f, "%.2f"); + ImGui::TreePop(); + } + if (ImGui::BeginTable("table_angled_headers", columns_count, table_flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 12))) { ImGui::TableSetupColumn(column_names[0], ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_NoReorder); @@ -5479,6 +5529,7 @@ static void ShowDemoWindowTables() HelpMarker("Multiple tables with the same identifier will share their settings, width, visibility, order etc."); static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings; + ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); ImGui::CheckboxFlags("ImGuiTableFlags_SizingFixedFit", &flags, ImGuiTableFlags_SizingFixedFit); ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn); @@ -6138,12 +6189,14 @@ static void ShowDemoWindowInputs() // Display inputs submitted to ImGuiIO IMGUI_DEMO_MARKER("Inputs & Focus/Inputs"); ImGui::SetNextItemOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode("Inputs")) + bool inputs_opened = ImGui::TreeNode("Inputs"); + ImGui::SameLine(); + HelpMarker( + "This is a simplified view. See more detailed input state:\n" + "- in 'Tools->Metrics/Debugger->Inputs'.\n" + "- in 'Tools->Debug Log->IO'."); + if (inputs_opened) { - HelpMarker( - "This is a simplified view. See more detailed input state:\n" - "- in 'Tools->Metrics/Debugger->Inputs'.\n" - "- in 'Tools->Debug Log->IO'."); if (ImGui::IsMousePosValid()) ImGui::Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y); else @@ -6174,15 +6227,17 @@ static void ShowDemoWindowInputs() // Display ImGuiIO output flags IMGUI_DEMO_MARKER("Inputs & Focus/Outputs"); ImGui::SetNextItemOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode("Outputs")) + bool outputs_opened = ImGui::TreeNode("Outputs"); + ImGui::SameLine(); + HelpMarker( + "The value of io.WantCaptureMouse and io.WantCaptureKeyboard are normally set by Dear ImGui " + "to instruct your application of how to route inputs. Typically, when a value is true, it means " + "Dear ImGui wants the corresponding inputs and we expect the underlying application to ignore them.\n\n" + "The most typical case is: when hovering a window, Dear ImGui set io.WantCaptureMouse to true, " + "and underlying application should ignore mouse inputs (in practice there are many and more subtle " + "rules leading to how those flags are set)."); + if (outputs_opened) { - HelpMarker( - "The value of io.WantCaptureMouse and io.WantCaptureKeyboard are normally set by Dear ImGui " - "to instruct your application of how to route inputs. Typically, when a value is true, it means " - "Dear ImGui wants the corresponding inputs and we expect the underlying application to ignore them.\n\n" - "The most typical case is: when hovering a window, Dear ImGui set io.WantCaptureMouse to true, " - "and underlying application should ignore mouse inputs (in practice there are many and more subtle " - "rules leading to how those flags are set)."); ImGui::Text("io.WantCaptureMouse: %d", io.WantCaptureMouse); ImGui::Text("io.WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose); ImGui::Text("io.WantCaptureKeyboard: %d", io.WantCaptureKeyboard); @@ -6216,6 +6271,102 @@ static void ShowDemoWindowInputs() ImGui::TreePop(); } + // Demonstrate using Shortcut() and Routing Policies. + // The general flow is: + // - Code interested in a chord (e.g. "Ctrl+A") declares their intent. + // - Multiple locations may be interested in same chord! Routing helps find a winner. + // - Every frame, we resolve all claims and assign one owner if the modifiers are matching. + // - The lower-level function is 'bool SetShortcutRouting()', returns true when caller got the route. + // - Most of the times, SetShortcutRouting() is not called directly. User mostly calls Shortcut() with routing flags. + // - If you call Shortcut() WITHOUT any routing option, it uses ImGuiInputFlags_RouteFocused. + // TL;DR: Most uses will simply be: + // - Shortcut(ImGuiMod_Ctrl | ImGuiKey_A); // Use ImGuiInputFlags_RouteFocused policy. + IMGUI_DEMO_MARKER("Inputs & Focus/Shortcuts"); + if (ImGui::TreeNode("Shortcuts")) + { + static ImGuiInputFlags route_options = ImGuiInputFlags_Repeat; + static ImGuiInputFlags route_type = ImGuiInputFlags_RouteFocused; + ImGui::CheckboxFlags("ImGuiInputFlags_Repeat", &route_options, ImGuiInputFlags_Repeat); + ImGui::RadioButton("ImGuiInputFlags_RouteActive", &route_type, ImGuiInputFlags_RouteActive); + ImGui::RadioButton("ImGuiInputFlags_RouteFocused (default)", &route_type, ImGuiInputFlags_RouteFocused); + ImGui::RadioButton("ImGuiInputFlags_RouteGlobal", &route_type, ImGuiInputFlags_RouteGlobal); + ImGui::Indent(); + ImGui::BeginDisabled(route_type != ImGuiInputFlags_RouteGlobal); + ImGui::CheckboxFlags("ImGuiInputFlags_RouteOverFocused", &route_options, ImGuiInputFlags_RouteOverFocused); + ImGui::CheckboxFlags("ImGuiInputFlags_RouteOverActive", &route_options, ImGuiInputFlags_RouteOverActive); + ImGui::CheckboxFlags("ImGuiInputFlags_RouteUnlessBgFocused", &route_options, ImGuiInputFlags_RouteUnlessBgFocused); + ImGui::EndDisabled(); + ImGui::Unindent(); + ImGui::RadioButton("ImGuiInputFlags_RouteAlways", &route_type, ImGuiInputFlags_RouteAlways); + ImGuiInputFlags flags = route_type | route_options; // Merged flags + if (route_type != ImGuiInputFlags_RouteGlobal) + flags &= ~(ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused); + + ImGui::SeparatorText("Using SetNextItemShortcut()"); + ImGui::Text("Ctrl+S"); + ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_S, flags | ImGuiInputFlags_Tooltip); + ImGui::Button("Save"); + ImGui::Text("Alt+F"); + ImGui::SetNextItemShortcut(ImGuiMod_Alt | ImGuiKey_F, flags | ImGuiInputFlags_Tooltip); + static float f = 0.5f; + ImGui::SliderFloat("Factor", &f, 0.0f, 1.0f); + + ImGui::SeparatorText("Using Shortcut()"); + const float line_height = ImGui::GetTextLineHeightWithSpacing(); + const ImGuiKeyChord key_chord = ImGuiMod_Ctrl | ImGuiKey_A; + + ImGui::Text("Ctrl+A"); + ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "..."); + + ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 0.0f, 1.0f, 0.1f)); + + ImGui::BeginChild("WindowA", ImVec2(-FLT_MIN, line_height * 14), true); + ImGui::Text("Press CTRL+A and see who receives it!"); + ImGui::Separator(); + + // 1: Window polling for CTRL+A + ImGui::Text("(in WindowA)"); + ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "..."); + + // 2: InputText also polling for CTRL+A: it always uses _RouteFocused internally (gets priority when active) + // (Commmented because the owner-aware version of Shortcut() is still in imgui_internal.h) + //char str[16] = "Press CTRL+A"; + //ImGui::Spacing(); + //ImGui::InputText("InputTextB", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly); + //ImGuiID item_id = ImGui::GetItemID(); + //ImGui::SameLine(); HelpMarker("Internal widgets always use _RouteFocused"); + //ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, item_id) ? "PRESSED" : "..."); + + // 3: Dummy child is not claiming the route: focusing them shouldn't steal route away from WindowA + ImGui::BeginChild("ChildD", ImVec2(-FLT_MIN, line_height * 4), true); + ImGui::Text("(in ChildD: not using same Shortcut)"); + ImGui::Text("IsWindowFocused: %d", ImGui::IsWindowFocused()); + ImGui::EndChild(); + + // 4: Child window polling for CTRL+A. It is deeper than WindowA and gets priority when focused. + ImGui::BeginChild("ChildE", ImVec2(-FLT_MIN, line_height * 4), true); + ImGui::Text("(in ChildE: using same Shortcut)"); + ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "..."); + ImGui::EndChild(); + + // 5: In a popup + if (ImGui::Button("Open Popup")) + ImGui::OpenPopup("PopupF"); + if (ImGui::BeginPopup("PopupF")) + { + ImGui::Text("(in PopupF)"); + ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "..."); + // (Commmented because the owner-aware version of Shortcut() is still in imgui_internal.h) + //ImGui::InputText("InputTextG", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly); + //ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, ImGui::GetItemID()) ? "PRESSED" : "..."); + ImGui::EndPopup(); + } + ImGui::EndChild(); + ImGui::PopStyleColor(); + + ImGui::TreePop(); + } + // Display mouse cursors IMGUI_DEMO_MARKER("Inputs & Focus/Mouse Cursors"); if (ImGui::TreeNode("Mouse Cursors")) @@ -6351,7 +6502,7 @@ void ImGui::ShowAboutWindow(bool* p_open) ImGui::Separator(); ImGui::Text("By Omar Cornut and all Dear ImGui contributors."); ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information."); - ImGui::Text("If your company uses this, please consider sponsoring the project!"); + ImGui::Text("If your company uses this, please consider funding the project."); static bool show_config_info = false; ImGui::Checkbox("Config/Build Information", &show_config_info); @@ -6616,12 +6767,13 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SeparatorText("Tables"); ImGui::SliderFloat2("CellPadding", (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderAngle("TableAngledHeadersAngle", &style.TableAngledHeadersAngle, -50.0f, +50.0f); + ImGui::SliderFloat2("TableAngledHeadersTextAlign", (float*)&style.TableAngledHeadersTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SeparatorText("Widgets"); ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); int window_menu_button_position = style.WindowMenuButtonPosition + 1; if (ImGui::Combo("WindowMenuButtonPosition", (int*)&window_menu_button_position, "None\0Left\0Right\0")) - style.WindowMenuButtonPosition = window_menu_button_position - 1; + style.WindowMenuButtonPosition = (ImGuiDir)(window_menu_button_position - 1); ImGui::Combo("ColorButtonPosition", (int*)&style.ColorButtonPosition, "Left\0Right\0"); ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); HelpMarker("Alignment applies when a button is larger than its text content."); @@ -6646,7 +6798,8 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) } ImGui::SeparatorText("Misc"); - ImGui::SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f"); ImGui::SameLine(); HelpMarker("Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured)."); + ImGui::SliderFloat2("DisplayWindowPadding", (float*)&style.DisplayWindowPadding, 0.0f, 30.0f, "%.0f"); ImGui::SameLine(); HelpMarker("Apply to regular windows: amount which we enforce to keep visible when moving near edges of your screen."); + ImGui::SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f"); ImGui::SameLine(); HelpMarker("Apply to every windows, menus, popups, tooltips: amount where we avoid displaying contents. Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured)."); ImGui::EndTabItem(); } @@ -7079,6 +7232,7 @@ struct ExampleAppConsole } // Options, Filter + ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_O, ImGuiInputFlags_Tooltip); if (ImGui::Button("Options")) ImGui::OpenPopup("Options"); ImGui::SameLine(); @@ -7087,7 +7241,7 @@ struct ExampleAppConsole // Reserve enough left-over height for 1 separator + 1 input text const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); - if (ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar)) + if (ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NavFlattened)) { if (ImGui::BeginPopupContextWindow()) { @@ -7789,7 +7943,7 @@ static void ShowExampleAppConstrainedResize(bool* p_open) if (type == 2) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 0), ImVec2(-1, FLT_MAX)); // Resize vertical + lock current width if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1), ImVec2(FLT_MAX, -1)); // Resize horizontal + lock current height if (type == 4) ImGui::SetNextWindowSizeConstraints(ImVec2(400, -1), ImVec2(500, -1)); // Width Between and 400 and 500 - if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 500), ImVec2(-1, FLT_MAX)); // Height at least 400 + if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 400), ImVec2(-1, FLT_MAX)); // Height at least 400 if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::AspectRatio, (void*)&aspect_ratio); // Aspect ratio if (type == 7) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square); // Always Square if (type == 8) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)&fixed_step); // Fixed Step @@ -7964,6 +8118,14 @@ static void ShowExampleAppWindowTitles(bool*) // [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering() //----------------------------------------------------------------------------- +// Add a |_| looking shape +static void PathConcaveShape(ImDrawList* draw_list, float x, float y, float sz) +{ + const ImVec2 pos_norms[] = { { 0.0f, 0.0f }, { 0.3f, 0.0f }, { 0.3f, 0.7f }, { 0.7f, 0.7f }, { 0.7f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 1.0f } }; + for (const ImVec2& p : pos_norms) + draw_list->PathLineTo(ImVec2(x + 0.5f + (int)(sz * p.x), y + 0.5f + (int)(sz * p.y))); +} + // Demonstrate using the low-level ImDrawList to draw custom shapes. static void ShowExampleAppCustomRendering(bool* p_open) { @@ -8047,12 +8209,14 @@ static void ShowExampleAppCustomRendering(bool* p_open) float th = (n == 0) ? 1.0f : thickness; draw_list->AddNgon(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, ngon_sides, th); x += sz + spacing; // N-gon draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, circle_segments, th); x += sz + spacing; // Circle - draw_list->AddEllipse(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, sz*0.3f, col, -0.3f, circle_segments, th); x += sz + spacing; // Ellipse + draw_list->AddEllipse(ImVec2(x + sz*0.5f, y + sz*0.5f), ImVec2(sz*0.5f, sz*0.3f), col, -0.3f, circle_segments, th); x += sz + spacing; // Ellipse draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, ImDrawFlags_None, th); x += sz + spacing; // Square draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, ImDrawFlags_None, th); x += sz + spacing; // Square with all rounded corners draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, corners_tl_br, th); x += sz + spacing; // Square with two rounded corners draw_list->AddTriangle(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col, th);x += sz + spacing; // Triangle //draw_list->AddTriangle(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col, th);x+= sz*0.4f + spacing; // Thin triangle + PathConcaveShape(draw_list, x, y, sz); draw_list->PathStroke(col, ImDrawFlags_Closed, th); x += sz + spacing; // Concave Shape + //draw_list->AddPolyline(concave_shape, IM_ARRAYSIZE(concave_shape), col, ImDrawFlags_Closed, th); draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th); x += sz + spacing; // Diagonal line @@ -8076,12 +8240,13 @@ static void ShowExampleAppCustomRendering(bool* p_open) // Filled shapes draw_list->AddNgonFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, ngon_sides); x += sz + spacing; // N-gon draw_list->AddCircleFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, circle_segments); x += sz + spacing; // Circle - draw_list->AddEllipseFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, sz * 0.3f, col, -0.3f, circle_segments); x += sz + spacing;// Ellipse + draw_list->AddEllipseFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), ImVec2(sz * 0.5f, sz * 0.3f), col, -0.3f, circle_segments); x += sz + spacing;// Ellipse draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col); x += sz + spacing; // Square draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f); x += sz + spacing; // Square with all rounded corners draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_tl_br); x += sz + spacing; // Square with two rounded corners draw_list->AddTriangleFilled(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col); x += sz + spacing; // Triangle //draw_list->AddTriangleFilled(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col); x += sz*0.4f + spacing; // Thin triangle + PathConcaveShape(draw_list, x, y, sz); draw_list->PathFillConcave(col); x += sz + spacing; // Concave shape draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col); x += sz + spacing; // Horizontal line (faster than AddLine, but only handle integer thickness) draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col); x += spacing * 2.0f;// Vertical line (faster than AddLine, but only handle integer thickness) draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col); x += sz; // Pixel (faster than AddLine) @@ -8097,15 +8262,10 @@ static void ShowExampleAppCustomRendering(bool* p_open) draw_list->PathFillConvex(col); x += sz + spacing; - // Cubic Bezier Curve (4 control points): this is concave so not drawing it yet - //draw_list->PathLineTo(ImVec2(x + cp4[0].x, y + cp4[0].y)); - //draw_list->PathBezierCubicCurveTo(ImVec2(x + cp4[1].x, y + cp4[1].y), ImVec2(x + cp4[2].x, y + cp4[2].y), ImVec2(x + cp4[3].x, y + cp4[3].y), curve_segments); - //draw_list->PathFillConvex(col); - //x += sz + spacing; - draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x + sz, y + sz), IM_COL32(0, 0, 0, 255), IM_COL32(255, 0, 0, 255), IM_COL32(255, 255, 0, 255), IM_COL32(0, 255, 0, 255)); + x += sz + spacing; - ImGui::Dummy(ImVec2((sz + spacing) * 12.2f, (sz + spacing) * 3.0f)); + ImGui::Dummy(ImVec2((sz + spacing) * 13.2f, (sz + spacing) * 3.0f)); ImGui::PopItemWidth(); ImGui::EndTabItem(); } @@ -8275,92 +8435,119 @@ static void ShowExampleAppCustomRendering(bool* p_open) // Simplified structure to mimic a Document model struct MyDocument { - const char* Name; // Document title + char Name[32]; // Document title + int UID; // Unique ID (necessary as we can change title) bool Open; // Set when open (we keep an array of all available documents to simplify demo code!) bool OpenPrev; // Copy of Open from last update. bool Dirty; // Set when the document has been modified - bool WantClose; // Set when the document ImVec4 Color; // An arbitrary variable associated to the document - MyDocument(const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f)) + MyDocument(int uid, const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f)) { - Name = name; + UID = uid; + snprintf(Name, sizeof(Name), "%s", name); Open = OpenPrev = open; Dirty = false; - WantClose = false; Color = color; } void DoOpen() { Open = true; } - void DoQueueClose() { WantClose = true; } void DoForceClose() { Open = false; Dirty = false; } void DoSave() { Dirty = false; } +}; + +struct ExampleAppDocuments +{ + ImVector Documents; + ImVector CloseQueue; + MyDocument* RenamingDoc = NULL; + bool RenamingStarted = false; + + ExampleAppDocuments() + { + Documents.push_back(MyDocument(0, "Lettuce", true, ImVec4(0.4f, 0.8f, 0.4f, 1.0f))); + Documents.push_back(MyDocument(1, "Eggplant", true, ImVec4(0.8f, 0.5f, 1.0f, 1.0f))); + Documents.push_back(MyDocument(2, "Carrot", true, ImVec4(1.0f, 0.8f, 0.5f, 1.0f))); + Documents.push_back(MyDocument(3, "Tomato", false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f))); + Documents.push_back(MyDocument(4, "A Rather Long Title", false, ImVec4(0.4f, 0.8f, 0.8f, 1.0f))); + Documents.push_back(MyDocument(5, "Some Document", false, ImVec4(0.8f, 0.8f, 1.0f, 1.0f))); + } + + // As we allow to change document name, we append a never-changing document ID so tabs are stable + void GetTabName(MyDocument* doc, char* out_buf, size_t out_buf_size) + { + snprintf(out_buf, out_buf_size, "%s###doc%d", doc->Name, doc->UID); + } // Display placeholder contents for the Document - static void DisplayContents(MyDocument* doc) + void DisplayDocContents(MyDocument* doc) { ImGui::PushID(doc); ImGui::Text("Document \"%s\"", doc->Name); ImGui::PushStyleColor(ImGuiCol_Text, doc->Color); ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."); ImGui::PopStyleColor(); - if (ImGui::Button("Modify", ImVec2(100, 0))) + + ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_R, ImGuiInputFlags_Tooltip); + if (ImGui::Button("Rename..")) + { + RenamingDoc = doc; + RenamingStarted = true; + } + ImGui::SameLine(); + + ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_M, ImGuiInputFlags_Tooltip); + if (ImGui::Button("Modify")) doc->Dirty = true; + ImGui::SameLine(); - if (ImGui::Button("Save", ImVec2(100, 0))) + ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_S, ImGuiInputFlags_Tooltip); + if (ImGui::Button("Save")) doc->DoSave(); + + ImGui::SameLine(); + ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_W, ImGuiInputFlags_Tooltip); + if (ImGui::Button("Close")) + CloseQueue.push_back(doc); ImGui::ColorEdit3("color", &doc->Color.x); // Useful to test drag and drop and hold-dragged-to-open-tab behavior. ImGui::PopID(); } // Display context menu for the Document - static void DisplayContextMenu(MyDocument* doc) + void DisplayDocContextMenu(MyDocument* doc) { if (!ImGui::BeginPopupContextItem()) return; char buf[256]; sprintf(buf, "Save %s", doc->Name); - if (ImGui::MenuItem(buf, "CTRL+S", false, doc->Open)) + if (ImGui::MenuItem(buf, "Ctrl+S", false, doc->Open)) doc->DoSave(); - if (ImGui::MenuItem("Close", "CTRL+W", false, doc->Open)) - doc->DoQueueClose(); + if (ImGui::MenuItem("Rename...", "Ctrl+R", false, doc->Open)) + RenamingDoc = doc; + if (ImGui::MenuItem("Close", "Ctrl+W", false, doc->Open)) + CloseQueue.push_back(doc); ImGui::EndPopup(); } -}; - -struct ExampleAppDocuments -{ - ImVector Documents; - ExampleAppDocuments() + // [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface. + // If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo, + // as opposed to clicking on the regular tab closing button) and stops being submitted, it will take a frame for + // the tab bar to notice its absence. During this frame there will be a gap in the tab bar, and if the tab that has + // disappeared was the selected one, the tab bar will report no selected tab during the frame. This will effectively + // give the impression of a flicker for one frame. + // We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch. + // Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag. + void NotifyOfDocumentsClosedElsewhere() { - Documents.push_back(MyDocument("Lettuce", true, ImVec4(0.4f, 0.8f, 0.4f, 1.0f))); - Documents.push_back(MyDocument("Eggplant", true, ImVec4(0.8f, 0.5f, 1.0f, 1.0f))); - Documents.push_back(MyDocument("Carrot", true, ImVec4(1.0f, 0.8f, 0.5f, 1.0f))); - Documents.push_back(MyDocument("Tomato", false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f))); - Documents.push_back(MyDocument("A Rather Long Title", false)); - Documents.push_back(MyDocument("Some Document", false)); + for (MyDocument& doc : Documents) + { + if (!doc.Open && doc.OpenPrev) + ImGui::SetTabItemClosed(doc.Name); + doc.OpenPrev = doc.Open; + } } }; -// [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface. -// If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo, -// as opposed to clicking on the regular tab closing button) and stops being submitted, it will take a frame for -// the tab bar to notice its absence. During this frame there will be a gap in the tab bar, and if the tab that has -// disappeared was the selected one, the tab bar will report no selected tab during the frame. This will effectively -// give the impression of a flicker for one frame. -// We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch. -// Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag. -static void NotifyOfDocumentsClosedElsewhere(ExampleAppDocuments& app) -{ - for (MyDocument& doc : app.Documents) - { - if (!doc.Open && doc.OpenPrev) - ImGui::SetTabItemClosed(doc.Name); - doc.OpenPrev = doc.Open; - } -} - void ShowExampleAppDocuments(bool* p_open) { static ExampleAppDocuments app; @@ -8394,8 +8581,8 @@ void ShowExampleAppDocuments(bool* p_open) } if (ImGui::MenuItem("Close All Documents", NULL, false, open_count > 0)) for (MyDocument& doc : app.Documents) - doc.DoQueueClose(); - if (ImGui::MenuItem("Exit", "Ctrl+F4") && p_open) + app.CloseQueue.push_back(&doc); + if (ImGui::MenuItem("Exit") && p_open) *p_open = false; ImGui::EndMenu(); } @@ -8433,7 +8620,7 @@ void ShowExampleAppDocuments(bool* p_open) if (ImGui::BeginTabBar("##tabs", tab_bar_flags)) { if (opt_reorderable) - NotifyOfDocumentsClosedElsewhere(app); + app.NotifyOfDocumentsClosedElsewhere(); // [DEBUG] Stress tests //if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1; // [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on. @@ -8445,20 +8632,23 @@ void ShowExampleAppDocuments(bool* p_open) if (!doc.Open) continue; + // As we allow to change document name, we append a never-changing document id so tabs are stable + char doc_name_buf[64]; + app.GetTabName(&doc, doc_name_buf, sizeof(doc_name_buf)); ImGuiTabItemFlags tab_flags = (doc.Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0); - bool visible = ImGui::BeginTabItem(doc.Name, &doc.Open, tab_flags); + bool visible = ImGui::BeginTabItem(doc_name_buf, &doc.Open, tab_flags); // Cancel attempt to close when unsaved add to save queue so we can display a popup. if (!doc.Open && doc.Dirty) { doc.Open = true; - doc.DoQueueClose(); + app.CloseQueue.push_back(&doc); } - MyDocument::DisplayContextMenu(&doc); + app.DisplayDocContextMenu(&doc); if (visible) { - MyDocument::DisplayContents(&doc); + app.DisplayDocContents(&doc); ImGui::EndTabItem(); } } @@ -8467,33 +8657,44 @@ void ShowExampleAppDocuments(bool* p_open) } } - // Update closing queue - static ImVector close_queue; - if (close_queue.empty()) + // Display renaming UI + if (app.RenamingDoc != NULL) { - // Close queue is locked once we started a popup - for (MyDocument& doc : app.Documents) - if (doc.WantClose) + if (app.RenamingStarted) + ImGui::OpenPopup("Rename"); + if (ImGui::BeginPopup("Rename")) + { + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 30); + if (ImGui::InputText("###Name", app.RenamingDoc->Name, IM_ARRAYSIZE(app.RenamingDoc->Name), ImGuiInputTextFlags_EnterReturnsTrue)) { - doc.WantClose = false; - close_queue.push_back(&doc); + ImGui::CloseCurrentPopup(); + app.RenamingDoc = NULL; } + if (app.RenamingStarted) + ImGui::SetKeyboardFocusHere(-1); + ImGui::EndPopup(); + } + else + { + app.RenamingDoc = NULL; + } + app.RenamingStarted = false; } // Display closing confirmation UI - if (!close_queue.empty()) + if (!app.CloseQueue.empty()) { int close_queue_unsaved_documents = 0; - for (int n = 0; n < close_queue.Size; n++) - if (close_queue[n]->Dirty) + for (int n = 0; n < app.CloseQueue.Size; n++) + if (app.CloseQueue[n]->Dirty) close_queue_unsaved_documents++; if (close_queue_unsaved_documents == 0) { // Close documents when all are unsaved - for (int n = 0; n < close_queue.Size; n++) - close_queue[n]->DoForceClose(); - close_queue.clear(); + for (int n = 0; n < app.CloseQueue.Size; n++) + app.CloseQueue[n]->DoForceClose(); + app.CloseQueue.clear(); } else { @@ -8504,37 +8705,35 @@ void ShowExampleAppDocuments(bool* p_open) ImGui::Text("Save change to the following items?"); float item_height = ImGui::GetTextLineHeightWithSpacing(); if (ImGui::BeginChild(ImGui::GetID("frame"), ImVec2(-FLT_MIN, 6.25f * item_height), ImGuiChildFlags_FrameStyle)) - { - for (int n = 0; n < close_queue.Size; n++) - if (close_queue[n]->Dirty) - ImGui::Text("%s", close_queue[n]->Name); - } + for (MyDocument* doc : app.CloseQueue) + if (doc->Dirty) + ImGui::Text("%s", doc->Name); ImGui::EndChild(); ImVec2 button_size(ImGui::GetFontSize() * 7.0f, 0.0f); if (ImGui::Button("Yes", button_size)) { - for (int n = 0; n < close_queue.Size; n++) + for (MyDocument* doc : app.CloseQueue) { - if (close_queue[n]->Dirty) - close_queue[n]->DoSave(); - close_queue[n]->DoForceClose(); + if (doc->Dirty) + doc->DoSave(); + doc->DoForceClose(); } - close_queue.clear(); + app.CloseQueue.clear(); ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("No", button_size)) { - for (int n = 0; n < close_queue.Size; n++) - close_queue[n]->DoForceClose(); - close_queue.clear(); + for (MyDocument* doc : app.CloseQueue) + doc->DoForceClose(); + app.CloseQueue.clear(); ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Cancel", button_size)) { - close_queue.clear(); + app.CloseQueue.clear(); ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); @@ -8555,4 +8754,4 @@ void ImGui::ShowStyleEditor(ImGuiStyle*) {} #endif -#endif // #ifndef IMGUI_DISABLE +#endif // #ifndef IMGUI_DISABLE \ No newline at end of file diff --git a/platforms/desktop-shared/imgui/imgui_draw.cpp b/platforms/desktop-shared/imgui/imgui_draw.cpp index 1319a6e..cf9304c 100644 --- a/platforms/desktop-shared/imgui/imgui_draw.cpp +++ b/platforms/desktop-shared/imgui/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.4 +// dear imgui, v1.90.8 // (drawing and font code) /* @@ -8,6 +8,7 @@ Index of this file: // [SECTION] STB libraries implementation // [SECTION] Style functions // [SECTION] ImDrawList +// [SECTION] ImTriangulator, ImDrawList concave polygon fill // [SECTION] ImDrawListSplitter // [SECTION] ImDrawData // [SECTION] Helpers ShadeVertsXXX functions @@ -64,6 +65,7 @@ Index of this file: #pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision #pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access #elif defined(__GNUC__) #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used @@ -383,6 +385,7 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error) } // Initialize before use in a new frame. We always have a command ready in the buffer. +// In the majority of cases, you would want to call PushClipRect() and PushTextureID() after this. void ImDrawList::_ResetForNewFrame() { // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory. @@ -1217,10 +1220,10 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa } } -void ImDrawList::PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments) +void ImDrawList::PathEllipticalArcTo(const ImVec2& center, const ImVec2& radius, float rot, float a_min, float a_max, int num_segments) { if (num_segments <= 0) - num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here. + num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here. _Path.reserve(_Path.Size + (num_segments + 1)); @@ -1229,11 +1232,10 @@ void ImDrawList::PathEllipticalArcTo(const ImVec2& center, float radius_x, float for (int i = 0; i <= num_segments; i++) { const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min); - ImVec2 point(ImCos(a) * radius_x, ImSin(a) * radius_y); - const float rel_x = (point.x * cos_rot) - (point.y * sin_rot); - const float rel_y = (point.x * sin_rot) + (point.y * cos_rot); - point.x = rel_x + center.x; - point.y = rel_y + center.y; + ImVec2 point(ImCos(a) * radius.x, ImSin(a) * radius.y); + const ImVec2 rel((point.x * cos_rot) - (point.y * sin_rot), (point.x * sin_rot) + (point.y * cos_rot)); + point.x = rel.x + center.x; + point.y = rel.y + center.y; _Path.push_back(point); } } @@ -1558,31 +1560,31 @@ void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, in } // Ellipse -void ImDrawList::AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot, int num_segments, float thickness) +void ImDrawList::AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments, float thickness) { if ((col & IM_COL32_A_MASK) == 0) return; if (num_segments <= 0) - num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here. + num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here. // Because we are filling a closed shape we remove 1 from the count of segments/points const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments; - PathEllipticalArcTo(center, radius_x, radius_y, rot, 0.0f, a_max, num_segments - 1); + PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1); PathStroke(col, true, thickness); } -void ImDrawList::AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot, int num_segments) +void ImDrawList::AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments) { if ((col & IM_COL32_A_MASK) == 0) return; if (num_segments <= 0) - num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here. + num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here. // Because we are filling a closed shape we remove 1 from the count of segments/points const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments; - PathEllipticalArcTo(center, radius_x, radius_y, rot, 0.0f, a_max, num_segments - 1); + PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1); PathFillConvex(col); } @@ -1613,10 +1615,11 @@ void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, if ((col & IM_COL32_A_MASK) == 0) return; + // Accept null ranges + if (text_begin == text_end || text_begin[0] == 0) + return; if (text_end == NULL) text_end = text_begin + strlen(text_begin); - if (text_begin == text_end) - return; // Pull default font/size from the shared ImDrawListSharedData instance if (font == NULL) @@ -1700,6 +1703,316 @@ void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_mi PopTextureID(); } +//----------------------------------------------------------------------------- +// [SECTION] ImTriangulator, ImDrawList concave polygon fill +//----------------------------------------------------------------------------- +// Triangulate concave polygons. Based on "Triangulation by Ear Clipping" paper, O(N^2) complexity. +// Reference: https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf +// Provided as a convenience for user but not used by main library. +//----------------------------------------------------------------------------- +// - ImTriangulator [Internal] +// - AddConcavePolyFilled() +//----------------------------------------------------------------------------- + +enum ImTriangulatorNodeType +{ + ImTriangulatorNodeType_Convex, + ImTriangulatorNodeType_Ear, + ImTriangulatorNodeType_Reflex +}; + +struct ImTriangulatorNode +{ + ImTriangulatorNodeType Type; + int Index; + ImVec2 Pos; + ImTriangulatorNode* Next; + ImTriangulatorNode* Prev; + + void Unlink() { Next->Prev = Prev; Prev->Next = Next; } +}; + +struct ImTriangulatorNodeSpan +{ + ImTriangulatorNode** Data = NULL; + int Size = 0; + + void push_back(ImTriangulatorNode* node) { Data[Size++] = node; } + void find_erase_unsorted(int idx) { for (int i = Size - 1; i >= 0; i--) if (Data[i]->Index == idx) { Data[i] = Data[Size - 1]; Size--; return; } } +}; + +struct ImTriangulator +{ + static int EstimateTriangleCount(int points_count) { return (points_count < 3) ? 0 : points_count - 2; } + static int EstimateScratchBufferSize(int points_count) { return sizeof(ImTriangulatorNode) * points_count + sizeof(ImTriangulatorNode*) * points_count * 2; } + + void Init(const ImVec2* points, int points_count, void* scratch_buffer); + void GetNextTriangle(unsigned int out_triangle[3]); // Return relative indexes for next triangle + + // Internal functions + void BuildNodes(const ImVec2* points, int points_count); + void BuildReflexes(); + void BuildEars(); + void FlipNodeList(); + bool IsEar(int i0, int i1, int i2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2) const; + void ReclassifyNode(ImTriangulatorNode* node); + + // Internal members + int _TrianglesLeft = 0; + ImTriangulatorNode* _Nodes = NULL; + ImTriangulatorNodeSpan _Ears; + ImTriangulatorNodeSpan _Reflexes; +}; + +// Distribute storage for nodes, ears and reflexes. +// FIXME-OPT: if everything is convex, we could report it to caller and let it switch to an convex renderer +// (this would require first building reflexes to bail to convex if empty, without even building nodes) +void ImTriangulator::Init(const ImVec2* points, int points_count, void* scratch_buffer) +{ + IM_ASSERT(scratch_buffer != NULL && points_count >= 3); + _TrianglesLeft = EstimateTriangleCount(points_count); + _Nodes = (ImTriangulatorNode*)scratch_buffer; // points_count x Node + _Ears.Data = (ImTriangulatorNode**)(_Nodes + points_count); // points_count x Node* + _Reflexes.Data = (ImTriangulatorNode**)(_Nodes + points_count) + points_count; // points_count x Node* + BuildNodes(points, points_count); + BuildReflexes(); + BuildEars(); +} + +void ImTriangulator::BuildNodes(const ImVec2* points, int points_count) +{ + for (int i = 0; i < points_count; i++) + { + _Nodes[i].Type = ImTriangulatorNodeType_Convex; + _Nodes[i].Index = i; + _Nodes[i].Pos = points[i]; + _Nodes[i].Next = _Nodes + i + 1; + _Nodes[i].Prev = _Nodes + i - 1; + } + _Nodes[0].Prev = _Nodes + points_count - 1; + _Nodes[points_count - 1].Next = _Nodes; +} + +void ImTriangulator::BuildReflexes() +{ + ImTriangulatorNode* n1 = _Nodes; + for (int i = _TrianglesLeft; i >= 0; i--, n1 = n1->Next) + { + if (ImTriangleIsClockwise(n1->Prev->Pos, n1->Pos, n1->Next->Pos)) + continue; + n1->Type = ImTriangulatorNodeType_Reflex; + _Reflexes.push_back(n1); + } +} + +void ImTriangulator::BuildEars() +{ + ImTriangulatorNode* n1 = _Nodes; + for (int i = _TrianglesLeft; i >= 0; i--, n1 = n1->Next) + { + if (n1->Type != ImTriangulatorNodeType_Convex) + continue; + if (!IsEar(n1->Prev->Index, n1->Index, n1->Next->Index, n1->Prev->Pos, n1->Pos, n1->Next->Pos)) + continue; + n1->Type = ImTriangulatorNodeType_Ear; + _Ears.push_back(n1); + } +} + +void ImTriangulator::GetNextTriangle(unsigned int out_triangle[3]) +{ + if (_Ears.Size == 0) + { + FlipNodeList(); + + ImTriangulatorNode* node = _Nodes; + for (int i = _TrianglesLeft; i >= 0; i--, node = node->Next) + node->Type = ImTriangulatorNodeType_Convex; + _Reflexes.Size = 0; + BuildReflexes(); + BuildEars(); + + // If we still don't have ears, it means geometry is degenerated. + if (_Ears.Size == 0) + { + // Return first triangle available, mimicking the behavior of convex fill. + IM_ASSERT(_TrianglesLeft > 0); // Geometry is degenerated + _Ears.Data[0] = _Nodes; + _Ears.Size = 1; + } + } + + ImTriangulatorNode* ear = _Ears.Data[--_Ears.Size]; + out_triangle[0] = ear->Prev->Index; + out_triangle[1] = ear->Index; + out_triangle[2] = ear->Next->Index; + + ear->Unlink(); + if (ear == _Nodes) + _Nodes = ear->Next; + + ReclassifyNode(ear->Prev); + ReclassifyNode(ear->Next); + _TrianglesLeft--; +} + +void ImTriangulator::FlipNodeList() +{ + ImTriangulatorNode* prev = _Nodes; + ImTriangulatorNode* temp = _Nodes; + ImTriangulatorNode* current = _Nodes->Next; + prev->Next = prev; + prev->Prev = prev; + while (current != _Nodes) + { + temp = current->Next; + + current->Next = prev; + prev->Prev = current; + _Nodes->Next = current; + current->Prev = _Nodes; + + prev = current; + current = temp; + } + _Nodes = prev; +} + +// A triangle is an ear is no other vertex is inside it. We can test reflexes vertices only (see reference algorithm) +bool ImTriangulator::IsEar(int i0, int i1, int i2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2) const +{ + ImTriangulatorNode** p_end = _Reflexes.Data + _Reflexes.Size; + for (ImTriangulatorNode** p = _Reflexes.Data; p < p_end; p++) + { + ImTriangulatorNode* reflex = *p; + if (reflex->Index != i0 && reflex->Index != i1 && reflex->Index != i2) + if (ImTriangleContainsPoint(v0, v1, v2, reflex->Pos)) + return false; + } + return true; +} + +void ImTriangulator::ReclassifyNode(ImTriangulatorNode* n1) +{ + // Classify node + ImTriangulatorNodeType type; + const ImTriangulatorNode* n0 = n1->Prev; + const ImTriangulatorNode* n2 = n1->Next; + if (!ImTriangleIsClockwise(n0->Pos, n1->Pos, n2->Pos)) + type = ImTriangulatorNodeType_Reflex; + else if (IsEar(n0->Index, n1->Index, n2->Index, n0->Pos, n1->Pos, n2->Pos)) + type = ImTriangulatorNodeType_Ear; + else + type = ImTriangulatorNodeType_Convex; + + // Update lists when a type changes + if (type == n1->Type) + return; + if (n1->Type == ImTriangulatorNodeType_Reflex) + _Reflexes.find_erase_unsorted(n1->Index); + else if (n1->Type == ImTriangulatorNodeType_Ear) + _Ears.find_erase_unsorted(n1->Index); + if (type == ImTriangulatorNodeType_Reflex) + _Reflexes.push_back(n1); + else if (type == ImTriangulatorNodeType_Ear) + _Ears.push_back(n1); + n1->Type = type; +} + +// Use ear-clipping algorithm to triangulate a simple polygon (no self-interaction, no holes). +// (Reminder: we don't perform any coarse clipping/culling in ImDrawList layer! +// It is up to caller to ensure not making costly calls that will be outside of visible area. +// As concave fill is noticeably more expensive than other primitives, be mindful of this... +// Caller can build AABB of points, and avoid filling if 'draw_list->_CmdHeader.ClipRect.Overlays(points_bb) == false') +void ImDrawList::AddConcavePolyFilled(const ImVec2* points, const int points_count, ImU32 col) +{ + if (points_count < 3 || (col & IM_COL32_A_MASK) == 0) + return; + + const ImVec2 uv = _Data->TexUvWhitePixel; + ImTriangulator triangulator; + unsigned int triangle[3]; + if (Flags & ImDrawListFlags_AntiAliasedFill) + { + // Anti-aliased Fill + const float AA_SIZE = _FringeScale; + const ImU32 col_trans = col & ~IM_COL32_A_MASK; + const int idx_count = (points_count - 2) * 3 + points_count * 6; + const int vtx_count = (points_count * 2); + PrimReserve(idx_count, vtx_count); + + // Add indexes for fill + unsigned int vtx_inner_idx = _VtxCurrentIdx; + unsigned int vtx_outer_idx = _VtxCurrentIdx + 1; + + _Data->TempBuffer.reserve_discard((ImTriangulator::EstimateScratchBufferSize(points_count) + sizeof(ImVec2)) / sizeof(ImVec2)); + triangulator.Init(points, points_count, _Data->TempBuffer.Data); + while (triangulator._TrianglesLeft > 0) + { + triangulator.GetNextTriangle(triangle); + _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (triangle[0] << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (triangle[1] << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx + (triangle[2] << 1)); + _IdxWritePtr += 3; + } + + // Compute normals + _Data->TempBuffer.reserve_discard(points_count); + ImVec2* temp_normals = _Data->TempBuffer.Data; + for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++) + { + const ImVec2& p0 = points[i0]; + const ImVec2& p1 = points[i1]; + float dx = p1.x - p0.x; + float dy = p1.y - p0.y; + IM_NORMALIZE2F_OVER_ZERO(dx, dy); + temp_normals[i0].x = dy; + temp_normals[i0].y = -dx; + } + + for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++) + { + // Average normals + const ImVec2& n0 = temp_normals[i0]; + const ImVec2& n1 = temp_normals[i1]; + float dm_x = (n0.x + n1.x) * 0.5f; + float dm_y = (n0.y + n1.y) * 0.5f; + IM_FIXNORMAL2F(dm_x, dm_y); + dm_x *= AA_SIZE * 0.5f; + dm_y *= AA_SIZE * 0.5f; + + // Add vertices + _VtxWritePtr[0].pos.x = (points[i1].x - dm_x); _VtxWritePtr[0].pos.y = (points[i1].y - dm_y); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner + _VtxWritePtr[1].pos.x = (points[i1].x + dm_x); _VtxWritePtr[1].pos.y = (points[i1].y + dm_y); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer + _VtxWritePtr += 2; + + // Add indexes for fringes + _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (i0 << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); + _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx + (i1 << 1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); + _IdxWritePtr += 6; + } + _VtxCurrentIdx += (ImDrawIdx)vtx_count; + } + else + { + // Non Anti-aliased Fill + const int idx_count = (points_count - 2) * 3; + const int vtx_count = points_count; + PrimReserve(idx_count, vtx_count); + for (int i = 0; i < vtx_count; i++) + { + _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; + _VtxWritePtr++; + } + _Data->TempBuffer.reserve_discard((ImTriangulator::EstimateScratchBufferSize(points_count) + sizeof(ImVec2)) / sizeof(ImVec2)); + triangulator.Init(points, points_count, _Data->TempBuffer.Data); + while (triangulator._TrianglesLeft > 0) + { + triangulator.GetNextTriangle(triangle); + _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx + triangle[0]); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + triangle[1]); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + triangle[2]); + _IdxWritePtr += 3; + } + _VtxCurrentIdx += (ImDrawIdx)vtx_count; + } +} //----------------------------------------------------------------------------- // [SECTION] ImDrawListSplitter @@ -2672,8 +2985,8 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) int unscaled_ascent, unscaled_descent, unscaled_line_gap; stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap); - const float ascent = ImTrunc(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1)); - const float descent = ImTrunc(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1)); + const float ascent = ImCeil(unscaled_ascent * font_scale); + const float descent = ImFloor(unscaled_descent * font_scale); ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent); const float font_off_x = cfg.GlyphOffset.x; const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent); @@ -3768,6 +4081,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im { x = start_x; y += line_height; + if (y > clip_rect.w) + break; // break out of main loop word_wrap_eol = NULL; s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks continue; @@ -4305,4 +4620,4 @@ static const char* GetDefaultCompressedFontDataTTFBase85() return proggy_clean_ttf_compressed_data_base85; } -#endif // #ifndef IMGUI_DISABLE +#endif // #ifndef IMGUI_DISABLE \ No newline at end of file diff --git a/platforms/desktop-shared/imgui/imgui_impl_opengl2.cpp b/platforms/desktop-shared/imgui/imgui_impl_opengl2.cpp index 17935b1..ad82410 100644 --- a/platforms/desktop-shared/imgui/imgui_impl_opengl2.cpp +++ b/platforms/desktop-shared/imgui/imgui_impl_opengl2.cpp @@ -83,6 +83,7 @@ static ImGui_ImplOpenGL2_Data* ImGui_ImplOpenGL2_GetBackendData() bool ImGui_ImplOpenGL2_Init() { ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); // Setup backend capabilities flags @@ -108,7 +109,7 @@ void ImGui_ImplOpenGL2_Shutdown() void ImGui_ImplOpenGL2_NewFrame() { ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); - IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplOpenGL2_Init()?"); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplOpenGL2_Init()?"); if (!bd->FontTexture) ImGui_ImplOpenGL2_CreateDeviceObjects(); @@ -299,4 +300,4 @@ void ImGui_ImplOpenGL2_DestroyDeviceObjects() #pragma clang diagnostic pop #endif -#endif // #ifndef IMGUI_DISABLE +#endif // #ifndef IMGUI_DISABLE \ No newline at end of file diff --git a/platforms/desktop-shared/imgui/imgui_impl_opengl2.h b/platforms/desktop-shared/imgui/imgui_impl_opengl2.h index 9c756c7..7ce21ac 100644 --- a/platforms/desktop-shared/imgui/imgui_impl_opengl2.h +++ b/platforms/desktop-shared/imgui/imgui_impl_opengl2.h @@ -35,4 +35,4 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyFontsTexture(); IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyDeviceObjects(); -#endif // #ifndef IMGUI_DISABLE +#endif // #ifndef IMGUI_DISABLE \ No newline at end of file diff --git a/platforms/desktop-shared/imgui/imgui_impl_sdl2.cpp b/platforms/desktop-shared/imgui/imgui_impl_sdl2.cpp index 7abeb0e..9b43328 100644 --- a/platforms/desktop-shared/imgui/imgui_impl_sdl2.cpp +++ b/platforms/desktop-shared/imgui/imgui_impl_sdl2.cpp @@ -306,8 +306,9 @@ static void ImGui_ImplSDL2_UpdateKeyModifiers(SDL_Keymod sdl_key_mods) // If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field. bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL2_Init()?"); + ImGuiIO& io = ImGui::GetIO(); switch (event->type) { @@ -399,6 +400,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer) { ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); // Check and store if we are on a SDL backend that supports global mouse position @@ -704,7 +706,7 @@ static void ImGui_ImplSDL2_UpdateGamepads() void ImGui_ImplSDL2_NewFrame() { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); - IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplSDL2_Init()?"); + IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL2_Init()?"); ImGuiIO& io = ImGui::GetIO(); // Setup display size (every frame to accommodate for window resizing) @@ -750,4 +752,4 @@ void ImGui_ImplSDL2_NewFrame() #pragma clang diagnostic pop #endif -#endif // #ifndef IMGUI_DISABLE +#endif // #ifndef IMGUI_DISABLE \ No newline at end of file diff --git a/platforms/desktop-shared/imgui/imgui_impl_sdl2.h b/platforms/desktop-shared/imgui/imgui_impl_sdl2.h index 7020a29..f407b4d 100644 --- a/platforms/desktop-shared/imgui/imgui_impl_sdl2.h +++ b/platforms/desktop-shared/imgui/imgui_impl_sdl2.h @@ -42,4 +42,4 @@ IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event); enum ImGui_ImplSDL2_GamepadMode { ImGui_ImplSDL2_GamepadMode_AutoFirst, ImGui_ImplSDL2_GamepadMode_AutoAll, ImGui_ImplSDL2_GamepadMode_Manual }; IMGUI_IMPL_API void ImGui_ImplSDL2_SetGamepadMode(ImGui_ImplSDL2_GamepadMode mode, struct _SDL_GameController** manual_gamepads_array = NULL, int manual_gamepads_count = -1); -#endif // #ifndef IMGUI_DISABLE +#endif // #ifndef IMGUI_DISABLE \ No newline at end of file diff --git a/platforms/desktop-shared/imgui/imgui_internal.h b/platforms/desktop-shared/imgui/imgui_internal.h index 4ac8b1c..6ff93d6 100644 --- a/platforms/desktop-shared/imgui/imgui_internal.h +++ b/platforms/desktop-shared/imgui/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.90.4 +// dear imgui, v1.90.8 // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. @@ -14,8 +14,8 @@ Index of this file: // [SECTION] Macros // [SECTION] Generic helpers // [SECTION] ImDrawList support -// [SECTION] Widgets support: flags, enums, data structures // [SECTION] Data types support +// [SECTION] Widgets support: flags, enums, data structures // [SECTION] Popup support // [SECTION] Inputs support // [SECTION] Clipper support @@ -87,6 +87,8 @@ Index of this file: #pragma clang diagnostic ignored "-Wdouble-promotion" #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision #pragma clang diagnostic ignored "-Wmissing-noreturn" // warning: function 'xxx' could be declared with attribute 'noreturn' +#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind @@ -124,7 +126,7 @@ struct ImDrawListSharedData; // Data shared between all ImDrawList instan struct ImGuiColorMod; // Stacked color modifier, backup of modified data so we can restore it struct ImGuiContext; // Main Dear ImGui context struct ImGuiContextHook; // Hook for extensions like ImGuiTestEngine -struct ImGuiDataVarInfo; // Variable information (e.g. to avoid style variables from an enum) +struct ImGuiDataVarInfo; // Variable information (e.g. to access style variables from an enum) struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup() struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box @@ -146,6 +148,7 @@ struct ImGuiStyleMod; // Stacked style modifier, backup of modifie 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 ImGuiTableHeaderData; // Storage for TableAngledHeadersRow() struct ImGuiTableColumn; // Storage for one column of a table struct ImGuiTableInstanceData; // Storage for one instance of a same table struct ImGuiTableTempData; // Temporary storage for one table (one per table in the stack), shared between tables. @@ -166,7 +169,6 @@ typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // E typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later) typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags typedef int ImGuiFocusRequestFlags; // -> enum ImGuiFocusRequestFlags_ // Flags: for FocusWindow(); -typedef int ImGuiInputFlags; // -> enum ImGuiInputFlags_ // Flags: for IsKeyPressed(), IsMouseClicked(), SetKeyOwner(), SetItemKeyOwner() etc. typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag(), g.LastItemData.InFlags typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for g.LastItemData.StatusFlags typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns() @@ -179,6 +181,7 @@ typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // F typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx() typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // Flags: for BeginTooltipEx() typedef int ImGuiTypingSelectFlags; // -> enum ImGuiTypingSelectFlags_ // Flags: for GetTypingSelectRequest() +typedef int ImGuiWindowRefreshFlags; // -> enum ImGuiWindowRefreshFlags_ // Flags: for SetNextWindowRefreshPolicy() typedef void (*ImGuiErrorLogCallback)(void* user_data, const char* fmt, ...); @@ -404,6 +407,7 @@ IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char IMGUI_API int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end); // return number of bytes to express one char in UTF-8 IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string in UTF-8 IMGUI_API const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr); // return previous UTF-8 code-point. +IMGUI_API int ImTextCountLines(const char* in_text, const char* in_text_end); // return number of lines taken by text. trailing carriage return doesn't count as an extra line. // Helpers: File System #ifdef IMGUI_DISABLE_FILE_FUNCTIONS @@ -468,7 +472,7 @@ template static inline T ImSubClampOverflow(T a, T b, T mn, T mx) // - Misc maths helpers static inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x < rhs.x ? lhs.x : rhs.x, lhs.y < rhs.y ? lhs.y : rhs.y); } static inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x >= rhs.x ? lhs.x : rhs.x, lhs.y >= rhs.y ? lhs.y : rhs.y); } -static inline ImVec2 ImClamp(const ImVec2& v, const ImVec2& mn, ImVec2 mx) { return ImVec2((v.x < mn.x) ? mn.x : (v.x > mx.x) ? mx.x : v.x, (v.y < mn.y) ? mn.y : (v.y > mx.y) ? mx.y : v.y); } +static inline ImVec2 ImClamp(const ImVec2& v, const ImVec2&mn, const ImVec2&mx) { return ImVec2((v.x < mn.x) ? mn.x : (v.x > mx.x) ? mx.x : v.x, (v.y < mn.y) ? mn.y : (v.y > mx.y) ? mx.y : v.y); } static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, float t) { return ImVec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t); } static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); } static inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t) { return ImVec4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t); } @@ -498,7 +502,8 @@ IMGUI_API ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const IMGUI_API bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); IMGUI_API ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); IMGUI_API void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w); -inline float ImTriangleArea(const ImVec2& a, const ImVec2& b, const ImVec2& c) { return ImFabs((a.x * (b.y - c.y)) + (b.x * (c.y - a.y)) + (c.x * (a.y - b.y))) * 0.5f; } +inline float ImTriangleArea(const ImVec2& a, const ImVec2& b, const ImVec2& c) { return ImFabs((a.x * (b.y - c.y)) + (b.x * (c.y - a.y)) + (c.x * (a.y - b.y))) * 0.5f; } +inline bool ImTriangleIsClockwise(const ImVec2& a, const ImVec2& b, const ImVec2& c) { return ((b.x - a.x) * (c.y - b.y)) - ((c.x - b.x) * (b.y - a.y)) > 0.0f; } // Helper: ImVec1 (1D vector) // (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches) @@ -793,6 +798,40 @@ struct ImDrawDataBuilder ImDrawDataBuilder() { memset(this, 0, sizeof(*this)); } }; +//----------------------------------------------------------------------------- +// [SECTION] Data types support +//----------------------------------------------------------------------------- + +struct ImGuiDataVarInfo +{ + ImGuiDataType Type; + ImU32 Count; // 1+ + ImU32 Offset; // Offset in parent structure + void* GetVarPtr(void* parent) const { return (void*)((unsigned char*)parent + Offset); } +}; + +struct ImGuiDataTypeStorage +{ + ImU8 Data[8]; // Opaque storage to fit any data up to ImGuiDataType_COUNT +}; + +// Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo(). +struct ImGuiDataTypeInfo +{ + size_t Size; // Size in bytes + const char* Name; // Short descriptive name for the type, for debugging + const char* PrintFmt; // Default printf format for the type + const char* ScanFmt; // Default scanf format for the type +}; + +// Extend ImGuiDataType_ +enum ImGuiDataTypePrivate_ +{ + ImGuiDataType_String = ImGuiDataType_COUNT + 1, + ImGuiDataType_Pointer, + ImGuiDataType_ID, +}; + //----------------------------------------------------------------------------- // [SECTION] Widgets support: flags, enums, data structures //----------------------------------------------------------------------------- @@ -836,7 +875,8 @@ enum ImGuiItemStatusFlags_ 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_Visible = 1 << 8, // [WIP] Set when item is overlapping the current clipping rectangle (Used internally. Please don't use yet: API/system will change as we refactor Itemadd()). - ImGuiItemStatusFlags_HasClipRect = 1 << 9, // g.LastItemData.ClipRect is valid + ImGuiItemStatusFlags_HasClipRect = 1 << 9, // g.LastItemData.ClipRect is valid. + ImGuiItemStatusFlags_HasShortcut = 1 << 10, // g.LastItemData.Shortcut valid. Set by SetNextItemShortcut() -> ItemAdd(). // Additional status + semantic for ImGuiTestEngine #ifdef IMGUI_ENABLE_TEST_ENGINE @@ -863,6 +903,7 @@ enum ImGuiInputTextFlagsPrivate_ 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. + ImGuiInputTextFlags_LocalizeDecimalPoint= 1 << 29, // For internal use by InputScalar() and TempInputScalar() }; // Extend ImGuiButtonFlags_ @@ -882,7 +923,7 @@ enum ImGuiButtonFlagsPrivate_ ImGuiButtonFlags_AlignTextBaseLine = 1 << 15, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine ImGuiButtonFlags_NoKeyModifiers = 1 << 16, // disable mouse interaction if a key modifier is held ImGuiButtonFlags_NoHoldingActiveId = 1 << 17, // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only) - ImGuiButtonFlags_NoNavFocus = 1 << 18, // don't override navigation focus when activated (FIXME: this is essentially used everytime an item uses ImGuiItemFlags_NoNav, but because legacy specs don't requires LastItemData to be set ButtonBehavior(), we can't poll g.LastItemData.InFlags) + ImGuiButtonFlags_NoNavFocus = 1 << 18, // don't override navigation focus when activated (FIXME: this is essentially used every time an item uses ImGuiItemFlags_NoNav, but because legacy specs don't requires LastItemData to be set ButtonBehavior(), we can't poll g.LastItemData.InFlags) ImGuiButtonFlags_NoHoveredOnFocus = 1 << 19, // don't report as hovered when nav focus is on this item ImGuiButtonFlags_NoSetKeyOwner = 1 << 20, // don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!) ImGuiButtonFlags_NoTestKeyOwner = 1 << 21, // don't test key/input owner when polling the key (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!) @@ -1111,6 +1152,15 @@ struct IMGUI_API ImGuiInputTextState }; +enum ImGuiWindowRefreshFlags_ +{ + ImGuiWindowRefreshFlags_None = 0, + ImGuiWindowRefreshFlags_TryToAvoidRefresh = 1 << 0, // [EXPERIMENTAL] Try to keep existing contents, USER MUST NOT HONOR BEGIN() RETURNING FALSE AND NOT APPEND. + ImGuiWindowRefreshFlags_RefreshOnHover = 1 << 1, // [EXPERIMENTAL] Always refresh on hover + ImGuiWindowRefreshFlags_RefreshOnFocus = 1 << 2, // [EXPERIMENTAL] Always refresh on focus + // Refresh policy/frequency, Load Balancing etc. +}; + enum ImGuiNextWindowDataFlags_ { ImGuiNextWindowDataFlags_None = 0, @@ -1123,6 +1173,7 @@ enum ImGuiNextWindowDataFlags_ ImGuiNextWindowDataFlags_HasBgAlpha = 1 << 6, ImGuiNextWindowDataFlags_HasScroll = 1 << 7, ImGuiNextWindowDataFlags_HasChildFlags = 1 << 8, + ImGuiNextWindowDataFlags_HasRefreshPolicy = 1 << 9, }; // Storage for SetNexWindow** functions @@ -1144,6 +1195,7 @@ struct ImGuiNextWindowData void* SizeCallbackUserData; float BgAlphaVal; // Override background alpha ImVec2 MenuBarOffsetMinVal; // (Always on) This is not exposed publicly, so we don't clear it and it doesn't have a corresponding flag (could we? for consistency?) + ImGuiWindowRefreshFlags RefreshFlagsVal; ImGuiNextWindowData() { memset(this, 0, sizeof(*this)); } inline void ClearFlags() { Flags = ImGuiNextWindowDataFlags_None; } @@ -1159,6 +1211,7 @@ enum ImGuiNextItemDataFlags_ ImGuiNextItemDataFlags_HasWidth = 1 << 0, ImGuiNextItemDataFlags_HasOpen = 1 << 1, ImGuiNextItemDataFlags_HasShortcut = 1 << 2, + ImGuiNextItemDataFlags_HasRefVal = 1 << 3, }; struct ImGuiNextItemData @@ -1169,8 +1222,10 @@ struct ImGuiNextItemData ImGuiSelectionUserData SelectionUserData; // Set by SetNextItemSelectionUserData() (note that NULL/0 is a valid value, we use -1 == ImGuiSelectionUserData_Invalid to mark invalid values) float Width; // Set by SetNextItemWidth() ImGuiKeyChord Shortcut; // Set by SetNextItemShortcut() + ImGuiInputFlags ShortcutFlags; // Set by SetNextItemShortcut() bool OpenVal; // Set by SetNextItemOpen() - ImGuiCond OpenCond : 8; + ImU8 OpenCond; // Set by SetNextItemOpen() + ImGuiDataTypeStorage RefVal; // Not exposed yet, for ImGuiInputTextFlags_ParseEmptyAsRefVal ImGuiNextItemData() { memset(this, 0, sizeof(*this)); SelectionUserData = -1; } inline void ClearFlags() { Flags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()! @@ -1184,9 +1239,10 @@ struct ImGuiLastItemData ImGuiItemStatusFlags StatusFlags; // See ImGuiItemStatusFlags_ ImRect Rect; // Full rectangle ImRect NavRect; // Navigation scoring rectangle (not displayed) - // Rarely used fields are not explicitly cleared, only valid when the corresponding ImGuiItemStatusFlags is set. - ImRect DisplayRect; // Display rectangle (ONLY VALID IF ImGuiItemStatusFlags_HasDisplayRect is set) - ImRect ClipRect; // Clip rectangle at the time of submitting item (ONLY VALID IF ImGuiItemStatusFlags_HasClipRect is set) + // Rarely used fields are not explicitly cleared, only valid when the corresponding ImGuiItemStatusFlags ar set. + ImRect DisplayRect; // Display rectangle. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) is set. + ImRect ClipRect; // Clip rectangle at the time of submitting item. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasClipRect) is set.. + ImGuiKeyChord Shortcut; // Shortcut at the time of submitting item. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasShortcut) is set.. ImGuiLastItemData() { memset(this, 0, sizeof(*this)); } }; @@ -1223,7 +1279,8 @@ struct ImGuiWindowStackData { ImGuiWindow* Window; ImGuiLastItemData ParentLastItemDataBackup; - ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting + ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting + bool DisabledOverrideReenable; // Non-child window override disabled flag }; struct ImGuiShrinkWidthItem @@ -1242,40 +1299,6 @@ struct ImGuiPtrOrIndex ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; } }; -//----------------------------------------------------------------------------- -// [SECTION] Data types support -//----------------------------------------------------------------------------- - -struct ImGuiDataVarInfo -{ - ImGuiDataType Type; - ImU32 Count; // 1+ - ImU32 Offset; // Offset in parent structure - void* GetVarPtr(void* parent) const { return (void*)((unsigned char*)parent + Offset); } -}; - -struct ImGuiDataTypeTempStorage -{ - ImU8 Data[8]; // Can fit any data up to ImGuiDataType_COUNT -}; - -// Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo(). -struct ImGuiDataTypeInfo -{ - size_t Size; // Size in bytes - const char* Name; // Short descriptive name for the type, for debugging - const char* PrintFmt; // Default printf format for the type - const char* ScanFmt; // Default scanf format for the type -}; - -// Extend ImGuiDataType_ -enum ImGuiDataTypePrivate_ -{ - ImGuiDataType_String = ImGuiDataType_COUNT + 1, - ImGuiDataType_Pointer, - ImGuiDataType_ID, -}; - //----------------------------------------------------------------------------- // [SECTION] Popup support //----------------------------------------------------------------------------- @@ -1292,7 +1315,7 @@ struct ImGuiPopupData { ImGuiID PopupId; // Set on OpenPopup() ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() - ImGuiWindow* BackupNavWindow;// Set on OpenPopup(), a NavWindow that will be restored on popup close + ImGuiWindow* RestoreNavWindow;// Set on OpenPopup(), a NavWindow that will be restored on popup close int ParentNavLayer; // Resolved on BeginPopup(). Actually a ImGuiNavLayer type (declared down below), initialized to -1 which is not part of an enum, but serves well-enough as "not any of layers" value int OpenFrameCount; // Set on OpenPopup() ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) @@ -1349,7 +1372,6 @@ enum ImGuiInputSource ImGuiInputSource_Mouse, // Note: may be Mouse or TouchScreen or Pen. See io.MouseSource to distinguish them. ImGuiInputSource_Keyboard, ImGuiInputSource_Gamepad, - ImGuiInputSource_Clipboard, // Currently only used by InputText() ImGuiInputSource_COUNT }; @@ -1383,7 +1405,8 @@ struct ImGuiInputEvent // Input function taking an 'ImGuiID owner_id' argument defaults to (ImGuiKeyOwner_Any == 0) aka don't test ownership, which matches legacy behavior. #define ImGuiKeyOwner_Any ((ImGuiID)0) // Accept key that have an owner, UNLESS a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease. -#define ImGuiKeyOwner_None ((ImGuiID)-1) // Require key to have no owner. +#define ImGuiKeyOwner_NoOwner ((ImGuiID)-1) // Require key to have no owner. +//#define ImGuiKeyOwner_None ImGuiKeyOwner_NoOwner // We previously called this 'ImGuiKeyOwner_None' but it was inconsistent with our pattern that _None values == 0 and quite dangerous. Also using _NoOwner makes the IsKeyPressed() calls more explicit. typedef ImS16 ImGuiKeyRoutingIndex; @@ -1391,13 +1414,13 @@ typedef ImS16 ImGuiKeyRoutingIndex; struct ImGuiKeyRoutingData { ImGuiKeyRoutingIndex NextEntryIndex; - ImU16 Mods; // Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits. ImGuiMod_Shortcut is already translated to Ctrl/Super. + ImU16 Mods; // Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits. ImU8 RoutingCurrScore; // [DEBUG] For debug display ImU8 RoutingNextScore; // Lower is better (0: perfect score) ImGuiID RoutingCurr; ImGuiID RoutingNext; - ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 255; RoutingCurr = RoutingNext = ImGuiKeyOwner_None; } + ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 255; RoutingCurr = RoutingNext = ImGuiKeyOwner_NoOwner; } }; // Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching. @@ -1421,73 +1444,47 @@ struct ImGuiKeyOwnerData bool LockThisFrame; // Reading this key requires explicit owner id (until end of frame). Set by ImGuiInputFlags_LockThisFrame. bool LockUntilRelease; // Reading this key requires explicit owner id (until key is released). Set by ImGuiInputFlags_LockUntilRelease. When this is true LockThisFrame is always true as well. - ImGuiKeyOwnerData() { OwnerCurr = OwnerNext = ImGuiKeyOwner_None; LockThisFrame = LockUntilRelease = false; } + ImGuiKeyOwnerData() { OwnerCurr = OwnerNext = ImGuiKeyOwner_NoOwner; LockThisFrame = LockUntilRelease = false; } }; +// Extend ImGuiInputFlags_ // Flags for extended versions of IsKeyPressed(), IsMouseClicked(), Shortcut(), SetKeyOwner(), SetItemKeyOwner() // Don't mistake with ImGuiInputTextFlags! (which is for ImGui::InputText() function) -enum ImGuiInputFlags_ +enum ImGuiInputFlagsPrivate_ { // Flags for IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(), Shortcut() - ImGuiInputFlags_None = 0, - - // Repeat mode - ImGuiInputFlags_Repeat = 1 << 0, // Enable repeat. Return true on successive repeats. Default for legacy IsKeyPressed(). NOT Default for legacy IsMouseClicked(). MUST BE == 1. - ImGuiInputFlags_RepeatRateDefault = 1 << 1, // Repeat rate: Regular (default) - ImGuiInputFlags_RepeatRateNavMove = 1 << 2, // Repeat rate: Fast - ImGuiInputFlags_RepeatRateNavTweak = 1 << 3, // Repeat rate: Faster - - // Repeat mode: Specify when repeating key pressed can be interrupted. - // In theory ImGuiInputFlags_RepeatUntilOtherKeyPress may be a desirable default, but it would break too many behavior so everything is opt-in. - ImGuiInputFlags_RepeatUntilRelease = 1 << 4, // Stop repeating when released (default for all functions except Shortcut). This only exists to allow overriding Shortcut() default behavior. - ImGuiInputFlags_RepeatUntilKeyModsChange = 1 << 5, // Stop repeating when released OR if keyboard mods are changed (default for Shortcut) + // - Repeat mode: Repeat rate selection + ImGuiInputFlags_RepeatRateDefault = 1 << 1, // Repeat rate: Regular (default) + ImGuiInputFlags_RepeatRateNavMove = 1 << 2, // Repeat rate: Fast + ImGuiInputFlags_RepeatRateNavTweak = 1 << 3, // Repeat rate: Faster + // - Repeat mode: Specify when repeating key pressed can be interrupted. + // - In theory ImGuiInputFlags_RepeatUntilOtherKeyPress may be a desirable default, but it would break too many behavior so everything is opt-in. + ImGuiInputFlags_RepeatUntilRelease = 1 << 4, // Stop repeating when released (default for all functions except Shortcut). This only exists to allow overriding Shortcut() default behavior. + ImGuiInputFlags_RepeatUntilKeyModsChange = 1 << 5, // Stop repeating when released OR if keyboard mods are changed (default for Shortcut) ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone = 1 << 6, // Stop repeating when released OR if keyboard mods are leaving the None state. Allows going from Mod+Key to Key by releasing Mod. - ImGuiInputFlags_RepeatUntilOtherKeyPress = 1 << 7, // Stop repeating when released OR if any other keyboard key is pressed during the repeat - - // Flags for SetItemKeyOwner() - ImGuiInputFlags_CondHovered = 1 << 8, // Only set if item is hovered (default to both) - ImGuiInputFlags_CondActive = 1 << 9, // Only set if item is active (default to both) - ImGuiInputFlags_CondDefault_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive, + ImGuiInputFlags_RepeatUntilOtherKeyPress = 1 << 7, // Stop repeating when released OR if any other keyboard key is pressed during the repeat // Flags for SetKeyOwner(), SetItemKeyOwner() - // Locking is useful to make input-owner-aware code steal keys from non-input-owner-aware code. If all code is input-owner-aware locking would never be necessary. - ImGuiInputFlags_LockThisFrame = 1 << 10, // Further accesses to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared at end of frame. - ImGuiInputFlags_LockUntilRelease = 1 << 11, // Further accesses to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared when the key is released or at end of each frame if key is released. - - // Routing policies for Shortcut() + low-level SetShortcutRouting() - // - The general idea is that several callers register interest in a shortcut, and only one owner gets it. - // Parent -> call Shortcut(Ctrl+S) // When Parent is focused, Parent gets the shortcut. - // Child1 -> call Shortcut(Ctrl+S) // When Child1 is focused, Child1 gets the shortcut (Child1 overrides Parent shortcuts) - // Child2 -> no call // When Child2 is focused, Parent gets the shortcut. - // The whole system is order independent, so if Child1 does it calls before Parent results will be identical. - // This is an important property as it facilitate working with foreign code or larger codebase. - // - Visualize registered routes in 'Metrics->Inputs' and submitted routes in 'Debug Log->InputRouting'. - // - When a policy (except for _RouteAlways *) is set, Shortcut() will register itself with SetShortcutRouting(), - // allowing the system to decide where to route the input among other route-aware calls. - // (* Using ImGuiInputFlags_RouteAlways is roughly equivalent to calling IsKeyChordPressed(key)). - // - Shortcut() uses ImGuiInputFlags_RouteFocused by default. Meaning that a Shortcut() call will register - // a route and only succeed when parent window is in the focus-stack and if no-one with a higher priority - // is claiming the same shortcut. - // - You can chain two unrelated windows in the focus stack using SetWindowParentWindowForFocusRoute(). - // - Priorities: GlobalHigh > Focused (when owner is active item) > Global > Focused (when focused window) > GlobalLow. - // - Can select only 1 policy among all available. - ImGuiInputFlags_RouteFocused = 1 << 12, // (Default) Honor focus route: Accept inputs if window is in focus stack. Deep-most focused window takes inputs. ActiveId takes inputs over deep-most focused window. - ImGuiInputFlags_RouteGlobalLow = 1 << 13, // Register route globally (lowest priority: unless a focused window or active item registered the route) -> recommended Global priority IF you need a Global priority. - ImGuiInputFlags_RouteGlobal = 1 << 14, // Register route globally (medium priority: unless an active item registered the route, e.g. CTRL+A registered by InputText will take priority over this). - ImGuiInputFlags_RouteGlobalHigh = 1 << 15, // Register route globally (higher priority: unlikely you need to use that: will interfere with every active items, e.g. CTRL+A registered by InputText will be overriden by this) - ImGuiInputFlags_RouteAlways = 1 << 16, // Do not register route, poll keys directly. - // Routing polices: extra options - ImGuiInputFlags_RouteUnlessBgFocused= 1 << 17, // Global routes will not be applied if underlying background/void is focused (== no Dear ImGui windows are focused). Useful for overlay applications. + // - Locking key away from non-input aware code. Locking is useful to make input-owner-aware code steal keys from non-input-owner-aware code. If all code is input-owner-aware locking would never be necessary. + ImGuiInputFlags_LockThisFrame = 1 << 20, // Further accesses to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared at end of frame. + ImGuiInputFlags_LockUntilRelease = 1 << 21, // Further accesses to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared when the key is released or at end of each frame if key is released. + + // - Condition for SetItemKeyOwner() + ImGuiInputFlags_CondHovered = 1 << 22, // Only set if item is hovered (default to both) + ImGuiInputFlags_CondActive = 1 << 23, // Only set if item is active (default to both) + ImGuiInputFlags_CondDefault_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive, // [Internal] Mask of which function support which flags ImGuiInputFlags_RepeatRateMask_ = ImGuiInputFlags_RepeatRateDefault | ImGuiInputFlags_RepeatRateNavMove | ImGuiInputFlags_RepeatRateNavTweak, ImGuiInputFlags_RepeatUntilMask_ = ImGuiInputFlags_RepeatUntilRelease | ImGuiInputFlags_RepeatUntilKeyModsChange | ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone | ImGuiInputFlags_RepeatUntilOtherKeyPress, ImGuiInputFlags_RepeatMask_ = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_, ImGuiInputFlags_CondMask_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive, - ImGuiInputFlags_RouteMask_ = ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteGlobalLow | ImGuiInputFlags_RouteGlobalHigh, // _Always not part of this! + ImGuiInputFlags_RouteTypeMask_ = ImGuiInputFlags_RouteActive | ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteAlways, + ImGuiInputFlags_RouteOptionsMask_ = ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused | ImGuiInputFlags_RouteFromRootWindow, ImGuiInputFlags_SupportedByIsKeyPressed = ImGuiInputFlags_RepeatMask_, ImGuiInputFlags_SupportedByIsMouseClicked = ImGuiInputFlags_Repeat, - ImGuiInputFlags_SupportedByShortcut = ImGuiInputFlags_RepeatMask_ | ImGuiInputFlags_RouteMask_ | ImGuiInputFlags_RouteAlways | ImGuiInputFlags_RouteUnlessBgFocused, + ImGuiInputFlags_SupportedByShortcut = ImGuiInputFlags_RepeatMask_ | ImGuiInputFlags_RouteTypeMask_ | ImGuiInputFlags_RouteOptionsMask_, + ImGuiInputFlags_SupportedBySetNextItemShortcut = ImGuiInputFlags_RepeatMask_ | ImGuiInputFlags_RouteTypeMask_ | ImGuiInputFlags_RouteOptionsMask_ | ImGuiInputFlags_Tooltip, ImGuiInputFlags_SupportedBySetKeyOwner = ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease, ImGuiInputFlags_SupportedBySetItemKeyOwner = ImGuiInputFlags_SupportedBySetKeyOwner | ImGuiInputFlags_CondMask_, }; @@ -1578,6 +1575,7 @@ enum ImGuiNavMoveFlags_ ImGuiNavMoveFlags_Activate = 1 << 12, // Activate/select target item. ImGuiNavMoveFlags_NoSelect = 1 << 13, // Don't trigger selection by not setting g.NavJustMovedTo ImGuiNavMoveFlags_NoSetNavHighlight = 1 << 14, // Do not alter the visible state of keyboard vs mouse nav highlight + ImGuiNavMoveFlags_NoClearActiveId = 1 << 15, // (Experimental) Do not clear active id when applying move result }; enum ImGuiNavLayer @@ -1587,6 +1585,7 @@ enum ImGuiNavLayer ImGuiNavLayer_COUNT }; +// Storage for navigation query/results struct ImGuiNavItemData { ImGuiWindow* Window; // Init,Move // Best candidate window (result->ItemWindow->RootWindowForNav == request->Window) @@ -1594,15 +1593,16 @@ struct ImGuiNavItemData ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space ImGuiItemFlags InFlags; // ????,Move // Best candidate item flags - ImGuiSelectionUserData SelectionUserData;//I+Mov // Best candidate SetNextItemSelectionData() value. 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 + ImGuiSelectionUserData SelectionUserData;//I+Mov // Best candidate SetNextItemSelectionData() value. ImGuiNavItemData() { Clear(); } void Clear() { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; SelectionUserData = -1; DistBox = DistCenter = DistAxial = FLT_MAX; } }; +// Storage for PushFocusScope() struct ImGuiFocusScopeData { ImGuiID ID; @@ -1924,6 +1924,7 @@ struct ImGuiContext ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window. float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Base text height. + float CurrentDpiScale; // Current window/viewport DpiScale ImDrawListSharedData DrawListSharedData; double Time; int FrameCount; @@ -1949,7 +1950,7 @@ struct ImGuiContext 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) + ImVec2 WindowsHoverPadding; // Padding around resizable windows for which hovering on counts as hovering the window == ImMax(style.TouchExtraPadding, WINDOWS_HOVER_PADDING). ImGuiID DebugBreakInWindow; // Set to break in Begin() call. ImGuiWindow* CurrentWindow; // Window being drawn into ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs. @@ -1967,10 +1968,11 @@ struct ImGuiContext ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line] ImGuiID HoveredId; // Hovered widget, filled during the frame ImGuiID HoveredIdPreviousFrame; - bool HoveredIdAllowOverlap; - bool HoveredIdDisabled; // At least one widget passed the rect test, but has been discarded by disabled flag or popup inhibit. May be true even if HoveredId == 0. float HoveredIdTimer; // Measure contiguous hovering time float HoveredIdNotActiveTimer; // Measure contiguous hovering time where the item has not been active + bool HoveredIdAllowOverlap; + bool HoveredIdDisabled; // At least one widget passed the rect test, but has been discarded by disabled flag or popup inhibit. May be true even if HoveredId == 0. + bool ItemUnclipByLog; // Disable ItemAdd() clipping, essentially a memory-locality friendly copy of LogEnabled ImGuiID ActiveId; // Active widget ImGuiID ActiveIdIsAlive; // Active widget has been seen this frame (we can't use a bool as the ActiveId may change within the frame) float ActiveIdTimer; @@ -1992,9 +1994,9 @@ struct ImGuiContext ImGuiID LastActiveId; // Store the last non-zero ActiveId, useful for animation. float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation. - // [EXPERIMENTAL] Key/Input Ownership + Shortcut Routing system + // Key/Input Ownership + Shortcut Routing system // - The idea is that instead of "eating" a given key, we can link to an owner. - // - Input query can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_None (== -1) or a custom ID. + // - Input query can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_NoOwner (== -1) or a custom ID. // - Routing is requested ahead of time for a given chord (Key + Mods) and granted in NewFrame(). double LastKeyModsChangeTime; // Record the last time key mods changed (affect repeat delay when using shortcut logic) double LastKeyModsChangeFromNoneTime; // Record the last time key mods changed away from being 0 (affect repeat delay when using shortcut logic) @@ -2037,11 +2039,11 @@ struct ImGuiContext ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusedWindow' ImGuiID NavId; // Focused item for navigation ImGuiID NavFocusScopeId; // Focused focus scope (e.g. selection code often wants to "clear other items" when landing on an item of the same scope) - ImVector NavFocusRoute; // Reversed copy focus scope stack for NavId (should contains NavFocusScopeId). This essentially follow the window->ParentWindowForFocusRoute chain. ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItem() ImGuiID NavActivateDownId; // ~~ IsKeyDown(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyDown(ImGuiKey_NavGamepadActivate) ? NavId : 0 ImGuiID NavActivatePressedId; // ~~ IsKeyPressed(ImGuiKey_Space) || IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate) ? NavId : 0 (no repeat) ImGuiActivateFlags NavActivateFlags; + ImVector NavFocusRoute; // Reversed copy focus scope stack for NavId (should contains NavFocusScopeId). This essentially follow the window->ParentWindowForFocusRoute chain. ImGuiID NavHighlightActivatedId; float NavHighlightActivatedTimer; ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest). @@ -2082,8 +2084,8 @@ struct ImGuiContext ImGuiNavItemData NavTabbingResultFirst; // First tabbing request candidate within NavWindow and flattened hierarchy // Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize) - ImGuiKeyChord ConfigNavWindowingKeyNext; // = ImGuiMod_Ctrl | ImGuiKey_Tab, for reconfiguration (see #4828) - ImGuiKeyChord ConfigNavWindowingKeyPrev; // = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab + ImGuiKeyChord ConfigNavWindowingKeyNext; // = ImGuiMod_Ctrl | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiKey_Tab on OS X). For reconfiguration (see #4828) + ImGuiKeyChord ConfigNavWindowingKeyPrev; // = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab on OS X) ImGuiWindow* NavWindowingTarget; // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most! ImGuiWindow* NavWindowingTargetAnim; // Record of last valid NavWindowingTarget until DimBgRatio and NavWindowingHighlightAlpha becomes 0.0f, so the fade-out can stay on it. ImGuiWindow* NavWindowingListWindow; // Internal window actually listing the CTRL+Tab contents @@ -2154,6 +2156,7 @@ struct ImGuiContext ImGuiInputTextDeactivatedState InputTextDeactivatedState; ImFont InputTextPasswordFont; ImGuiID TempInputId; // Temporary text input when CTRL+clicking on a slider, etc. + ImGuiDataTypeStorage DataTypeZeroValue; // 0 for all data types int BeginMenuDepth; int BeginComboDepth; ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets @@ -2166,13 +2169,14 @@ struct ImGuiContext ImGuiComboPreviewData ComboPreviewData; ImRect WindowResizeBorderExpectedRect; // Expected border rect, switch to relative edit if moving bool WindowResizeRelativeMode; + short ScrollbarSeekMode; // 0: relative, -1/+1: prev/next page. + float ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? float SliderGrabClickOffset; float SliderCurrentAccum; // Accumulated slider delta when using navigation controls. bool SliderCurrentAccumDirty; // Has the accumulated slider delta changed since last time we tried to apply it? bool DragCurrentAccumDirty; float DragCurrentAccum; // Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio - float ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? float DisabledAlphaBackup; // Backup for style.Alpha for BeginDisabled() short DisabledStackSize; short LockMarkEdited; @@ -2250,7 +2254,7 @@ struct ImGuiContext Initialized = false; FontAtlasOwnedByContext = shared_font_atlas ? false : true; Font = NULL; - FontSize = FontBaseSize = 0.0f; + FontSize = FontBaseSize = CurrentDpiScale = 0.0f; IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)(); Time = 0.0f; FrameCount = 0; @@ -2277,6 +2281,7 @@ struct ImGuiContext HoveredIdAllowOverlap = false; HoveredIdDisabled = false; HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f; + ItemUnclipByLog = false; ActiveId = 0; ActiveIdIsAlive = 0; ActiveIdTimer = 0.0f; @@ -2338,8 +2343,10 @@ struct ImGuiContext NavTabbingDir = 0; NavTabbingCounter = 0; - ConfigNavWindowingKeyNext = ImGuiMod_Ctrl | ImGuiKey_Tab; - ConfigNavWindowingKeyPrev = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab; + // All platforms use Ctrl+Tab but Ctrl<>Super are swapped on Mac... + // FIXME: Because this value is stored, it annoyingly interfere with toggling io.ConfigMacOSXBehaviors updating this.. + ConfigNavWindowingKeyNext = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiKey_Tab); + ConfigNavWindowingKeyPrev = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab); NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL; NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingToggleLayer = false; @@ -2372,19 +2379,21 @@ struct ImGuiContext MouseStationaryTimer = 0.0f; TempInputId = 0; + memset(&DataTypeZeroValue, 0, sizeof(DataTypeZeroValue)); BeginMenuDepth = BeginComboDepth = 0; ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_; ColorEditCurrentID = ColorEditSavedID = 0; ColorEditSavedHue = ColorEditSavedSat = 0.0f; ColorEditSavedColor = 0; WindowResizeRelativeMode = false; + ScrollbarSeekMode = 0; + ScrollbarClickDeltaToGrabCenter = 0.0f; SliderGrabClickOffset = 0.0f; SliderCurrentAccum = 0.0f; SliderCurrentAccumDirty = false; DragCurrentAccumDirty = false; DragCurrentAccum = 0.0f; DragSpeedDefaultRatio = 1.0f / 100.0f; - ScrollbarClickDeltaToGrabCenter = 0.0f; DisabledAlphaBackup = 0.0f; DisabledStackSize = 0; LockMarkEdited = 0; @@ -2509,12 +2518,14 @@ struct IMGUI_API ImGuiWindow ImVec2 WindowPadding; // Window padding at the time of Begin(). float WindowRounding; // Window rounding at the time of Begin(). May be clamped lower to avoid rendering artifacts with title bar, menu bar etc. float WindowBorderSize; // Window border size at the time of Begin(). + float TitleBarHeight, MenuBarHeight; float DecoOuterSizeX1, DecoOuterSizeY1; // Left/Up offsets. Sum of non-scrolling outer decorations (X1 generally == 0.0f. Y1 generally = TitleBarHeight + MenuBarHeight). Locked during Begin(). float DecoOuterSizeX2, DecoOuterSizeY2; // Right/Down offsets (X2 generally == ScrollbarSize.x, Y2 == ScrollbarSizes.y). float DecoInnerSizeX1, DecoInnerSizeY1; // Applied AFTER/OVER InnerRect. Specialized for Tables as they use specialized form of clipping and frozen rows/columns are inside InnerRect (and not part of regular decoration sizes). int NameBufLen; // Size of buffer storing Name. May be larger than strlen(Name)! ImGuiID MoveId; // == window->GetID("#MOVE") ImGuiID ChildId; // ID of corresponding item in parent window (for navigation to return from child window to parent window) + ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) ImVec2 Scroll; ImVec2 ScrollMax; ImVec2 ScrollTarget; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change) @@ -2528,6 +2539,7 @@ struct IMGUI_API ImGuiWindow bool Collapsed; // Set when collapsing window to become only title-bar bool WantCollapseToggle; bool SkipItems; // Set when items can safely be all clipped (e.g. window not visible or collapsed) + bool SkipRefresh; // [EXPERIMENTAL] Reuse previous frame drawn contents, Begin() returns false. bool Appearing; // Set during the frame where the window is appearing (or re-appearing) bool Hidden; // Do not display (== HiddenFrames*** > 0) bool IsFallbackWindow; // Set on the "Debug##Default" window. @@ -2540,7 +2552,6 @@ struct IMGUI_API ImGuiWindow 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; bool AutoFitOnlyGrows; ImGuiDir AutoPosLastDirection; @@ -2609,10 +2620,8 @@ struct IMGUI_API ImGuiWindow // We don't use g.FontSize because the window may be != g.CurrentWindow. ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } float CalcFontSize() const { ImGuiContext& g = *Ctx; float scale = g.FontBaseSize * FontWindowScale; if (ParentWindow) scale *= ParentWindow->FontWindowScale; return scale; } - float TitleBarHeight() const { ImGuiContext& g = *Ctx; return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + g.Style.FramePadding.y * 2.0f; } - ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); } - float MenuBarHeight() const { ImGuiContext& g = *Ctx; return (Flags & ImGuiWindowFlags_MenuBar) ? DC.MenuBarOffset.y + CalcFontSize() + g.Style.FramePadding.y * 2.0f : 0.0f; } - ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); } + ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight)); } + ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight; return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight); } }; //----------------------------------------------------------------------------- @@ -2767,13 +2776,24 @@ struct ImGuiTableColumn }; // Transient cell data stored per row. -// sizeof() ~ 6 +// sizeof() ~ 6 bytes struct ImGuiTableCellData { ImU32 BgColor; // Actual color ImGuiTableColumnIdx Column; // Column number }; +// Parameters for TableAngledHeadersRowEx() +// This may end up being refactored for more general purpose. +// sizeof() ~ 12 bytes +struct ImGuiTableHeaderData +{ + ImGuiTableColumnIdx Index; // Column index + ImU32 TextColor; + ImU32 BgColor0; + ImU32 BgColor1; +}; + // Per-instance data that needs preserving across frames (seemingly most others do not need to be preserved aside from debug needs. Does that means they could be moved to ImGuiTableTempData?) // sizeof() ~ 24 bytes struct ImGuiTableInstanceData @@ -2788,8 +2808,7 @@ struct ImGuiTableInstanceData ImGuiTableInstanceData() { TableInstanceID = 0; LastOuterHeight = LastTopHeadersRowHeight = LastFrozenHeight = 0.0f; HoveredRowLast = HoveredRowNext = -1; } }; -// FIXME-TABLE: more transient data could be stored in a stacked ImGuiTableTempData: e.g. SortSpecs, incoming RowData -// sizeof() ~ 580 bytes + heap allocs described in TableBeginInitMemory() +// sizeof() ~ 592 bytes + heap allocs described in TableBeginInitMemory() struct IMGUI_API ImGuiTable { ImGuiID ID; @@ -2859,7 +2878,7 @@ struct IMGUI_API ImGuiTable ImGuiTableSortSpecs SortSpecs; // Public facing sorts specs, this is what we return in TableGetSortSpecs() ImGuiTableColumnIdx SortSpecsCount; ImGuiTableColumnIdx ColumnsEnabledCount; // Number of enabled columns (<= ColumnsCount) - ImGuiTableColumnIdx ColumnsEnabledFixedCount; // Number of enabled columns (<= ColumnsCount) + ImGuiTableColumnIdx ColumnsEnabledFixedCount; // Number of enabled columns using fixed width (<= ColumnsCount) ImGuiTableColumnIdx DeclColumnsCount; // Count calls to TableSetupColumn() ImGuiTableColumnIdx AngledHeadersCount; // Count columns with angled headers ImGuiTableColumnIdx HoveredColumnBody; // Index of column whose visible region is being hovered. Important: == ColumnsCount when hovering empty region after the right-most column! @@ -2912,12 +2931,14 @@ struct IMGUI_API ImGuiTable // 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. -// sizeof() ~ 120 bytes. +// FIXME-TABLE: more transient data could be stored in a stacked ImGuiTableTempData: e.g. SortSpecs. +// sizeof() ~ 136 bytes. struct IMGUI_API ImGuiTableTempData { int TableIndex; // Index in g.Tables.Buf[] pool float LastTimeActive; // Last timestamp this structure was used float AngledHeadersExtraWidth; // Used in EndTable() + ImVector AngledHeadersRequests; // Used in TableAngledHeadersRow() ImVec2 UserOuterSize; // outer_size.x passed to BeginTable() ImDrawListSplitter DrawSplitter; @@ -2981,7 +3002,7 @@ namespace ImGui { // Windows // We should always have a CurrentWindow in the stack (there is an implicit "Debug" window) - // If this ever crash because g.CurrentWindow is NULL it means that either + // If this ever crashes because g.CurrentWindow is NULL, it means that either: // - ImGui::NewFrame() has never been called, which is illegal. // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal. inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; } @@ -2989,6 +3010,7 @@ namespace ImGui IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id); IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); + IMGUI_API void UpdateWindowSkipRefresh(ImGuiWindow* window); IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window); IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy); IMGUI_API bool IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent); @@ -3014,6 +3036,9 @@ namespace ImGui IMGUI_API int FindWindowDisplayIndex(ImGuiWindow* window); IMGUI_API ImGuiWindow* FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* window); + // Windows: Idle, Refresh Policies [EXPERIMENTAL] + IMGUI_API void SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags); + // Fonts, drawing IMGUI_API void SetCurrentFont(ImFont* font); inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; } @@ -3029,6 +3054,7 @@ namespace ImGui // NewFrame IMGUI_API void UpdateInputEvents(bool trickle_fast_inputs); IMGUI_API void UpdateHoveredWindowAndCaptureFlags(); + IMGUI_API void FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window); IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window); IMGUI_API void UpdateMouseMovingWindowNewFrame(); IMGUI_API void UpdateMouseMovingWindowEndFrame(); @@ -3108,6 +3134,8 @@ namespace ImGui IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); IMGUI_API void PopItemFlag(); IMGUI_API const ImGuiDataVarInfo* GetStyleVarInfo(ImGuiStyleVar idx); + IMGUI_API void BeginDisabledOverrideReenable(); + IMGUI_API void EndDisabledOverrideReenable(); // 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. @@ -3169,23 +3197,21 @@ namespace ImGui // Inputs // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions. - inline bool IsNamedKey(ImGuiKey key) { return key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END; } - inline bool IsNamedKeyOrModKey(ImGuiKey key) { return (key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END) || key == ImGuiMod_Ctrl || key == ImGuiMod_Shift || key == ImGuiMod_Alt || key == ImGuiMod_Super || key == ImGuiMod_Shortcut; } - inline bool IsLegacyKey(ImGuiKey key) { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; } - inline bool IsKeyboardKey(ImGuiKey key) { return key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END; } - inline bool IsGamepadKey(ImGuiKey key) { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; } - inline bool IsMouseKey(ImGuiKey key) { return key >= ImGuiKey_Mouse_BEGIN && key < ImGuiKey_Mouse_END; } - inline bool IsAliasKey(ImGuiKey key) { return key >= ImGuiKey_Aliases_BEGIN && key < ImGuiKey_Aliases_END; } - inline bool IsModKey(ImGuiKey key) { return key >= ImGuiKey_LeftCtrl && key <= ImGuiKey_RightSuper; } - ImGuiKeyChord FixupKeyChord(ImGuiContext* ctx, ImGuiKeyChord key_chord); - inline ImGuiKey ConvertSingleModFlagToKey(ImGuiContext* ctx, ImGuiKey key) + inline bool IsNamedKey(ImGuiKey key) { return key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END; } + inline bool IsNamedKeyOrMod(ImGuiKey key) { return (key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END) || key == ImGuiMod_Ctrl || key == ImGuiMod_Shift || key == ImGuiMod_Alt || key == ImGuiMod_Super; } + inline bool IsLegacyKey(ImGuiKey key) { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; } + inline bool IsKeyboardKey(ImGuiKey key) { return key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END; } + inline bool IsGamepadKey(ImGuiKey key) { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; } + inline bool IsMouseKey(ImGuiKey key) { return key >= ImGuiKey_Mouse_BEGIN && key < ImGuiKey_Mouse_END; } + inline bool IsAliasKey(ImGuiKey key) { return key >= ImGuiKey_Aliases_BEGIN && key < ImGuiKey_Aliases_END; } + inline bool IsModKey(ImGuiKey key) { return key >= ImGuiKey_LeftCtrl && key <= ImGuiKey_RightSuper; } + ImGuiKeyChord FixupKeyChord(ImGuiKeyChord key_chord); + inline ImGuiKey ConvertSingleModFlagToKey(ImGuiKey key) { - ImGuiContext& g = *ctx; if (key == ImGuiMod_Ctrl) return ImGuiKey_ReservedForModCtrl; if (key == ImGuiMod_Shift) return ImGuiKey_ReservedForModShift; if (key == ImGuiMod_Alt) return ImGuiKey_ReservedForModAlt; if (key == ImGuiMod_Super) return ImGuiKey_ReservedForModSuper; - if (key == ImGuiMod_Shortcut) return (g.IO.ConfigMacOSXBehaviors ? ImGuiKey_ReservedForModSuper : ImGuiKey_ReservedForModCtrl); return key; } @@ -3205,7 +3231,7 @@ namespace ImGui // [EXPERIMENTAL] Low-Level: Key/Input Ownership // - The idea is that instead of "eating" a given input, we can link to an owner id. // - Ownership is most often claimed as a result of reacting to a press/down event (but occasionally may be claimed ahead). - // - Input queries can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_None (== -1) or a custom ID. + // - Input queries can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_NoOwner (== -1) or a custom ID. // - Legacy input queries (without specifying an owner or _Any or _None) are equivalent to using ImGuiKeyOwner_Any (== 0). // - Input ownership is automatically released on the frame after a key is released. Therefore: // - for ownership registration happening as a result of a down/press event, the SetKeyOwner() call may be done once (common case). @@ -3213,12 +3239,12 @@ namespace ImGui // - SetItemKeyOwner() is a shortcut for common simple case. A custom widget will probably want to call SetKeyOwner() multiple times directly based on its interaction state. // - This is marked experimental because not all widgets are fully honoring the Set/Test idioms. We will need to move forward step by step. // Please open a GitHub Issue to submit your usage scenario or if there's a use case you need solved. - IMGUI_API ImGuiID GetKeyOwner(ImGuiKey key); - IMGUI_API void SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0); - IMGUI_API void SetKeyOwnersForKeyChord(ImGuiKeyChord key, ImGuiID owner_id, ImGuiInputFlags flags = 0); - IMGUI_API void SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags = 0); // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'. - IMGUI_API bool TestKeyOwner(ImGuiKey key, ImGuiID owner_id); // Test that key is either not owned, either owned by 'owner_id' - inline ImGuiKeyOwnerData* GetKeyOwnerData(ImGuiContext* ctx, ImGuiKey key) { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(ctx, key); IM_ASSERT(IsNamedKey(key)); return &ctx->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; } + IMGUI_API ImGuiID GetKeyOwner(ImGuiKey key); + IMGUI_API void SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0); + IMGUI_API void SetKeyOwnersForKeyChord(ImGuiKeyChord key, ImGuiID owner_id, ImGuiInputFlags flags = 0); + IMGUI_API void SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags = 0); // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'. + IMGUI_API bool TestKeyOwner(ImGuiKey key, ImGuiID owner_id); // Test that key is either not owned, either owned by 'owner_id' + inline ImGuiKeyOwnerData* GetKeyOwnerData(ImGuiContext* ctx, ImGuiKey key) { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(key); IM_ASSERT(IsNamedKey(key)); return &ctx->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; } // [EXPERIMENTAL] High-Level: Input Access functions w/ support for Key/Input Ownership // - Important: legacy IsKeyPressed(ImGuiKey, bool repeat=true) _DEFAULTS_ to repeat, new IsKeyPressed() requires _EXPLICIT_ ImGuiInputFlags_Repeat flag. @@ -3226,32 +3252,32 @@ namespace ImGui // - Specifying a value for 'ImGuiID owner' will test that EITHER the key is NOT owned (UNLESS locked), EITHER the key is owned by 'owner'. // Legacy functions use ImGuiKeyOwner_Any meaning that they typically ignore ownership, unless a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease. // - Binding generators may want to ignore those for now, or suffix them with Ex() until we decide if this gets moved into public API. - IMGUI_API bool IsKeyDown(ImGuiKey key, ImGuiID owner_id); - IMGUI_API bool IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0); // Important: when transitioning from old to new IsKeyPressed(): old API has "bool repeat = true", so would default to repeat. New API requiress explicit ImGuiInputFlags_Repeat. - IMGUI_API bool IsKeyReleased(ImGuiKey key, ImGuiID owner_id); - IMGUI_API bool IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id); - IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0); - IMGUI_API bool IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id); - IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id); - - // [EXPERIMENTAL] Shortcut Routing - // - ImGuiKeyChord = a ImGuiKey optionally OR-red with ImGuiMod_Alt/ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Super. - // ImGuiKey_C (accepted by functions taking ImGuiKey or ImGuiKeyChord) - // ImGuiKey_C | ImGuiMod_Ctrl (accepted by functions taking ImGuiKeyChord) - // ONLY ImGuiMod_XXX values are legal to 'OR' with an ImGuiKey. You CANNOT 'OR' two ImGuiKey values. - // - When using one of the routing flags (e.g. ImGuiInputFlags_RouteFocused): routes requested ahead of time given a chord (key + modifiers) and a routing policy. - // - Routes are resolved during NewFrame(): if keyboard modifiers are matching current ones: SetKeyOwner() is called + route is granted for the frame. - // - Route is granted to a single owner. When multiple requests are made we have policies to select the winning route. - // - Multiple read sites may use the same owner id and will all get the granted route. - // - For routing: when owner_id is 0 we use the current Focus Scope ID as a default owner in order to identify our location. - // - TL;DR; - // - IsKeyChordPressed() compares mods + call IsKeyPressed() -> function has no side-effect. - // - Shortcut() submits a route then if currently can be routed calls IsKeyChordPressed() -> function has (desirable) side-effects. - IMGUI_API bool IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags = 0); - IMGUI_API void SetNextItemShortcut(ImGuiKeyChord key_chord); - IMGUI_API bool Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0); - IMGUI_API bool SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags = 0); // owner_id needs to be explicit and cannot be 0 - IMGUI_API bool TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id); + IMGUI_API bool IsKeyDown(ImGuiKey key, ImGuiID owner_id); + IMGUI_API bool IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0); // Important: when transitioning from old to new IsKeyPressed(): old API has "bool repeat = true", so would default to repeat. New API requiress explicit ImGuiInputFlags_Repeat. + IMGUI_API bool IsKeyReleased(ImGuiKey key, ImGuiID owner_id); + IMGUI_API bool IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id = 0); + IMGUI_API bool IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id); + IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0); + IMGUI_API bool IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id); + IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id); + + // Shortcut Testing & Routing + // - Set Shortcut() and SetNextItemShortcut() in imgui.h + // - When a policy (except for ImGuiInputFlags_RouteAlways *) is set, Shortcut() will register itself with SetShortcutRouting(), + // allowing the system to decide where to route the input among other route-aware calls. + // (* using ImGuiInputFlags_RouteAlways is roughly equivalent to calling IsKeyChordPressed(key) and bypassing route registration and check) + // - When using one of the routing option: + // - The default route is ImGuiInputFlags_RouteFocused (accept inputs if window is in focus stack. Deep-most focused window takes inputs. ActiveId takes inputs over deep-most focused window.) + // - Routes are requested given a chord (key + modifiers) and a routing policy. + // - Routes are resolved during NewFrame(): if keyboard modifiers are matching current ones: SetKeyOwner() is called + route is granted for the frame. + // - Each route may be granted to a single owner. When multiple requests are made we have policies to select the winning route (e.g. deep most window). + // - Multiple read sites may use the same owner id can all access the granted route. + // - When owner_id is 0 we use the current Focus Scope ID as a owner ID in order to identify our location. + // - You can chain two unrelated windows in the focus stack using SetWindowParentWindowForFocusRoute() + // e.g. if you have a tool window associated to a document, and you want document shortcuts to run when the tool is focused. + IMGUI_API bool Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id); + IMGUI_API bool SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id); // owner_id needs to be explicit and cannot be 0 + IMGUI_API bool TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id); IMGUI_API ImGuiKeyRoutingData* GetShortcutRoutingData(ImGuiKeyChord key_chord); // [EXPERIMENTAL] Focus Scope @@ -3301,7 +3327,7 @@ namespace ImGui IMGUI_API float TableGetHeaderAngledMaxLabelWidth(); IMGUI_API void TablePushBackgroundChannel(); IMGUI_API void TablePopBackgroundChannel(); - IMGUI_API void TableAngledHeadersRowEx(float angle, float max_label_width = 0.0f); + IMGUI_API void TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count); // Tables: Internals inline ImGuiTable* GetCurrentTable() { ImGuiContext& g = *GImGui; return g.CurrentTable; } @@ -3436,7 +3462,7 @@ namespace ImGui IMGUI_API const ImGuiDataTypeInfo* DataTypeGetInfo(ImGuiDataType data_type); IMGUI_API int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format); IMGUI_API void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const void* arg_1, const void* arg_2); - IMGUI_API bool DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format); + IMGUI_API bool DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format, void* p_data_when_empty = NULL); IMGUI_API int DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2); IMGUI_API bool DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max); @@ -3447,6 +3473,7 @@ namespace ImGui IMGUI_API bool TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL); inline bool TempInputIsActive(ImGuiID id) { ImGuiContext& g = *GImGui; return (g.ActiveId == id && g.TempInputId == id); } inline ImGuiInputTextState* GetInputTextState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.InputTextState.ID == id) ? &g.InputTextState : NULL; } // Get input text state if active + IMGUI_API void SetNextItemRefVal(ImGuiDataType data_type, void* p_data); // Color IMGUI_API void ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags); @@ -3478,6 +3505,7 @@ namespace ImGui IMGUI_API void DebugDrawCursorPos(ImU32 col = IM_COL32(255, 0, 0, 255)); IMGUI_API void DebugDrawLineExtents(ImU32 col = IM_COL32(255, 0, 0, 255)); IMGUI_API void DebugDrawItemRect(ImU32 col = IM_COL32(255, 0, 0, 255)); + IMGUI_API void DebugTextUnformattedWithLocateItem(const char* line_begin, const char* line_end); IMGUI_API void DebugLocateItem(ImGuiID target_id); // Call sparingly: only 1 at the same time! IMGUI_API void DebugLocateItemOnHover(ImGuiID target_id); // Only call on reaction to a mouse Hover: because only 1 at the same time! IMGUI_API void DebugLocateItemResolveWithLastItem(); @@ -3510,6 +3538,8 @@ namespace ImGui inline void SetItemUsingMouseWheel() { SetItemKeyOwner(ImGuiKey_MouseWheelY); } // Changed in 1.89 inline bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0) { return TreeNodeUpdateNextOpen(id, flags); } // Renamed in 1.89 + //inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // Removed in 1.87: Mapping from named key is always identity! + // Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets which used FocusableItemRegister(): // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool tab_focused = FocusableItemRegister(...)' // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0' @@ -3517,9 +3547,6 @@ namespace ImGui //inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd() //inline void FocusableItemUnregister(ImGuiWindow* window) // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem #endif -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // Removed in 1.87: Mapping from named key is always identity! -#endif } // namespace ImGui @@ -3579,4 +3606,4 @@ extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiI #pragma warning (pop) #endif -#endif // #ifndef IMGUI_DISABLE +#endif // #ifndef IMGUI_DISABLE \ No newline at end of file diff --git a/platforms/desktop-shared/imgui/imgui_tables.cpp b/platforms/desktop-shared/imgui/imgui_tables.cpp index 260df1a..922231e 100644 --- a/platforms/desktop-shared/imgui/imgui_tables.cpp +++ b/platforms/desktop-shared/imgui/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.4 +// dear imgui, v1.90.8 // (tables and columns code) /* @@ -24,8 +24,9 @@ Index of this file: */ // Navigating this file: -// - In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. -// - With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. +// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. +// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments. +// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments. //----------------------------------------------------------------------------- // [SECTION] Commentary @@ -227,6 +228,7 @@ Index of this file: #pragma clang diagnostic ignored "-Wenum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') #pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access #elif defined(__GNUC__) #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked @@ -318,6 +320,12 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG IM_ASSERT(inner_width >= 0.0f); // If an outer size is specified ahead we will be able to early out when not visible. Exact clipping criteria may evolve. + // FIXME: coarse clipping because access to table data causes two issues: + // - instance numbers varying/unstable. may not be a direct problem for users, but could make outside access broken or confusing, e.g. TestEngine. + // - can't implement support for ImGuiChildFlags_ResizeY as we need to somehow pull the height data from somewhere. this also needs stable instance numbers. + // The side-effects of accessing table data on coarse clip would be: + // - always reserving the pooled ImGuiTable data ahead for a fully clipped table (minor IMHO). Also the 'outer_window_is_measuring_size' criteria may already be defeating this in some situations. + // - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[]. const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0; const ImVec2 avail_size = GetContentRegionAvail(); const ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f); @@ -326,6 +334,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size) { ItemSize(outer_rect); + ItemAdd(outer_rect, id); return false; } @@ -335,7 +344,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // Acquire storage for the table ImGuiTable* table = g.Tables.GetOrAddByKey(id); - const ImGuiTableFlags table_last_flags = table->Flags; // Acquire temporary buffers const int table_idx = g.Tables.GetIndex(table); @@ -353,6 +361,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // Initialize const int previous_frame_active = table->LastFrameActive; const int instance_no = (previous_frame_active != g.FrameCount) ? 0 : table->InstanceCurrent + 1; + const ImGuiTableFlags previous_flags = table->Flags; table->ID = id; table->Flags = flags; table->LastFrameActive = g.FrameCount; @@ -399,7 +408,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG SetNextWindowContentSize(ImVec2(override_content_size.x != FLT_MAX ? override_content_size.x : 0.0f, override_content_size.y != FLT_MAX ? override_content_size.y : 0.0f)); // Reset scroll if we are reactivating it - if ((table_last_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0) + if ((previous_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0) SetNextWindowScroll(ImVec2(0.0f, 0.0f)); // Create scrolling region (without border and zero window padding) @@ -428,6 +437,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // For non-scrolling tables, WorkRect == OuterRect == InnerRect. // But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable(). table->WorkRect = table->OuterRect = table->InnerRect = outer_rect; + table->HasScrollbarYPrev = table->HasScrollbarYCurr = false; } // Push a standardized ID for both child-using and not-child-using tables @@ -498,6 +508,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->DeclColumnsCount = table->AngledHeadersCount = 0; if (previous_frame_active + 1 < g.FrameCount) table->IsActiveIdInTable = false; + table->AngledHeadersHeight = 0.0f; temp_data->AngledHeadersExtraWidth = 0.0f; // Using opaque colors facilitate overlapping lines of the grid, otherwise we'd need to improve TableDrawBorders() @@ -511,7 +522,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly. inner_window->DC.CurrentTableIdx = table_idx; - if ((table_last_flags & ImGuiTableFlags_Reorderable) && (flags & ImGuiTableFlags_Reorderable) == 0) + if ((previous_flags & ImGuiTableFlags_Reorderable) && (flags & ImGuiTableFlags_Reorderable) == 0) table->IsResetDisplayOrderRequest = true; // Mark as used to avoid GC @@ -1066,6 +1077,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // - ClipRect.Max.x: using WorkMaxX instead of MaxX (aka including padding) makes things more consistent when resizing down, tho slightly detrimental to visibility in very-small column. // - ClipRect.Max.x: using MaxX makes it easier for header to receive hover highlight with no discontinuity and display sorting arrow. // - FIXME-TABLE: We want equal width columns to have equal (ClipRect.Max.x - WorkMinX) width, which means ClipRect.max.x cannot stray off host_clip_rect.Max.x else right-most column may appear shorter. + const float previous_instance_work_min_x = column->WorkMinX; column->WorkMinX = column->MinX + table->CellPaddingX + table->CellSpacingX1; column->WorkMaxX = column->MaxX - table->CellPaddingX - table->CellSpacingX2; // Expected max column->ItemWidth = ImTrunc(column->WidthGiven * 0.65f); @@ -1118,8 +1130,22 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // column->WorkMinX = ImLerp(column->WorkMinX, ImMax(column->StartX, column->MaxX - column->ContentWidthRowsUnfrozen), 0.5f); // Reset content width variables - column->ContentMaxXFrozen = column->ContentMaxXUnfrozen = column->WorkMinX; - column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX; + if (table->InstanceCurrent == 0) + { + column->ContentMaxXFrozen = column->WorkMinX; + column->ContentMaxXUnfrozen = column->WorkMinX; + column->ContentMaxXHeadersUsed = column->WorkMinX; + column->ContentMaxXHeadersIdeal = column->WorkMinX; + } + else + { + // As we store an absolute value to make per-cell updates faster, we need to offset values used for width computation. + const float offset_from_previous_instance = column->WorkMinX - previous_instance_work_min_x; + column->ContentMaxXFrozen += offset_from_previous_instance; + column->ContentMaxXUnfrozen += offset_from_previous_instance; + column->ContentMaxXHeadersUsed += offset_from_previous_instance; + column->ContentMaxXHeadersIdeal += offset_from_previous_instance; + } // Don't decrement auto-fit counters until container window got a chance to submit its items if (table->HostSkipItems == false) @@ -1238,9 +1264,9 @@ void ImGui::TableUpdateBorders(ImGuiTable* table) // really problematic (whereas the actual visual will be displayed in EndTable() and using the current frame height). // Actual columns highlight/render will be performed in EndTable() and not be affected. ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); - const float hit_half_width = TABLE_RESIZE_SEPARATOR_HALF_THICKNESS; + const float hit_half_width = ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale); const float hit_y1 = (table->FreezeRowsCount >= 1 ? table->OuterRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight; - const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight); + const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight - table->AngledHeadersHeight); const float hit_y2_head = hit_y1 + table_instance->LastTopHeadersRowHeight; for (int order_n = 0; order_n < table->ColumnsCount; order_n++) @@ -1415,7 +1441,7 @@ void ImGui::EndTable() if (table->ResizedColumn != -1 && table->InstanceCurrent == table->InstanceInteracted) { ImGuiTableColumn* column = &table->Columns[table->ResizedColumn]; - const float new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + TABLE_RESIZE_SEPARATOR_HALF_THICKNESS); + const float new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale)); const float new_width = ImTrunc(new_x2 - column->MinX - table->CellSpacingX1 - table->CellPaddingX * 2.0f); table->ResizedColumnNextWidth = new_width; } @@ -1444,7 +1470,10 @@ void ImGui::EndTable() // CursorPosPrevLine and CursorMaxPos manually. That should be a more general layout feature, see same problem e.g. #3414) if (inner_window != outer_window) { + short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask; + inner_window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // So empty table don't appear to navigate differently. EndChild(); + inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask; } else { @@ -1462,9 +1491,13 @@ void ImGui::EndTable() } else if (temp_data->UserOuterSize.x <= 0.0f) { - const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((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 - 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)); + // Some references for this: #7651 + tests "table_reported_size", "table_reported_size_outer" equivalent Y block + // - Checking for ImGuiTableFlags_ScrollX/ScrollY flag makes us a frame ahead when disabling those flags. + // - FIXME-TABLE: Would make sense to pre-compute expected scrollbar visibility/sizes to generally save a frame of feedback. + const float inner_content_max_x = table->OuterRect.Min.x + table->ColumnsAutoFitWidth; // Slightly misleading name but used for code symmetry with inner_content_max_y + const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.x : 0.0f); + outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, inner_content_max_x + decoration_size - temp_data->UserOuterSize.x); + outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, inner_content_max_x + decoration_size)); } else { @@ -1472,9 +1505,9 @@ void ImGui::EndTable() } if (temp_data->UserOuterSize.y <= 0.0f) { - const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.y : 0.0f; + const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.y : 0.0f; 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)); + outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y + decoration_size)); } else { @@ -1890,7 +1923,7 @@ void ImGui::TableEndRow(ImGuiTable* table) if (is_visible) { // Update data for TableGetHoveredRow() - if (table->HoveredColumnBody != -1 && g.IO.MousePos.y >= bg_y1 && g.IO.MousePos.y < bg_y2) + if (table->HoveredColumnBody != -1 && g.IO.MousePos.y >= bg_y1 && g.IO.MousePos.y < bg_y2 && table_instance->HoveredRowNext < 0) table_instance->HoveredRowNext = table->CurrentRow; // Decide of background color for the row @@ -1945,7 +1978,8 @@ void ImGui::TableEndRow(ImGuiTable* table) cell_bg_rect.ClipWith(table->BgClipRect); cell_bg_rect.Min.x = ImMax(cell_bg_rect.Min.x, column->ClipRect.Min.x); // So that first column after frozen one gets clipped when scrolling cell_bg_rect.Max.x = ImMin(cell_bg_rect.Max.x, column->MaxX); - window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor); + if (cell_bg_rect.Min.y < cell_bg_rect.Max.y) + window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor); } } @@ -2766,7 +2800,7 @@ ImGuiTableSortSpecs* ImGui::TableGetSortSpecs() static inline ImGuiSortDirection TableGetColumnAvailSortDirection(ImGuiTableColumn* column, int n) { IM_ASSERT(n < column->SortDirectionsAvailCount); - return (column->SortDirectionsAvailList >> (n << 1)) & 0x03; + return (ImGuiSortDirection)((column->SortDirectionsAvailList >> (n << 1)) & 0x03); } // Fix sort direction if currently set on a value which is unavailable (e.g. activating NoSortAscending/NoSortDescending) @@ -2907,6 +2941,7 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table) } // Write output + // May be able to move all SortSpecs data from table (48 bytes) to ImGuiTableTempData if we decide to write it back on every BeginTable() ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data; if (dirty && sort_specs != NULL) for (int column_n = 0; column_n < table->ColumnsCount; column_n++) @@ -2919,7 +2954,7 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table) sort_spec->ColumnUserID = column->UserID; sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n; sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder; - sort_spec->SortDirection = column->SortDirection; + sort_spec->SortDirection = (ImGuiSortDirection)column->SortDirection; } table->SortSpecs.Specs = sort_specs; @@ -2967,7 +3002,7 @@ float ImGui::TableGetHeaderAngledMaxLabelWidth() // [Public] This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn(). // The intent is that advanced users willing to create customized headers would not need to use this helper -// and can create their own! For example: TableHeader() may be preceeded by Checkbox() or other custom widgets. +// and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets. // See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this. // This code is constructed to not make much use of internal functions, as it is intended to be a template to copy. // FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public. @@ -3153,15 +3188,43 @@ void ImGui::TableHeader(const char* label) } // Unlike TableHeadersRow() it is not expected that you can reimplement or customize this with custom widgets. -// FIXME: highlight without ImGuiTableFlags_HighlightHoveredColumn // FIXME: No hit-testing/button on the angled header. void ImGui::TableAngledHeadersRow() { ImGuiContext& g = *GImGui; - TableAngledHeadersRowEx(g.Style.TableAngledHeadersAngle, 0.0f); + ImGuiTable* table = g.CurrentTable; + ImGuiTableTempData* temp_data = table->TempData; + temp_data->AngledHeadersRequests.resize(0); + temp_data->AngledHeadersRequests.reserve(table->ColumnsEnabledCount); + + // Which column needs highlight? + const ImGuiID row_id = GetID("##AngledHeaders"); + ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); + int highlight_column_n = table->HighlightColumnHeader; + if (highlight_column_n == -1 && table->HoveredColumnBody != -1) + if (table_instance->HoveredRowLast == 0 && table->HoveredColumnBorder == -1 && (g.ActiveId == 0 || g.ActiveId == row_id || (table->IsActiveIdInTable || g.DragDropActive))) + highlight_column_n = table->HoveredColumnBody; + + // Build up request + ImU32 col_header_bg = GetColorU32(ImGuiCol_TableHeaderBg); + ImU32 col_text = GetColorU32(ImGuiCol_Text); + for (int order_n = 0; order_n < table->ColumnsCount; order_n++) + if (IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n)) + { + const int column_n = table->DisplayOrderToIndex[order_n]; + ImGuiTableColumn* column = &table->Columns[column_n]; + if ((column->Flags & ImGuiTableColumnFlags_AngledHeader) == 0) // Note: can't rely on ImGuiTableColumnFlags_IsVisible test here. + continue; + ImGuiTableHeaderData request = { (ImGuiTableColumnIdx)column_n, col_text, col_header_bg, (column_n == highlight_column_n) ? GetColorU32(ImGuiCol_Header) : 0 }; + temp_data->AngledHeadersRequests.push_back(request); + } + + // Render row + TableAngledHeadersRowEx(row_id, g.Style.TableAngledHeadersAngle, 0.0f, temp_data->AngledHeadersRequests.Data, temp_data->AngledHeadersRequests.Size); } -void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width) +// Important: data must be fed left to right +void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count) { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; @@ -3185,7 +3248,7 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width) // Calculate our base metrics and set angled headers data _before_ the first call to TableNextRow() // FIXME-STYLE: Would it be better for user to submit 'max_label_width' or 'row_height' ? One can be derived from the other. const float header_height = g.FontSize + g.Style.CellPadding.x * 2.0f; - const float row_height = ImFabs(ImRotate(ImVec2(max_label_width, flip_label ? +header_height : -header_height), cos_a, sin_a).y); + const float row_height = ImTrunc(ImFabs(ImRotate(ImVec2(max_label_width, flip_label ? +header_height : -header_height), cos_a, sin_a).y)); table->AngledHeadersHeight = row_height; table->AngledHeadersSlope = (sin_a != 0.0f) ? (cos_a / sin_a) : 0.0f; const ImVec2 header_angled_vector = unit_right * (row_height / -sin_a); // vector from bottom-left to top-left, and from bottom-right to top-right @@ -3203,28 +3266,22 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width) draw_list->AddRectFilled(ImVec2(table->BgClipRect.Min.x, row_r.Min.y), ImVec2(table->BgClipRect.Max.x, row_r.Max.y), GetColorU32(ImGuiCol_TableHeaderBg, 0.25f)); // FIXME-STYLE: Change row background with an arbitrary color. PushClipRect(ImVec2(clip_rect_min_x, table->BgClipRect.Min.y), table->BgClipRect.Max, true); // Span all columns - const ImGuiID row_id = GetID("##AngledHeaders"); ButtonBehavior(row_r, row_id, NULL, NULL); KeepAliveID(row_id); - ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); - int highlight_column_n = table->HighlightColumnHeader; - if (highlight_column_n == -1 && table->HoveredColumnBody != -1) - if (table_instance->HoveredRowLast == 0 && table->HoveredColumnBorder == -1 && (g.ActiveId == 0 || g.ActiveId == row_id || (table->IsActiveIdInTable || g.DragDropActive))) - highlight_column_n = table->HoveredColumnBody; + const float ascent_scaled = g.Font->Ascent * (g.FontSize / g.Font->FontSize); // FIXME: Standardize those scaling factors better + const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f); + const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component + const ImVec2 align = g.Style.TableAngledHeadersTextAlign; // Draw background and labels in first pass, then all borders. float max_x = 0.0f; - ImVec2 padding = g.Style.CellPadding; // We will always use swapped component for (int pass = 0; pass < 2; pass++) - for (int order_n = 0; order_n < table->ColumnsCount; order_n++) + for (int order_n = 0; order_n < data_count; order_n++) { - if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n)) - continue; - const int column_n = table->DisplayOrderToIndex[order_n]; + const ImGuiTableHeaderData* request = &data[order_n]; + const int column_n = request->Index; ImGuiTableColumn* column = &table->Columns[column_n]; - if ((column->Flags & ImGuiTableColumnFlags_AngledHeader) == 0) // Note: can't rely on ImGuiTableColumnFlags_IsVisible test here. - continue; ImVec2 bg_shape[4]; bg_shape[0] = ImVec2(column->MaxX, row_r.Max.y); @@ -3234,9 +3291,8 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width) if (pass == 0) { // Draw shape - draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_TableHeaderBg)); - if (column_n == highlight_column_n) - draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_Header)); // Highlight on hover + draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor0); + draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor1); // Optional highlight max_x = ImMax(max_x, bg_shape[3].x); // Draw label @@ -3244,8 +3300,17 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width) // - Handle multiple lines manually, as we want each lines to follow on the horizontal border, rather than see a whole block rotated. const char* label_name = TableGetColumnName(table, column_n); const char* label_name_end = FindRenderedTextEnd(label_name); - const float line_off_step_x = g.FontSize / -sin_a; - float line_off_curr_x = 0.0f; + const float line_off_step_x = (g.FontSize / -sin_a); + const int label_lines = ImTextCountLines(label_name, label_name_end); + + // Left<>Right alignment + float line_off_curr_x = flip_label ? (label_lines - 1) * line_off_step_x : 0.0f; + float line_off_for_align_x = ImMax((((column->MaxX - column->MinX) - padding.x * 2.0f) - (label_lines * line_off_step_x)), 0.0f) * align.x; + line_off_curr_x += line_off_for_align_x - line_off_for_ascent_x; + + // Register header width + column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX + ImCeil(label_lines * line_off_step_x - line_off_for_align_x); + while (label_name < label_name_end) { const char* label_name_eol = strchr(label_name, '\n'); @@ -3254,26 +3319,30 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width) // FIXME: Individual line clipping for right-most column is broken for negative angles. ImVec2 label_size = CalcTextSize(label_name, label_name_eol); - float clip_width = max_label_width - padding.y; // Using padding.y*2.0f would be symetrical but hide more text. + float clip_width = max_label_width - padding.y; // Using padding.y*2.0f would be symmetrical but hide more text. float clip_height = ImMin(label_size.y, column->ClipRect.Max.x - column->WorkMinX - line_off_curr_x); ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height)); int vtx_idx_begin = draw_list->_VtxCurrentIdx; + PushStyleColor(ImGuiCol_Text, request->TextColor); RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, clip_r.Max.x, label_name, label_name_eol, &label_size); + PopStyleColor(); int vtx_idx_end = draw_list->_VtxCurrentIdx; + // Up<>Down alignment + const float available_space = ImMax(clip_width - label_size.x + ImAbs(padding.x * cos_a) * 2.0f - ImAbs(padding.y * sin_a) * 2.0f, 0.0f); + const float vertical_offset = available_space * align.y * (flip_label ? -1.0f : 1.0f); + // Rotate and offset label - ImVec2 pivot_in = ImVec2(window->ClipRect.Min.x, window->ClipRect.Min.y + label_size.y); + ImVec2 pivot_in = ImVec2(window->ClipRect.Min.x - vertical_offset, window->ClipRect.Min.y + label_size.y); ImVec2 pivot_out = ImVec2(column->WorkMinX, row_r.Max.y); - line_off_curr_x += line_off_step_x; + line_off_curr_x += flip_label ? -line_off_step_x : line_off_step_x; pivot_out += unit_right * padding.y; if (flip_label) pivot_out += unit_right * (clip_width - ImMax(0.0f, clip_width - label_size.x)); - pivot_out.x += flip_label ? line_off_curr_x - line_off_step_x : line_off_curr_x; + pivot_out.x += flip_label ? line_off_curr_x + line_off_step_x : line_off_curr_x; ShadeVertsTransformPos(draw_list, vtx_idx_begin, vtx_idx_end, pivot_in, label_cos_a, label_sin_a, pivot_out); // Rotate and offset - //if (g.IO.KeyShift) { ImDrawList* fg_dl = GetForegroundDrawList(); vtx_idx_begin = fg_dl->_VtxCurrentIdx; fg_dl->AddRect(clip_r.Min, clip_r.Max, IM_COL32(0, 255, 0, 255), 0.0f, 0, 2.0f); ShadeVertsTransformPos(fg_dl, vtx_idx_begin, fg_dl->_VtxCurrentIdx, pivot_in, label_cos_a, label_sin_a, pivot_out); } + //if (g.IO.KeyShift) { ImDrawList* fg_dl = GetForegroundDrawList(); vtx_idx_begin = fg_dl->_VtxCurrentIdx; fg_dl->AddRect(clip_r.Min, clip_r.Max, IM_COL32(0, 255, 0, 255), 0.0f, 0, 1.0f); ShadeVertsTransformPos(fg_dl, vtx_idx_begin, fg_dl->_VtxCurrentIdx, pivot_in, label_cos_a, label_sin_a, pivot_out); } - // Register header width - column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX + ImCeil(line_off_curr_x); label_name = label_name_eol + 1; } } @@ -3988,7 +4057,7 @@ float ImGui::GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offse return offset / (columns->OffMaxX - columns->OffMinX); } -static const float COLUMNS_HIT_RECT_HALF_WIDTH = 4.0f; +static const float COLUMNS_HIT_RECT_HALF_THICKNESS = 4.0f; static float GetDraggedColumnOffset(ImGuiOldColumns* columns, int column_index) { @@ -3999,7 +4068,7 @@ static float GetDraggedColumnOffset(ImGuiOldColumns* columns, int column_index) IM_ASSERT(column_index > 0); // We are not supposed to drag column 0. IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index)); - float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + COLUMNS_HIT_RECT_HALF_WIDTH - window->Pos.x; + float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale) - window->Pos.x; x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing); if ((columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths)) x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing); @@ -4314,7 +4383,7 @@ void ImGui::EndColumns() ImGuiOldColumnData* column = &columns->Columns[n]; float x = window->Pos.x + GetColumnOffset(n); const ImGuiID column_id = columns->ID + ImGuiID(n); - const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH; + const float column_hit_hw = ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale); const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2)); if (!ItemAdd(column_hit_rect, column_id, NULL, ImGuiItemFlags_NoNav)) continue; @@ -4376,4 +4445,4 @@ void ImGui::Columns(int columns_count, const char* id, bool border) //------------------------------------------------------------------------- -#endif // #ifndef IMGUI_DISABLE +#endif // #ifndef IMGUI_DISABLE \ No newline at end of file diff --git a/platforms/desktop-shared/imgui/imgui_widgets.cpp b/platforms/desktop-shared/imgui/imgui_widgets.cpp index 734950d..fc5c0a5 100644 --- a/platforms/desktop-shared/imgui/imgui_widgets.cpp +++ b/platforms/desktop-shared/imgui/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.4 +// dear imgui, v1.90.8 // (widgets code) /* @@ -75,6 +75,7 @@ Index of this file: #pragma clang diagnostic ignored "-Wenum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') #pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access #elif defined(__GNUC__) #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked @@ -122,9 +123,9 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); //------------------------------------------------------------------------- // For InputTextEx() -static bool InputTextFilterCharacter(ImGuiContext* ctx, 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(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); +static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard = false); +static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); +static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); //------------------------------------------------------------------------- // [SECTION] Widgets: Text, etc. @@ -487,7 +488,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool // Default only reacts to left mouse button if ((flags & ImGuiButtonFlags_MouseButtonMask_) == 0) - flags |= ImGuiButtonFlags_MouseButtonDefault_; + flags |= ImGuiButtonFlags_MouseButtonLeft; // Default behavior requires click + release inside bounding box if ((flags & ImGuiButtonFlags_PressedOnMask_) == 0) @@ -508,7 +509,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool #ifdef IMGUI_ENABLE_TEST_ENGINE // Alternate registration spot, for when caller didn't use ItemAdd() - if (id != 0 && g.LastItemData.ID != id) + if (g.LastItemData.ID != id) IMGUI_TEST_ENGINE_ITEM_ADD(id, bb, NULL); #endif @@ -536,6 +537,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool const ImGuiID test_owner_id = (flags & ImGuiButtonFlags_NoTestKeyOwner) ? ImGuiKeyOwner_Any : id; if (hovered) { + IM_ASSERT(id != 0); // Lazily check inside rare path. + // Poll mouse buttons // - 'mouse_button_clicked' is generally carried into ActiveIdMouseButton when setting ActiveId. // - Technically we only need some values in one code path, but since this is gated by hovered test this is fine. @@ -544,7 +547,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool for (int button = 0; button < 3; button++) if (flags & (ImGuiButtonFlags_MouseButtonLeft << button)) // Handle ImGuiButtonFlags_MouseButtonRight and ImGuiButtonFlags_MouseButtonMiddle here. { - if (IsMouseClicked(button, test_owner_id) && mouse_button_clicked == -1) { mouse_button_clicked = button; } + if (IsMouseClicked(button, ImGuiInputFlags_None, test_owner_id) && mouse_button_clicked == -1) { mouse_button_clicked = button; } if (IsMouseReleased(button, test_owner_id) && mouse_button_released == -1) { mouse_button_released = button; } } @@ -592,7 +595,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings. if (g.ActiveId == id && (item_flags & ImGuiItemFlags_ButtonRepeat)) - if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, test_owner_id, ImGuiInputFlags_Repeat)) + if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, ImGuiInputFlags_Repeat, test_owner_id)) pressed = true; } @@ -913,10 +916,10 @@ void ImGui::Scrollbar(ImGuiAxis axis) if (!window->ScrollbarX) rounding_corners |= ImDrawFlags_RoundCornersBottomRight; } - float size_avail = window->InnerRect.Max[axis] - window->InnerRect.Min[axis]; + float size_visible = window->InnerRect.Max[axis] - window->InnerRect.Min[axis]; float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f; ImS64 scroll = (ImS64)window->Scroll[axis]; - ScrollbarEx(bb, id, axis, &scroll, (ImS64)size_avail, (ImS64)size_contents, rounding_corners); + ScrollbarEx(bb, id, axis, &scroll, (ImS64)size_visible, (ImS64)size_contents, rounding_corners); window->Scroll[axis] = (float)scroll; } @@ -926,7 +929,7 @@ void ImGui::Scrollbar(ImGuiAxis axis) // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. // Still, the code should probably be made simpler.. -bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 size_avail_v, ImS64 size_contents_v, ImDrawFlags flags) +bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 size_visible_v, ImS64 size_contents_v, ImDrawFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -956,9 +959,9 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6 // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount) // But we maintain a minimum size in pixel to allow for the user to still aim inside. - IM_ASSERT(ImMax(size_contents_v, size_avail_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers. - const ImS64 win_size_v = ImMax(ImMax(size_contents_v, size_avail_v), (ImS64)1); - const float grab_h_pixels = ImClamp(scrollbar_size_v * ((float)size_avail_v / (float)win_size_v), style.GrabMinSize, scrollbar_size_v); + IM_ASSERT(ImMax(size_contents_v, size_visible_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers. + const ImS64 win_size_v = ImMax(ImMax(size_contents_v, size_visible_v), (ImS64)1); + const float grab_h_pixels = ImClamp(scrollbar_size_v * ((float)size_visible_v / (float)win_size_v), style.GrabMinSize, scrollbar_size_v); const float grab_h_norm = grab_h_pixels / scrollbar_size_v; // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). @@ -967,7 +970,7 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6 ItemAdd(bb_frame, id, NULL, ImGuiItemFlags_NoNav); ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); - const ImS64 scroll_max = ImMax((ImS64)1, size_contents_v - size_avail_v); + const ImS64 scroll_max = ImMax((ImS64)1, size_contents_v - size_visible_v); float scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max); float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Grab position in normalized space if (held && allow_interaction && grab_h_norm < 1.0f) @@ -978,29 +981,39 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6 // Click position in scrollbar normalized space (0.0f->1.0f) const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); - bool seek_absolute = false; + const int held_dir = (clicked_v_norm < grab_v_norm) ? -1 : (clicked_v_norm > grab_v_norm + grab_h_norm) ? +1 : 0; if (g.ActiveIdIsJustActivated) { // On initial click calculate the distance between mouse and the center of the grab - seek_absolute = (clicked_v_norm < grab_v_norm || clicked_v_norm > grab_v_norm + grab_h_norm); - if (seek_absolute) - g.ScrollbarClickDeltaToGrabCenter = 0.0f; - else - g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f; + g.ScrollbarSeekMode = (short)held_dir; + g.ScrollbarClickDeltaToGrabCenter = (g.ScrollbarSeekMode == 0.0f) ? clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f : 0.0f; } // Apply scroll (p_scroll_v will generally point on one member of window->Scroll) // It is ok to modify Scroll here because we are being called in Begin() after the calculation of ContentSize and before setting up our starting position - const float scroll_v_norm = ImSaturate((clicked_v_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5f) / (1.0f - grab_h_norm)); - *p_scroll_v = (ImS64)(scroll_v_norm * scroll_max); + if (g.ScrollbarSeekMode == 0) + { + // Absolute seeking + const float scroll_v_norm = ImSaturate((clicked_v_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5f) / (1.0f - grab_h_norm)); + *p_scroll_v = (ImS64)(scroll_v_norm * scroll_max); + } + else + { + // Page by page + if (IsMouseClicked(ImGuiMouseButton_Left, ImGuiInputFlags_Repeat) && held_dir == g.ScrollbarSeekMode) + { + float page_dir = (g.ScrollbarSeekMode > 0.0f) ? +1.0f : -1.0f; + *p_scroll_v = ImClamp(*p_scroll_v + (ImS64)(page_dir * size_visible_v), (ImS64)0, scroll_max); + } + } // Update values for rendering scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max); grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; - // Update distance to grab now that we have seeked and saturated - if (seek_absolute) - g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f; + // Update distance to grab now that we have seek'ed and saturated + //if (seek_absolute) + // g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f; } // Render @@ -1288,24 +1301,47 @@ void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* over if (!ItemAdd(bb, 0)) return; + // Fraction < 0.0f will display an indeterminate progress bar animation + // The value must be animated along with time, so e.g. passing '-1.0f * ImGui::GetTime()' as fraction works. + const bool is_indeterminate = (fraction < 0.0f); + if (!is_indeterminate) + fraction = ImSaturate(fraction); + + // Out of courtesy we accept a NaN fraction without crashing + float fill_n0 = 0.0f; + float fill_n1 = (fraction == fraction) ? fraction : 0.0f; + + if (is_indeterminate) + { + const float fill_width_n = 0.2f; + fill_n0 = ImFmod(-fraction, 1.0f) * (1.0f + fill_width_n) - fill_width_n; + fill_n1 = ImSaturate(fill_n0 + fill_width_n); + fill_n0 = ImSaturate(fill_n0); + } + // Render - fraction = ImSaturate(fraction); RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize)); - const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y); - RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding); + RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), fill_n0, fill_n1, style.FrameRounding); // Default displaying the fraction as percentage string, but user can override it + // Don't display text for indeterminate bars by default char overlay_buf[32]; - if (!overlay) + if (!is_indeterminate || overlay != NULL) { - ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction * 100 + 0.01f); - overlay = overlay_buf; - } + if (!overlay) + { + ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction * 100 + 0.01f); + overlay = overlay_buf; + } - ImVec2 overlay_size = CalcTextSize(overlay, NULL); - if (overlay_size.x > 0.0f) - RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f, 0.5f), &bb); + ImVec2 overlay_size = CalcTextSize(overlay, NULL); + if (overlay_size.x > 0.0f) + { + float text_x = is_indeterminate ? (bb.Min.x + bb.Max.x - overlay_size.x) * 0.5f : ImLerp(bb.Min.x, bb.Max.x, fill_n1) + style.ItemSpacing.x; + RenderTextClipped(ImVec2(ImClamp(text_x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f, 0.5f), &bb); + } + } } void ImGui::Bullet() @@ -1916,28 +1952,30 @@ bool ImGui::Combo(const char* label, int* current_item, const char* (*getter)(vo return false; // Display items - // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed) bool value_changed = false; - for (int i = 0; i < items_count; i++) - { - const char* item_text = getter(user_data, i); - if (item_text == NULL) - item_text = "*Unknown item*"; - - PushID(i); - const bool item_selected = (i == *current_item); - if (Selectable(item_text, item_selected) && *current_item != i) + ImGuiListClipper clipper; + clipper.Begin(items_count); + clipper.IncludeItemByIndex(*current_item); + while (clipper.Step()) + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { - value_changed = true; - *current_item = i; + const char* item_text = getter(user_data, i); + if (item_text == NULL) + item_text = "*Unknown item*"; + + PushID(i); + const bool item_selected = (i == *current_item); + if (Selectable(item_text, item_selected) && *current_item != i) + { + value_changed = true; + *current_item = i; + } + if (item_selected) + SetItemDefaultFocus(); + PopID(); } - if (item_selected) - SetItemDefaultFocus(); - PopID(); - } EndCombo(); - if (value_changed) MarkItemEdited(g.LastItemData.ID); @@ -2103,17 +2141,24 @@ void ImGui::DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const // User can input math operators (e.g. +100) to edit a numerical values. // NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess.. -bool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format) +bool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format, void* p_data_when_empty) { + // Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all. + const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type); + ImGuiDataTypeStorage data_backup; + memcpy(&data_backup, p_data, type_info->Size); + while (ImCharIsBlankA(*buf)) buf++; if (!buf[0]) + { + if (p_data_when_empty != NULL) + { + memcpy(p_data, p_data_when_empty, type_info->Size); + return memcmp(&data_backup, p_data, type_info->Size) != 0; + } return false; - - // Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all. - const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type); - ImGuiDataTypeTempStorage data_backup; - memcpy(&data_backup, p_data, type_info->Size); + } // Sanitize format // - For float/double we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in, so force them into %f and %lf @@ -2283,7 +2328,7 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0; const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow); const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast); - const float tweak_factor = tweak_slow ? 1.0f / 1.0f : tweak_fast ? 10.0f : 1.0f; + const float tweak_factor = tweak_slow ? 1.0f / 10.0f : tweak_fast ? 10.0f : 1.0f; adjust_delta = GetNavTweakPressedAmount(axis) * tweak_factor; v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); } @@ -2442,7 +2487,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, if (!temp_input_is_active) { // Tabbing or CTRL-clicking on Drag turns it into an InputText - const bool clicked = hovered && IsMouseClicked(0, id); + const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id); const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2 && TestKeyOwner(ImGuiKey_MouseLeft, id)); const bool make_active = (clicked || double_clicked || g.NavActivateId == id); if (make_active && (clicked || double_clicked)) @@ -3033,7 +3078,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat if (!temp_input_is_active) { // Tabbing or CTRL-clicking on Slider turns it into an input box - const bool clicked = hovered && IsMouseClicked(0, id); + const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id); const bool make_active = (clicked || g.NavActivateId == id); if (make_active && clicked) SetKeyOwner(ImGuiKey_MouseLeft, id); @@ -3195,7 +3240,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d format = DataTypeGetInfo(data_type)->PrintFmt; const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags); - const bool clicked = hovered && IsMouseClicked(0, id); + const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id); if (clicked || g.NavActivateId == id) { if (clicked) @@ -3426,18 +3471,18 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, p_data, format); ImStrTrimBlanks(data_buf); - ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_NoMarkEdited; + ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_NoMarkEdited | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint; bool value_changed = false; if (TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags)) { // Backup old value size_t data_type_size = type_info->Size; - ImGuiDataTypeTempStorage data_backup; + ImGuiDataTypeStorage data_backup; memcpy(&data_backup, p_data, data_type_size); // Apply new value (or operations) then clamp - DataTypeApplyFromText(data_buf, data_type, p_data, format); + DataTypeApplyFromText(data_buf, data_type, p_data, format, NULL); if (p_clamp_min || p_clamp_max) { if (p_clamp_min && p_clamp_max && DataTypeCompare(data_type, p_clamp_min, p_clamp_max) > 0) @@ -3453,6 +3498,13 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG return value_changed; } +void ImGui::SetNextItemRefVal(ImGuiDataType data_type, void* p_data) +{ + ImGuiContext& g = *GImGui; + g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasRefVal; + memcpy(&g.NextItemData.RefVal, p_data, DataTypeGetInfo(data_type)->Size); +} + // Note: p_data, p_step, p_step_fast are _pointers_ to a memory address holding the data. For an Input widget, p_step and p_step_fast are optional. // Read code of e.g. InputFloat(), InputInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly. bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step, const void* p_step_fast, const char* format, ImGuiInputTextFlags flags) @@ -3467,16 +3519,22 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data if (format == NULL) format = DataTypeGetInfo(data_type)->PrintFmt; + void* p_data_default = (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasRefVal) ? &g.NextItemData.RefVal : &g.DataTypeZeroValue; + char buf[64]; - DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format); + if ((flags & ImGuiInputTextFlags_DisplayEmptyRefVal) && DataTypeCompare(data_type, p_data, p_data_default) == 0) + buf[0] = 0; + else + DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format); flags |= ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselves by comparing the actual data rather than the string. + flags |= (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint; bool value_changed = false; if (p_step == NULL) { if (InputText(label, buf, IM_ARRAYSIZE(buf), flags)) - value_changed = DataTypeApplyFromText(buf, data_type, p_data, format); + value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL); } else { @@ -3486,7 +3544,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data PushID(label); SetNextItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2)); if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view - value_changed = DataTypeApplyFromText(buf, data_type, p_data, format); + value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL); IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable); // Step buttons @@ -3916,9 +3974,8 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons } // Return false to discard a character. -static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source) +static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard) { - IM_ASSERT(input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Clipboard); unsigned int c = *p_char; // Filter non-printable (NB: isprint is unreliable! see #2467) @@ -3933,7 +3990,7 @@ static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, Im apply_named_filters = false; // Override named filters below so newline and tabs can still be inserted. } - if (input_source != ImGuiInputSource_Clipboard) + if (input_source_is_clipboard == false) { // We ignore Ascii representation of delete (emitted from Backspace on OSX, see #2578, #2817) if (c == 127) @@ -3949,7 +4006,7 @@ static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, Im return false; // Generic named filters - if (apply_named_filters && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific))) + if (apply_named_filters && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint))) { // The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, "de_DE.UTF-8");' which affect the output/input of printf/scanf to use e.g. ',' instead of '.'. // The standard mandate that programs starts in the "C" locale where the decimal point is '.'. @@ -3959,7 +4016,7 @@ static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, Im // Users of non-default decimal point (in particular ',') may be affected by word-selection logic (is_word_boundary_from_right/is_word_boundary_from_left) functions. ImGuiContext& g = *ctx; const unsigned c_decimal_point = (unsigned int)g.IO.PlatformLocaleDecimalPoint; - if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific)) + if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint)) if (c == '.' || c == ',') c = c_decimal_point; @@ -4283,6 +4340,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ SetKeyOwner(ImGuiKey_PageUp, id); SetKeyOwner(ImGuiKey_PageDown, id); } + // FIXME: May be a problem to always steal Alt on OSX, would ideally still allow an uninterrupted Alt down-up to toggle menu if (is_osx) SetKeyOwner(ImGuiMod_Alt, id); } @@ -4415,15 +4473,15 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // (For Tab and Enter: Win32/SFML/Allegro are sending both keys and chars, GLFW and SDL are only sending keys. For Space they all send all threes) if ((flags & ImGuiInputTextFlags_AllowTabInput) && !is_readonly) { - if (Shortcut(ImGuiKey_Tab, id, ImGuiInputFlags_Repeat)) + if (Shortcut(ImGuiKey_Tab, ImGuiInputFlags_Repeat, id)) { unsigned int c = '\t'; // Insert TAB - if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) + if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data)) state->OnKeyPressed((int)c); } // FIXME: Implement Shift+Tab /* - if (Shortcut(ImGuiKey_Tab | ImGuiMod_Shift, id, ImGuiInputFlags_Repeat)) + if (Shortcut(ImGuiKey_Tab | ImGuiMod_Shift, ImGuiInputFlags_Repeat, id)) { } */ @@ -4431,7 +4489,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Process regular text input (before we check for Return because using some IME will effectively send a Return?) // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. - const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper); + const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeyCtrl); if (io.InputQueueCharacters.Size > 0) { if (!ignore_char_inputs && !is_readonly && !input_requested_by_nav) @@ -4441,7 +4499,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') // Skip Tab, see above. continue; - if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) + if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data)) state->OnKeyPressed((int)c); } @@ -4461,25 +4519,26 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl - const bool is_startend_key_down = is_osx && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End + const bool is_startend_key_down = is_osx && io.KeyCtrl && !io.KeySuper && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End // Using Shortcut() with ImGuiInputFlags_RouteFocused (default policy) to allow routing operations for other code (e.g. calling window trying to use CTRL+A and CTRL+B: formet would be handled by InputText) // Otherwise we could simply assume that we own the keys as we are active. const ImGuiInputFlags f_repeat = ImGuiInputFlags_Repeat; - const bool is_cut = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_X, id, f_repeat) || Shortcut(ImGuiMod_Shift | ImGuiKey_Delete, id, f_repeat)) && !is_readonly && !is_password && (!is_multiline || state->HasSelection()); - const bool is_copy = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_C, id) || Shortcut(ImGuiMod_Ctrl | ImGuiKey_Insert, id)) && !is_password && (!is_multiline || state->HasSelection()); - const bool is_paste = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_V, id, f_repeat) || Shortcut(ImGuiMod_Shift | ImGuiKey_Insert, id, f_repeat)) && !is_readonly; - const bool is_undo = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_Z, id, f_repeat)) && !is_readonly && is_undoable; - const bool is_redo = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_Y, id, f_repeat) || (is_osx && Shortcut(ImGuiMod_Shortcut | ImGuiMod_Shift | ImGuiKey_Z, id, f_repeat))) && !is_readonly && is_undoable; - const bool is_select_all = Shortcut(ImGuiMod_Shortcut | ImGuiKey_A, id); + const bool is_cut = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_X, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Delete, f_repeat, id)) && !is_readonly && !is_password && (!is_multiline || state->HasSelection()); + const bool is_copy = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, 0, id) || Shortcut(ImGuiMod_Ctrl | ImGuiKey_Insert, 0, id)) && !is_password && (!is_multiline || state->HasSelection()); + const bool is_paste = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_V, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Insert, f_repeat, id)) && !is_readonly; + const bool is_undo = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Z, f_repeat, id)) && !is_readonly && is_undoable; + const bool is_redo = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Y, f_repeat, id) || (is_osx && Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Z, f_repeat, id))) && !is_readonly && is_undoable; + const bool is_select_all = Shortcut(ImGuiMod_Ctrl | ImGuiKey_A, 0, id); // We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful. const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; const bool is_enter_pressed = IsKeyPressed(ImGuiKey_Enter, true) || IsKeyPressed(ImGuiKey_KeypadEnter, true); const bool is_gamepad_validate = nav_gamepad_active && (IsKeyPressed(ImGuiKey_NavGamepadActivate, false) || IsKeyPressed(ImGuiKey_NavGamepadInput, false)); - const bool is_cancel = Shortcut(ImGuiKey_Escape, id, f_repeat) || (nav_gamepad_active && Shortcut(ImGuiKey_NavGamepadCancel, id, f_repeat)); + const bool is_cancel = Shortcut(ImGuiKey_Escape, f_repeat, id) || (nav_gamepad_active && Shortcut(ImGuiKey_NavGamepadCancel, f_repeat, id)); // FIXME: Should use more Shortcut() and reduce IsKeyPressed()+SetKeyOwner(), but requires modifiers combination to be taken account of. + // FIXME-OSX: Missing support for Alt(option)+Right/Left = go to end of line, or next line if already in end of line. if (IsKeyPressed(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } else if (IsKeyPressed(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } else if (IsKeyPressed(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } @@ -4504,7 +4563,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { if (is_wordmove_key_down) state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT); - else if (is_osx && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) + else if (is_osx && io.KeyCtrl && !io.KeyAlt && !io.KeySuper) state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT); } state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); @@ -4524,7 +4583,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(&g, &c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) + if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data)) state->OnKeyPressed((int)c); } } @@ -4591,7 +4650,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { unsigned int c; s += ImTextCharFromUtf8(&c, s, NULL); - if (!InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, ImGuiInputSource_Clipboard)) + if (!InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, true)) continue; clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; } @@ -4674,7 +4733,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment. ImGuiInputTextFlags event_flag = 0; ImGuiKey event_key = ImGuiKey_None; - if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && Shortcut(ImGuiKey_Tab, id)) + if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && Shortcut(ImGuiKey_Tab, 0, id)) { event_flag = ImGuiInputTextFlags_CallbackCompletion; event_key = ImGuiKey_Tab; @@ -6179,13 +6238,17 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l label_end = FindRenderedTextEnd(label); const ImVec2 label_size = CalcTextSize(label, label_end, false); + const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2); // Collapsing arrow width + Spacing + const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it + const float text_width = g.FontSize + label_size.x + padding.x * 2; // Include collapsing arrow + // We vertically grow up to current line height up the typical widget height. const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), label_size.y + padding.y * 2); const bool span_all_columns = (flags & ImGuiTreeNodeFlags_SpanAllColumns) != 0 && (g.CurrentTable != NULL); ImRect frame_bb; frame_bb.Min.x = span_all_columns ? window->ParentWorkRect.Min.x : (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x; frame_bb.Min.y = window->DC.CursorPos.y; - frame_bb.Max.x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x; + frame_bb.Max.x = span_all_columns ? window->ParentWorkRect.Max.x : (flags & ImGuiTreeNodeFlags_SpanTextWidth) ? window->DC.CursorPos.x + text_width + padding.x : window->WorkRect.Max.x; frame_bb.Max.y = window->DC.CursorPos.y + frame_height; if (display_frame) { @@ -6195,16 +6258,13 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l frame_bb.Max.x += IM_TRUNC(window->WindowPadding.x * 0.5f); } - const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2); // Collapsing arrow width + Spacing - const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it - const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x * 2 : 0.0f); // Include collapsing ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y); ItemSize(ImVec2(text_width, frame_height), padding.y); // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing ImRect interact_bb = frame_bb; - if (!display_frame && (flags & (ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanAllColumns)) == 0) - interact_bb.Max.x = frame_bb.Min.x + text_width + style.ItemSpacing.x * 2.0f; + if ((flags & (ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanTextWidth | ImGuiTreeNodeFlags_SpanAllColumns)) == 0) + interact_bb.Max.x = frame_bb.Min.x + text_width + (label_size.x > 0.0f ? style.ItemSpacing.x * 2.0f : 0.0f); // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable.. const float backup_clip_rect_min_x = window->ClipRect.Min.x; @@ -6456,7 +6516,7 @@ void ImGui::SetNextItemOpen(bool is_open, ImGuiCond cond) return; g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasOpen; g.NextItemData.OpenVal = is_open; - g.NextItemData.OpenCond = cond ? cond : ImGuiCond_Always; + g.NextItemData.OpenCond = (ImU8)(cond ? cond : ImGuiCond_Always); } // CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag). @@ -6740,7 +6800,7 @@ ImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags f g.IO.InputQueueCharacters.resize(0); // Handle backspace - if ((flags & ImGuiTypingSelectFlags_AllowBackspace) && IsKeyPressed(ImGuiKey_Backspace, 0, ImGuiInputFlags_Repeat)) + if ((flags & ImGuiTypingSelectFlags_AllowBackspace) && IsKeyPressed(ImGuiKey_Backspace, ImGuiInputFlags_Repeat)) { char* p = (char*)(void*)ImTextFindPreviousUtf8Codepoint(data->SearchBuffer, data->SearchBuffer + buffer_len); *p = 0; @@ -6928,6 +6988,7 @@ bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg) ImVec2 label_pos = ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y); RenderText(label_pos, label); window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, label_pos + label_size); + AlignTextToFramePadding(); } BeginChild(id, frame_bb.GetSize(), ImGuiChildFlags_FrameStyle); @@ -6971,6 +7032,7 @@ bool ImGui::ListBox(const char* label, int* current_item, const char* (*getter)( bool value_changed = false; ImGuiListClipper clipper; clipper.Begin(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to. + clipper.IncludeItemByIndex(*current_item); while (clipper.Step()) for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { @@ -7489,7 +7551,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) // Menu inside an horizontal menu bar // Selectable extend their highlight by half ItemSpacing in each direction. // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() - popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight()); + popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight); window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); float w = label_size.x; @@ -8255,7 +8317,7 @@ ImGuiTabItem* ImGui::TabBarFindTabByOrder(ImGuiTabBar* tab_bar, int order) ImGuiTabItem* ImGui::TabBarGetCurrentTab(ImGuiTabBar* tab_bar) { - if (tab_bar->LastTabItemIdx <= 0 || tab_bar->LastTabItemIdx >= tab_bar->Tabs.Size) + if (tab_bar->LastTabItemIdx < 0 || tab_bar->LastTabItemIdx >= tab_bar->Tabs.Size) return NULL; return &tab_bar->Tabs[tab_bar->LastTabItemIdx]; } @@ -8964,4 +9026,4 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, } -#endif // #ifndef IMGUI_DISABLE +#endif // #ifndef IMGUI_DISABLE \ No newline at end of file