From 1107bffe842d9fa2f401ee44458f451a5078ab22 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 5 Oct 2023 17:45:31 +0200 Subject: [PATCH 01/14] Popups: clarified meaning of 'p_open != NULL' in BeginPopupModal() + set back user value to false when popup is closed in ways other than clicking the close button. (#6900) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a4c159d7..38ec532a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -87,6 +87,8 @@ Other changes: - Mostly legacy behavior when used inside old Columns(), as we favored that idiom back then, only different is left position follows indentation level, to match calling a Separator() inside or outside Columns(). +- Popups: clarified meaning of 'p_open != NULL' in BeginPopupModal() + set back user value + to false when popup is closed in ways other than clicking the close button. (#6900) - Drag and Drop: Rework drop target highlight: reduce rectangle to its visible portion, and then expand slightly. A full rectangle is always visible and it may protrude slightly. (#4281, #3272) - Drag and Drop: Fixed submitting a tooltip from drop target location when using AcceptDragDropPayload() diff --git a/imgui.cpp b/imgui.cpp index 23363af3..8d3d02a9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10577,7 +10577,9 @@ bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) } // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup. -// Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup) so the actual value of *p_open is meaningless here. +// Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup). +// - *p_open set back to false in BeginPopupModal() when popup is not open. +// - if you set *p_open to false before calling BeginPopupModal(), it will close the popup. bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; @@ -10586,6 +10588,8 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla if (!IsPopupOpen(id, ImGuiPopupFlags_None)) { g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values + if (p_open && *p_open) + *p_open = false; return false; } From 0312a29e4ca1cbb4a62bfa2549db8147807e8ccb Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 5 Oct 2023 18:51:45 +0200 Subject: [PATCH 02/14] ImageButton(): clarify purpose of size. (#6901, #5533, #4471, #2464, #1390). Amend 4a2ae06ca --- imgui.h | 3 ++- imgui_internal.h | 2 +- imgui_widgets.cpp | 9 +++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/imgui.h b/imgui.h index ffef7260..44dd12c3 100644 --- a/imgui.h +++ b/imgui.h @@ -520,8 +520,9 @@ namespace ImGui // Widgets: Images // - Read about ImTextureID here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples + // - Note that ImageButton() adds style.FramePadding*2.0f to provided size. This is in order to facilitate fitting an image in a button. IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& tint_col = ImVec4(1, 1, 1, 1), const ImVec4& border_col = ImVec4(0, 0, 0, 0)); - IMGUI_API bool ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); + IMGUI_API bool ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); // Widgets: Combo Box (Dropdown) // - The BeginCombo()/EndCombo() api allows you to manage your contents and selection state however you want it, by creating e.g. Selectable() items. diff --git a/imgui_internal.h b/imgui_internal.h index f7eb5dba..b9efac24 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3273,7 +3273,7 @@ namespace ImGui IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0); IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0); - IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0); + IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0); IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags, float thickness = 1.0f); IMGUI_API void SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_width); IMGUI_API bool CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 0481dc37..1d64b31d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1033,7 +1033,7 @@ void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& // ImageButton() is flawed as 'id' is always derived from 'texture_id' (see #2464 #1390) // We provide this internal helper to write your own variant while we figure out how to redesign the public ImageButton() API. -bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags) +bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); @@ -1041,7 +1041,7 @@ bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size return false; const ImVec2 padding = g.Style.FramePadding; - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2.0f); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f); ItemSize(bb); if (!ItemAdd(bb, id)) return false; @@ -1060,14 +1060,15 @@ bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size return pressed; } -bool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col) +// Note that ImageButton() adds style.FramePadding*2.0f to provided size. This is in order to facilitate fitting an image in a button. +bool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return false; - return ImageButtonEx(window->GetID(str_id), user_texture_id, size, uv0, uv1, bg_col, tint_col); + return ImageButtonEx(window->GetID(str_id), user_texture_id, image_size, uv0, uv1, bg_col, tint_col); } #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS From 73346e43412199d760f1363e580466acbb802a6a Mon Sep 17 00:00:00 2001 From: "Andon M. Coleman" Date: Tue, 3 Oct 2023 19:00:25 -0400 Subject: [PATCH 03/14] IO: Add extra keys to ImGuiKey enumerator: ImGuiKey_F13 to ImGuiKey_F24. (#6891, #4921) --- imgui.cpp | 1 + imgui.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 8d3d02a9..00be36bf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7983,6 +7983,7 @@ static const char* const GKeyNames[] = "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", + "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24", "Apostrophe", "Comma", "Minus", "Period", "Slash", "Semicolon", "Equal", "LeftBracket", "Backslash", "RightBracket", "GraveAccent", "CapsLock", "ScrollLock", "NumLock", "PrintScreen", "Pause", "Keypad0", "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", diff --git a/imgui.h b/imgui.h index 44dd12c3..51ee8472 100644 --- a/imgui.h +++ b/imgui.h @@ -1423,6 +1423,8 @@ enum ImGuiKey : int ImGuiKey_U, ImGuiKey_V, ImGuiKey_W, ImGuiKey_X, ImGuiKey_Y, ImGuiKey_Z, ImGuiKey_F1, ImGuiKey_F2, ImGuiKey_F3, ImGuiKey_F4, ImGuiKey_F5, ImGuiKey_F6, ImGuiKey_F7, ImGuiKey_F8, ImGuiKey_F9, ImGuiKey_F10, ImGuiKey_F11, ImGuiKey_F12, + ImGuiKey_F13, ImGuiKey_F14, ImGuiKey_F15, ImGuiKey_F16, ImGuiKey_F17, ImGuiKey_F18, + ImGuiKey_F19, ImGuiKey_F20, ImGuiKey_F21, ImGuiKey_F22, ImGuiKey_F23, ImGuiKey_F24, ImGuiKey_Apostrophe, // ' ImGuiKey_Comma, // , ImGuiKey_Minus, // - From b0758c86d89f94e5aa040d32e85e4dd219246328 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 5 Oct 2023 20:39:49 +0200 Subject: [PATCH 04/14] Backends: Added support for extra ImGuiKey values: F13 to F24 function keys. (#6891, #4921) --- backends/imgui_impl_glfw.cpp | 13 +++++++++++++ backends/imgui_impl_osx.mm | 29 +++++++++++++++-------------- backends/imgui_impl_sdl2.cpp | 13 +++++++++++++ backends/imgui_impl_sdl3.cpp | 13 +++++++++++++ backends/imgui_impl_win32.cpp | 16 ++++++++++++++++ docs/CHANGELOG.txt | 5 +++++ 6 files changed, 75 insertions(+), 14 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index a7ed8a40..b3b91dc4 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -20,6 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys. // 2023-07-18: Inputs: Revert ignoring mouse data on GLFW_CURSOR_DISABLED as it can be used differently. User may set ImGuiConfigFLags_NoMouse if desired. (#5625, #6609) // 2023-06-12: Accept glfwGetTime() not returning a monotonically increasing value. This seems to happens on some Windows setup when peripherals disconnect, and is likely to also happen on browser + Emscripten. (#6491) // 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen on Windows ONLY, using a custom WndProc hook. (#2702) @@ -274,6 +275,18 @@ static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key) case GLFW_KEY_F10: return ImGuiKey_F10; case GLFW_KEY_F11: return ImGuiKey_F11; case GLFW_KEY_F12: return ImGuiKey_F12; + case GLFW_KEY_F13: return ImGuiKey_F13; + case GLFW_KEY_F14: return ImGuiKey_F14; + case GLFW_KEY_F15: return ImGuiKey_F15; + case GLFW_KEY_F16: return ImGuiKey_F16; + case GLFW_KEY_F17: return ImGuiKey_F17; + case GLFW_KEY_F18: return ImGuiKey_F18; + case GLFW_KEY_F19: return ImGuiKey_F19; + case GLFW_KEY_F20: return ImGuiKey_F20; + case GLFW_KEY_F21: return ImGuiKey_F21; + case GLFW_KEY_F22: return ImGuiKey_F22; + case GLFW_KEY_F23: return ImGuiKey_F23; + case GLFW_KEY_F24: return ImGuiKey_F24; default: return ImGuiKey_None; } } diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index f7eb790d..b1ca5b03 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -29,6 +29,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F20 function keys. Stopped mapping F13 into PrintScreen. // 2023-04-09: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_Pen. // 2023-02-01: Fixed scroll wheel scaling for devices emitting events with hasPreciseScrollingDeltas==false (e.g. non-Apple mices). // 2022-11-02: Fixed mouse coordinates before clicking the host window. @@ -337,36 +338,36 @@ static ImGuiKey ImGui_ImplOSX_KeyCodeToImGuiKey(int key_code) case kVK_RightOption: return ImGuiKey_RightAlt; case kVK_RightCommand: return ImGuiKey_RightSuper; // case kVK_Function: return ImGuiKey_; -// case kVK_F17: return ImGuiKey_; // case kVK_VolumeUp: return ImGuiKey_; // case kVK_VolumeDown: return ImGuiKey_; // case kVK_Mute: return ImGuiKey_; -// case kVK_F18: return ImGuiKey_; -// case kVK_F19: return ImGuiKey_; -// case kVK_F20: return ImGuiKey_; + case kVK_F1: return ImGuiKey_F1; + case kVK_F2: return ImGuiKey_F2; + case kVK_F3: return ImGuiKey_F3; + case kVK_F4: return ImGuiKey_F4; case kVK_F5: return ImGuiKey_F5; case kVK_F6: return ImGuiKey_F6; case kVK_F7: return ImGuiKey_F7; - case kVK_F3: return ImGuiKey_F3; case kVK_F8: return ImGuiKey_F8; case kVK_F9: return ImGuiKey_F9; - case kVK_F11: return ImGuiKey_F11; - case kVK_F13: return ImGuiKey_PrintScreen; -// case kVK_F16: return ImGuiKey_; -// case kVK_F14: return ImGuiKey_; case kVK_F10: return ImGuiKey_F10; - case 0x6E: return ImGuiKey_Menu; + case kVK_F11: return ImGuiKey_F11; case kVK_F12: return ImGuiKey_F12; -// case kVK_F15: return ImGuiKey_; + case kVK_F13: return ImGuiKey_F13; + case kVK_F14: return ImGuiKey_F14; + case kVK_F15: return ImGuiKey_F15; + case kVK_F16: return ImGuiKey_F16; + case kVK_F17: return ImGuiKey_F17; + case kVK_F18: return ImGuiKey_F18; + case kVK_F19: return ImGuiKey_F19; + case kVK_F20: return ImGuiKey_F20; + case 0x6E: return ImGuiKey_Menu; case kVK_Help: return ImGuiKey_Insert; case kVK_Home: return ImGuiKey_Home; case kVK_PageUp: return ImGuiKey_PageUp; case kVK_ForwardDelete: return ImGuiKey_Delete; - case kVK_F4: return ImGuiKey_F4; case kVK_End: return ImGuiKey_End; - case kVK_F2: return ImGuiKey_F2; case kVK_PageDown: return ImGuiKey_PageDown; - case kVK_F1: return ImGuiKey_F1; case kVK_LeftArrow: return ImGuiKey_LeftArrow; case kVK_RightArrow: return ImGuiKey_RightArrow; case kVK_DownArrow: return ImGuiKey_DownArrow; diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index 87efd514..9587bda7 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -21,6 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys. // 2023-04-06: Inputs: Avoid calling SDL_StartTextInput()/SDL_StopTextInput() as they don't only pertain to IME. It's unclear exactly what their relation is to IME. (#6306) // 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen. (#2702) // 2023-02-23: Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. (#6189, #6114, #3644) @@ -263,6 +264,18 @@ static ImGuiKey ImGui_ImplSDL2_KeycodeToImGuiKey(int keycode) case SDLK_F10: return ImGuiKey_F10; case SDLK_F11: return ImGuiKey_F11; case SDLK_F12: return ImGuiKey_F12; + case SDLK_F13: return ImGuiKey_F13; + case SDLK_F14: return ImGuiKey_F14; + case SDLK_F15: return ImGuiKey_F15; + case SDLK_F16: return ImGuiKey_F16; + case SDLK_F17: return ImGuiKey_F17; + case SDLK_F18: return ImGuiKey_F18; + case SDLK_F19: return ImGuiKey_F19; + case SDLK_F20: return ImGuiKey_F20; + case SDLK_F21: return ImGuiKey_F21; + case SDLK_F22: return ImGuiKey_F22; + case SDLK_F23: return ImGuiKey_F23; + case SDLK_F24: return ImGuiKey_F24; } return ImGuiKey_None; } diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index a4d233d1..f12c0290 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -22,6 +22,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys. // 2023-05-04: Fixed build on Emscripten/iOS/Android. (#6391) // 2023-04-06: Inputs: Avoid calling SDL_StartTextInput()/SDL_StopTextInput() as they don't only pertain to IME. It's unclear exactly what their relation is to IME. (#6306) // 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen. (#2702) @@ -214,6 +215,18 @@ static ImGuiKey ImGui_ImplSDL3_KeycodeToImGuiKey(int keycode) case SDLK_F10: return ImGuiKey_F10; case SDLK_F11: return ImGuiKey_F11; case SDLK_F12: return ImGuiKey_F12; + case SDLK_F13: return ImGuiKey_F13; + case SDLK_F14: return ImGuiKey_F14; + case SDLK_F15: return ImGuiKey_F15; + case SDLK_F16: return ImGuiKey_F16; + case SDLK_F17: return ImGuiKey_F17; + case SDLK_F18: return ImGuiKey_F18; + case SDLK_F19: return ImGuiKey_F19; + case SDLK_F20: return ImGuiKey_F20; + case SDLK_F21: return ImGuiKey_F21; + case SDLK_F22: return ImGuiKey_F22; + case SDLK_F23: return ImGuiKey_F23; + case SDLK_F24: return ImGuiKey_F24; } return ImGuiKey_None; } diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index db861e01..52f67fa3 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -39,6 +39,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys. // 2023-09-25: Inputs: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit it (same behavior as GLFW/SDL). // 2023-09-07: Inputs: Added support for keyboard codepage conversion for when application is compiled in MBCS mode and using a non-Unicode window. // 2023-04-19: Added ImGui_ImplWin32_InitForOpenGL() to facilitate combining raw Win32/Winapi with OpenGL. (#3218) @@ -516,6 +517,18 @@ static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam) case VK_F10: return ImGuiKey_F10; case VK_F11: return ImGuiKey_F11; case VK_F12: return ImGuiKey_F12; + case VK_F13: return ImGuiKey_F13; + case VK_F14: return ImGuiKey_F14; + case VK_F15: return ImGuiKey_F15; + case VK_F16: return ImGuiKey_F16; + case VK_F17: return ImGuiKey_F17; + case VK_F18: return ImGuiKey_F18; + case VK_F19: return ImGuiKey_F19; + case VK_F20: return ImGuiKey_F20; + case VK_F21: return ImGuiKey_F21; + case VK_F22: return ImGuiKey_F22; + case VK_F23: return ImGuiKey_F23; + case VK_F24: return ImGuiKey_F24; default: return ImGuiKey_None; } } @@ -553,6 +566,8 @@ static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo() return ImGuiMouseSource_Mouse; } +#include "imgui_internal.h" + IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (ImGui::GetCurrentContext() == nullptr) @@ -659,6 +674,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA vk = IM_VK_KEYPAD_ENTER; const ImGuiKey key = ImGui_ImplWin32_VirtualKeyToImGuiKey(vk); const int scancode = (int)LOBYTE(HIWORD(lParam)); + printf("vk = %X -> key %d, scancode %d\n", vk, key, scancode); // Special behavior for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit the key down event. if (key == ImGuiKey_PrintScreen && !is_key_down) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 38ec532a..82bcc4ef 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -125,6 +125,7 @@ Other changes: register contents size in a way that would affect the scrolling layer. Was most often noticable when using an horizontal scrollbar. (#6789) - Misc: Most text functions also treat "%.*s" (along with "%s") specially to avoid formatting. (#3466, #6846) +- IO: Add extra keys to ImGuiKey enum: ImGuiKey_F13 to ImGuiKey_F24. (#6891, #4921) - IO: Setting io.WantSetMousePos ignores incoming MousePos events. (#6837, #228) [@bertaye] - Debug Tools: Metrics: Added log of recent alloc/free calls. - Debug Tools: Metrics: Added "Show groups rectangles" in tools. @@ -132,12 +133,16 @@ Other changes: - ImVector: Added find_index() helper. - Demo: Added "Drag and Drop -> Tooltip at target location" demo. - Backends: GLFW: Clear emscripten's MouseWheel callback before shutdown. (#6790, #6096, #4019) [@halx99] +- Backends: GLFW: Added support for F13 to F24 function keys. (#6891) +- Backends: SDL2, SDL3: Added support for F13 to F24 function keys. (#6891) +- Backends: Win32: Added support for F13 to F24 function keys. (#6891) - Backends: Win32: Added support for keyboard codepage conversion for when application is compiled in MBCS mode and using a non-Unicode window. (#6785, #6782, #5725, #5961) [@sneakyevil] - Backends: Win32: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit it (same behavior as GLFW/SDL). (#6859) [@thedmd, @SuperWangKai] - Backends: OpenGL3: rename symbols in our internal loader so that LTO compilation with another copy of gl3w becomes possible. (#6875, #6668, #4445) [@nicolasnoble] +- Backends: OSX: Added support for F13 to F20 function keys. Support mapping F13 to PrintScreen. (#6891) - Internals: Renamed ImFloor() to ImTrunc(). Renamed ImFloorSigned() to ImFloor(). (#6861) From 7bbd758681c48d4bc1c61094c859ba5ea8ba8177 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 5 Oct 2023 20:58:25 +0200 Subject: [PATCH 05/14] Backends: Win32: revert oops chunk. Amend b0758c8 --- backends/imgui_impl_win32.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 52f67fa3..3cf8fc71 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -566,8 +566,6 @@ static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo() return ImGuiMouseSource_Mouse; } -#include "imgui_internal.h" - IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (ImGui::GetCurrentContext() == nullptr) @@ -674,7 +672,6 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA vk = IM_VK_KEYPAD_ENTER; const ImGuiKey key = ImGui_ImplWin32_VirtualKeyToImGuiKey(vk); const int scancode = (int)LOBYTE(HIWORD(lParam)); - printf("vk = %X -> key %d, scancode %d\n", vk, key, scancode); // Special behavior for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit the key down event. if (key == ImGuiKey_PrintScreen && !is_key_down) From 001f102f3871a175a2b83b6ef99babab7d579a1d Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 5 Oct 2023 21:26:07 +0200 Subject: [PATCH 06/14] IO, Backends: added ImGuiKey_AppBack, ImGuiKey_AppForward. (#6891, #4921) --- backends/imgui_impl_sdl2.cpp | 4 +++- backends/imgui_impl_sdl3.cpp | 4 +++- backends/imgui_impl_win32.cpp | 4 +++- docs/CHANGELOG.txt | 5 +++-- imgui.cpp | 1 + imgui.h | 2 ++ 6 files changed, 15 insertions(+), 5 deletions(-) diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index 9587bda7..9f950bb1 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -21,7 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys. +// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys. // 2023-04-06: Inputs: Avoid calling SDL_StartTextInput()/SDL_StopTextInput() as they don't only pertain to IME. It's unclear exactly what their relation is to IME. (#6306) // 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen. (#2702) // 2023-02-23: Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. (#6189, #6114, #3644) @@ -276,6 +276,8 @@ static ImGuiKey ImGui_ImplSDL2_KeycodeToImGuiKey(int keycode) case SDLK_F22: return ImGuiKey_F22; case SDLK_F23: return ImGuiKey_F23; case SDLK_F24: return ImGuiKey_F24; + case SDLK_AC_BACK: return ImGuiKey_AppBack; + case SDLK_AC_FORWARD: return ImGuiKey_AppForward; } return ImGuiKey_None; } diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index f12c0290..8a06d995 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -22,7 +22,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys. +// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys. // 2023-05-04: Fixed build on Emscripten/iOS/Android. (#6391) // 2023-04-06: Inputs: Avoid calling SDL_StartTextInput()/SDL_StopTextInput() as they don't only pertain to IME. It's unclear exactly what their relation is to IME. (#6306) // 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen. (#2702) @@ -227,6 +227,8 @@ static ImGuiKey ImGui_ImplSDL3_KeycodeToImGuiKey(int keycode) case SDLK_F22: return ImGuiKey_F22; case SDLK_F23: return ImGuiKey_F23; case SDLK_F24: return ImGuiKey_F24; + case SDLK_AC_BACK: return ImGuiKey_AppBack; + case SDLK_AC_FORWARD: return ImGuiKey_AppForward; } return ImGuiKey_None; } diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 3cf8fc71..73ed700c 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -39,7 +39,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys. +// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys. // 2023-09-25: Inputs: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit it (same behavior as GLFW/SDL). // 2023-09-07: Inputs: Added support for keyboard codepage conversion for when application is compiled in MBCS mode and using a non-Unicode window. // 2023-04-19: Added ImGui_ImplWin32_InitForOpenGL() to facilitate combining raw Win32/Winapi with OpenGL. (#3218) @@ -529,6 +529,8 @@ static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam) case VK_F22: return ImGuiKey_F22; case VK_F23: return ImGuiKey_F23; case VK_F24: return ImGuiKey_F24; + case VK_BROWSER_BACK: return ImGuiKey_AppBack; + case VK_BROWSER_FORWARD: return ImGuiKey_AppForward; default: return ImGuiKey_None; } } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 82bcc4ef..a2fec836 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -126,6 +126,7 @@ Other changes: Was most often noticable when using an horizontal scrollbar. (#6789) - Misc: Most text functions also treat "%.*s" (along with "%s") specially to avoid formatting. (#3466, #6846) - IO: Add extra keys to ImGuiKey enum: ImGuiKey_F13 to ImGuiKey_F24. (#6891, #4921) +- IO: Add extra keys to ImGuiKey enum: ImGuiKey_AppBack, ImGuiKey_AppForward. (#4921) - IO: Setting io.WantSetMousePos ignores incoming MousePos events. (#6837, #228) [@bertaye] - Debug Tools: Metrics: Added log of recent alloc/free calls. - Debug Tools: Metrics: Added "Show groups rectangles" in tools. @@ -134,8 +135,8 @@ Other changes: - Demo: Added "Drag and Drop -> Tooltip at target location" demo. - Backends: GLFW: Clear emscripten's MouseWheel callback before shutdown. (#6790, #6096, #4019) [@halx99] - Backends: GLFW: Added support for F13 to F24 function keys. (#6891) -- Backends: SDL2, SDL3: Added support for F13 to F24 function keys. (#6891) -- Backends: Win32: Added support for F13 to F24 function keys. (#6891) +- Backends: SDL2, SDL3: Added support for F13 to F24 function keys, AppBack, AppForward. (#6891) +- Backends: Win32: Added support for F13 to F24 function keys, AppBack, AppForward. (#6891) - Backends: Win32: Added support for keyboard codepage conversion for when application is compiled in MBCS mode and using a non-Unicode window. (#6785, #6782, #5725, #5961) [@sneakyevil] - Backends: Win32: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows diff --git a/imgui.cpp b/imgui.cpp index 00be36bf..346d48a7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7989,6 +7989,7 @@ static const char* const GKeyNames[] = "Pause", "Keypad0", "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", "Keypad7", "Keypad8", "Keypad9", "KeypadDecimal", "KeypadDivide", "KeypadMultiply", "KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual", + "AppBack", "AppForward", "GamepadStart", "GamepadBack", "GamepadFaceLeft", "GamepadFaceRight", "GamepadFaceUp", "GamepadFaceDown", "GamepadDpadLeft", "GamepadDpadRight", "GamepadDpadUp", "GamepadDpadDown", diff --git a/imgui.h b/imgui.h index 51ee8472..c0a0d9f0 100644 --- a/imgui.h +++ b/imgui.h @@ -1450,6 +1450,8 @@ enum ImGuiKey : int ImGuiKey_KeypadAdd, ImGuiKey_KeypadEnter, ImGuiKey_KeypadEqual, + ImGuiKey_AppBack, // Available on some keyboard/mouses. Often referred as "Browser Back" + ImGuiKey_AppForward, // Gamepad (some of those are analog values, 0.0f to 1.0f) // NAVIGATION ACTION // (download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets) From 112d8fc41d4404f20cbe8b65d272ed8bd59bcf62 Mon Sep 17 00:00:00 2001 From: mpv-enjoyer <123133847+mpv-enjoyer@users.noreply.github.com> Date: Mon, 9 Oct 2023 11:24:52 +0200 Subject: [PATCH 07/14] Combo: added ImGuiComboFlags_WidthFitPreview. (#6881) --- imgui.h | 1 + imgui_demo.cpp | 1 + imgui_widgets.cpp | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index c0a0d9f0..29fac7c0 100644 --- a/imgui.h +++ b/imgui.h @@ -1115,6 +1115,7 @@ enum ImGuiComboFlags_ ImGuiComboFlags_HeightLargest = 1 << 4, // As many fitting items as possible ImGuiComboFlags_NoArrowButton = 1 << 5, // Display on the preview box without the square arrow button ImGuiComboFlags_NoPreview = 1 << 6, // Display only a square arrow button + ImGuiComboFlags_WidthFitPreview = 1 << 7, // Dynamic width depending on current selected element ImGuiComboFlags_HeightMask_ = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest, }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 2ba1dd5b..00d82ef9 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1190,6 +1190,7 @@ static void ShowDemoWindowWidgets() static ImGuiComboFlags flags = 0; ImGui::CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", &flags, ImGuiComboFlags_PopupAlignLeft); ImGui::SameLine(); HelpMarker("Only makes a difference if the popup is larger than the combo"); + ImGui::CheckboxFlags("ImGuiComboFlags_WidthFitPreview", &flags, ImGuiComboFlags_WidthFitPreview); if (ImGui::CheckboxFlags("ImGuiComboFlags_NoArrowButton", &flags, ImGuiComboFlags_NoArrowButton)) flags &= ~ImGuiComboFlags_NoPreview; // Clear the other flag, as we cannot combine both if (ImGui::CheckboxFlags("ImGuiComboFlags_NoPreview", &flags, ImGuiComboFlags_NoPreview)) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 1d64b31d..76c9ae11 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1686,7 +1686,8 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); const ImVec2 label_size = CalcTextSize(label, NULL, true); - const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : CalcItemWidth(); + const float preview_width = (preview_value != nullptr) ? CalcTextSize(preview_value, NULL, true).x : 0.0f; + const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : ((flags & ImGuiComboFlags_WidthFitPreview) ? arrow_size + preview_width + style.ItemInnerSpacing.x * 2.0f : CalcItemWidth()); const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); ItemSize(total_bb, style.FramePadding.y); From feddcf303035534a257f4fb29de7ea8ac9f6f5a1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 9 Oct 2023 11:34:05 +0200 Subject: [PATCH 08/14] Combo: amends for ImGuiComboFlags_WidthFitPreview. (#6881) Amend 112d8fc --- docs/CHANGELOG.txt | 1 + imgui.h | 2 +- imgui_demo.cpp | 5 +++-- imgui_widgets.cpp | 6 ++++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a2fec836..35c57780 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -118,6 +118,7 @@ Other changes: be accepted by the widget when navigation highlight is visible. (#6802, #3092, #5759, #787) - BeginGroup(): Fixed a bug pushing line lower extent too far down when called after a call to SameLine() followed by manual cursor manipulation. +- BeginCombo(): Added ImGuiComboFlags_WidthFitPreview flag. (#6881) [@mpv-enjoyer] - BeginListBox(): Fixed not consuming SetNextWindowXXX data when returning false. - Menus: Fixed a bug where activating an item in a child-menu and dragging mouse over the parent-menu would erroneously close the child-menu. (Regression from 1.88). (#6869) diff --git a/imgui.h b/imgui.h index 29fac7c0..bd7743a0 100644 --- a/imgui.h +++ b/imgui.h @@ -1115,7 +1115,7 @@ enum ImGuiComboFlags_ ImGuiComboFlags_HeightLargest = 1 << 4, // As many fitting items as possible ImGuiComboFlags_NoArrowButton = 1 << 5, // Display on the preview box without the square arrow button ImGuiComboFlags_NoPreview = 1 << 6, // Display only a square arrow button - ImGuiComboFlags_WidthFitPreview = 1 << 7, // Dynamic width depending on current selected element + ImGuiComboFlags_WidthFitPreview = 1 << 7, // Width dynamically calculated from preview contents ImGuiComboFlags_HeightMask_ = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest, }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 00d82ef9..1b627c00 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1190,11 +1190,12 @@ static void ShowDemoWindowWidgets() static ImGuiComboFlags flags = 0; ImGui::CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", &flags, ImGuiComboFlags_PopupAlignLeft); ImGui::SameLine(); HelpMarker("Only makes a difference if the popup is larger than the combo"); - ImGui::CheckboxFlags("ImGuiComboFlags_WidthFitPreview", &flags, ImGuiComboFlags_WidthFitPreview); if (ImGui::CheckboxFlags("ImGuiComboFlags_NoArrowButton", &flags, ImGuiComboFlags_NoArrowButton)) flags &= ~ImGuiComboFlags_NoPreview; // Clear the other flag, as we cannot combine both if (ImGui::CheckboxFlags("ImGuiComboFlags_NoPreview", &flags, ImGuiComboFlags_NoPreview)) - flags &= ~ImGuiComboFlags_NoArrowButton; // Clear the other flag, as we cannot combine both + flags &= ~(ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_WidthFitPreview); // Clear the other flag, as we cannot combine both + if (ImGui::CheckboxFlags("ImGuiComboFlags_WidthFitPreview", &flags, ImGuiComboFlags_WidthFitPreview)) + flags &= ~ImGuiComboFlags_NoPreview; // Using the generic BeginCombo() API, you have full control over how to display the combo contents. // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 76c9ae11..9b2b9707 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1683,11 +1683,13 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together + if (flags & ImGuiComboFlags_WidthFitPreview) + IM_ASSERT((flags & (ImGuiComboFlags_NoPreview | ImGuiComboFlags_CustomPreview)) == 0); const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); const ImVec2 label_size = CalcTextSize(label, NULL, true); - const float preview_width = (preview_value != nullptr) ? CalcTextSize(preview_value, NULL, true).x : 0.0f; - const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : ((flags & ImGuiComboFlags_WidthFitPreview) ? arrow_size + preview_width + style.ItemInnerSpacing.x * 2.0f : CalcItemWidth()); + const float preview_width = ((flags & ImGuiComboFlags_WidthFitPreview) && (preview_value != NULL)) ? CalcTextSize(preview_value, NULL, true).x : 0.0f; + const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : ((flags & ImGuiComboFlags_WidthFitPreview) ? (arrow_size + preview_width + style.FramePadding.x * 2.0f) : CalcItemWidth()); const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); ItemSize(total_bb, style.FramePadding.y); From b9ebb8e06f9c6e254a718f5384a0ca438cbc4c12 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 10 Oct 2023 16:13:29 +0200 Subject: [PATCH 09/14] Tables: fixed right-clicking right-most section (past right-most column) from highlighting right-most column. --- docs/CHANGELOG.txt | 1 + imgui.h | 2 +- imgui_tables.cpp | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 35c57780..d379e26b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -103,6 +103,7 @@ Other changes: - Tables: Fixed bottom-most and right-most outer border offset by one. (#6765, #3752) [@v-ein] - Tables: Fixed top-most outer border being drawn with both TableBorderLight and TableBorderStrong in some situations, causing the earlier to be visible underneath when alpha is not 1.0f. +- Tables: fixed right-clicking right-most section (past right-most column) from highlighting a column. - TabBar: Fixed position of unsaved document marker (ImGuiTabItemFlags_UnsavedDocument) which was accidentally offset in 1.89.9. (#6862) [@alektron] - Fonts: 'float size_pixels' passed to AddFontXXX() functions is now rounded to lowest integer. diff --git a/imgui.h b/imgui.h index bd7743a0..46d3b3ba 100644 --- a/imgui.h +++ b/imgui.h @@ -507,7 +507,7 @@ namespace ImGui // - Most widgets return true when the value has been changed or when pressed/selected // - You may also use one of the many IsItemXXX functions (e.g. IsItemActive, IsItemHovered, etc.) to query widget state. IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0, 0)); // button - IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) to easily embed within text + IMGUI_API bool SmallButton(const char* label); // button with (FramePadding.y == 0) to easily embed within text IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size, ImGuiButtonFlags flags = 0); // flexible button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.) IMGUI_API bool ArrowButton(const char* str_id, ImGuiDir dir); // square button with an arrow shape IMGUI_API bool Checkbox(const char* label, bool* v); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index edd5dfba..01851bf6 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -2919,9 +2919,9 @@ void ImGui::TableHeadersRow() TableUpdateLayout(table); // Open row - const float row_y1 = GetCursorScreenPos().y; const float row_height = TableGetHeaderRowHeight(); TableNextRow(ImGuiTableRowFlags_Headers, row_height); + const float row_y1 = GetCursorScreenPos().y; if (table->HostSkipItems) // Merely an optimization, you may skip in your own code. return; @@ -2943,7 +2943,7 @@ void ImGui::TableHeadersRow() ImVec2 mouse_pos = ImGui::GetMousePos(); if (IsMouseReleased(1) && TableGetHoveredColumn() == columns_count) if (mouse_pos.y >= row_y1 && mouse_pos.y < row_y1 + row_height) - TableOpenContextMenu(-1); // Will open a non-column-specific popup. + TableOpenContextMenu(columns_count); // Will open a non-column-specific popup. } // Emit a column header (text + optional sort order) From 5053d79a2431d2367b55a805a394af45d532952b Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 9 Oct 2023 20:35:57 +0200 Subject: [PATCH 10/14] Tables: Internal: rework so stacked headers height may be used. --- imgui.cpp | 4 ++-- imgui_internal.h | 9 ++------- imgui_tables.cpp | 35 +++++++++++++++++------------------ 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 346d48a7..c32e4e89 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13799,8 +13799,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) else if (rect_type == TRT_ColumnsRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table_instance->LastOuterHeight); } else if (rect_type == TRT_ColumnsWorkRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->WorkRect.Min.y, c->WorkMaxX, table->WorkRect.Max.y); } else if (rect_type == TRT_ColumnsClipRect) { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; } - else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight); } // Note: y1/y2 not always accurate - else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight); } + else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); } // Note: y1/y2 not always accurate + else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); } else if (rect_type == TRT_ColumnsContentFrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight); } else if (rect_type == TRT_ColumnsContentUnfrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); } IM_ASSERT(0); diff --git a/imgui_internal.h b/imgui_internal.h index b9efac24..19ff1b00 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2,11 +2,6 @@ // (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. -// To implement maths operators for ImVec2 (disabled by default to not conflict with using IM_VEC2_CLASS_EXTRA with your own math types+operators), use: -/* -#define IMGUI_DEFINE_MATH_OPERATORS -#include "imgui_internal.h" -*/ /* @@ -2678,12 +2673,12 @@ struct ImGuiTableInstanceData { ImGuiID TableInstanceID; float LastOuterHeight; // Outer height from last frame - float LastFirstRowHeight; // Height of first row from last frame (FIXME: this is used as "header height" and may be reworked) + float LastTopHeadersRowHeight; // Height of first consecutive header rows from last frame (FIXME: this is used assuming consecutive headers are in same frozen set) float LastFrozenHeight; // Height of frozen section from last frame int HoveredRowLast; // Index of row which was hovered last frame. int HoveredRowNext; // Index of row hovered this frame, set after encountering it. - ImGuiTableInstanceData() { TableInstanceID = 0; LastOuterHeight = LastFirstRowHeight = LastFrozenHeight = 0.0f; HoveredRowLast = HoveredRowNext = -1; } + 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 diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 01851bf6..95f90064 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1157,7 +1157,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // [Part 10] Hit testing on borders if (table->Flags & ImGuiTableFlags_Resizable) TableUpdateBorders(table); - table_instance->LastFirstRowHeight = 0.0f; + table_instance->LastTopHeadersRowHeight = 0.0f; table->IsLayoutLocked = true; table->IsUsingHeaders = false; @@ -1203,7 +1203,7 @@ void ImGui::TableUpdateBorders(ImGuiTable* table) const float hit_half_width = TABLE_RESIZE_SEPARATOR_HALF_THICKNESS; const float hit_y1 = table->OuterRect.Min.y; const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight); - const float hit_y2_head = hit_y1 + table_instance->LastFirstRowHeight; + const float hit_y2_head = hit_y1 + table_instance->LastTopHeadersRowHeight; for (int order_n = 0; order_n < table->ColumnsCount; order_n++) { @@ -1461,7 +1461,7 @@ void ImGui::EndTable() NavUpdateCurrentWindowIsScrollPushableX(); } -// See "COLUMN SIZING POLICIES" comments at the top of this file +// See "COLUMNS SIZING POLICIES" comments at the top of this file // If (init_width_or_weight <= 0.0f) it is ignored void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id) { @@ -1836,15 +1836,16 @@ void ImGui::TableEndRow(ImGuiTable* table) const float bg_y2 = table->RowPosY2; const bool unfreeze_rows_actual = (table->CurrentRow + 1 == table->FreezeRowsCount); const bool unfreeze_rows_request = (table->CurrentRow + 1 == table->FreezeRowsRequest); - if (table->CurrentRow == 0) - TableGetInstanceData(table, table->InstanceCurrent)->LastFirstRowHeight = bg_y2 - bg_y1; + ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); + if ((table->RowFlags & ImGuiTableRowFlags_Headers) && (table->CurrentRow == 0 || (table->LastRowFlags & ImGuiTableRowFlags_Headers))) + table_instance->LastTopHeadersRowHeight += bg_y2 - bg_y1; const bool is_visible = (bg_y2 >= table->InnerClipRect.Min.y && bg_y1 <= table->InnerClipRect.Max.y); if (is_visible) { // Update data for TableGetHoveredRow() if (table->HoveredColumnBody != -1 && g.IO.MousePos.y >= bg_y1 && g.IO.MousePos.y < bg_y2) - TableGetInstanceData(table, table->InstanceCurrent)->HoveredRowNext = table->CurrentRow; + table_instance->HoveredRowNext = table->CurrentRow; // Decide of background color for the row ImU32 bg_col0 = 0; @@ -1922,7 +1923,7 @@ void ImGui::TableEndRow(ImGuiTable* table) IM_ASSERT(table->IsUnfrozenRows == false); const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y); table->IsUnfrozenRows = true; - TableGetInstanceData(table, table->InstanceCurrent)->LastFrozenHeight = y0 - table->OuterRect.Min.y; + table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y; // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y); @@ -2599,7 +2600,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) const float border_size = TABLE_BORDER_SIZE; const float draw_y1 = table->InnerRect.Min.y + ((table->Flags & ImGuiTableFlags_BordersOuterH) ? 1.0f : 0.0f); const float draw_y2_body = table->InnerRect.Max.y; - const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastFirstRowHeight) : draw_y1; + const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastTopHeadersRowHeight) : draw_y1; if (table->Flags & ImGuiTableFlags_BordersInnerV) { for (int order_n = 0; order_n < table->ColumnsCount; order_n++) @@ -2890,16 +2891,14 @@ float ImGui::TableGetHeaderRowHeight() // Calculate row height, for the unlikely case that some labels may be taller than others. // If we didn't do that, uneven header height would highlight but smaller one before the tallest wouldn't catch input for all height. // In your custom header row you may omit this all together and just call TableNextRow() without a height... - float row_height = GetTextLineHeight(); - int columns_count = TableGetColumnCount(); - for (int column_n = 0; column_n < columns_count; column_n++) - { - ImGuiTableColumnFlags flags = TableGetColumnFlags(column_n); - if ((flags & ImGuiTableColumnFlags_IsEnabled) && !(flags & ImGuiTableColumnFlags_NoHeaderLabel)) - row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(column_n)).y); - } - row_height += GetStyle().CellPadding.y * 2.0f; - return row_height; + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + float row_height = g.FontSize; + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n)) + if ((table->Columns[column_n].Flags & ImGuiTableColumnFlags_NoHeaderLabel) == 0) + row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(table, column_n)).y); + return row_height + g.Style.CellPadding.y * 2.0f; } // [Public] This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn(). From 08606714a30c39d6bb48bf138731d49c1d5ff0c8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 12 Oct 2023 12:21:30 +0200 Subject: [PATCH 11/14] Fixed incorrect assert in FocusTopMostWindowUnderOne() preventing child+popup from being used. (#6915, #718) --- imgui.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index c32e4e89..3dde2930 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7145,7 +7145,6 @@ void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWind { // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user. ImGuiWindow* window = g.WindowsFocusOrder[i]; - IM_ASSERT(window == window->RootWindow); if (window == ignore_window || !window->WasActive) continue; if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) From be1311cfc10b1761b150924a4654b045b8fdd7d3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 11 Oct 2023 21:40:21 +0200 Subject: [PATCH 12/14] Tables: fixed double-clicking on a column from clearing HoveredColumnBorder for a frame. --- imgui.h | 2 +- imgui_tables.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.h b/imgui.h index 46d3b3ba..47420c44 100644 --- a/imgui.h +++ b/imgui.h @@ -24,7 +24,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.90 WIP" -#define IMGUI_VERSION_NUM 18994 +#define IMGUI_VERSION_NUM 18995 #define IMGUI_HAS_TABLE /* diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 95f90064..c9acf5e7 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1234,7 +1234,7 @@ void ImGui::TableUpdateBorders(ImGuiTable* table) { TableSetColumnWidthAutoSingle(table, column_n); ClearActiveID(); - held = hovered = false; + held = false; } if (held) { From 9f851ebfe42b99057a6c241c0106c74cc93ed78f Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 12 Oct 2023 14:43:34 +0200 Subject: [PATCH 13/14] Tables: added ImGuiTableFlags_HighlightHoveredColumn. --- docs/CHANGELOG.txt | 1 + imgui.h | 2 ++ imgui_demo.cpp | 14 +++++++++--- imgui_internal.h | 4 ++++ imgui_tables.cpp | 55 ++++++++++++++++++++++++++++------------------ 5 files changed, 52 insertions(+), 24 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d379e26b..bd4d385f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -94,6 +94,7 @@ Other changes: - Drag and Drop: Fixed submitting a tooltip from drop target location when using AcceptDragDropPayload() with ImGuiDragDropFlags_AcceptNoPreviewTooltip and submitting a tooltip manually. - TreeNode: Added ImGuiTreeNodeFlags_SpanAllColumns for use in tables. (#3151, #3565, #2451, #2438) +- Tables: Added ImGuiTableFlags_HighlightHoveredColumn flag, currently highlighting column header. - Tables: Fixed an edge-case when no columns are visible + table scrollbar is visible + user code is always testing return value of TableSetColumnIndex() to coarse clip. With an active clipper it would have asserted. Without a clipper, the scrollbar range would be wrong. diff --git a/imgui.h b/imgui.h index 47420c44..aec90a99 100644 --- a/imgui.h +++ b/imgui.h @@ -1216,6 +1216,8 @@ enum ImGuiTableFlags_ // Sorting ImGuiTableFlags_SortMulti = 1 << 26, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). ImGuiTableFlags_SortTristate = 1 << 27, // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). + // Miscellaneous + ImGuiTableFlags_HighlightHoveredColumn = 1 << 28, // Highlight column headers when hovered (may evolve into a fuller highlight) // [Internal] Combinations and masks ImGuiTableFlags_SizingMask_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame, diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 1b627c00..b0280eb3 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3973,10 +3973,10 @@ static void ShowDemoWindowTables() ImGui::PushID("Tables"); int open_action = -1; - if (ImGui::Button("Open all")) + if (ImGui::Button("Expand all")) open_action = 1; ImGui::SameLine(); - if (ImGui::Button("Close all")) + if (ImGui::Button("Collapse all")) open_action = 0; ImGui::SameLine(); @@ -4262,6 +4262,7 @@ static void ShowDemoWindowTables() ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", &flags, ImGuiTableFlags_Hideable); ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)"); + ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn); PopStyleCompact(); if (ImGui::BeginTable("table1", 3, flags)) @@ -5299,6 +5300,7 @@ static void ShowDemoWindowTables() static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings; ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); ImGui::CheckboxFlags("ImGuiTableFlags_SizingFixedFit", &flags, ImGuiTableFlags_SizingFixedFit); + ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn); for (int n = 0; n < 3; n++) { char buf[32]; @@ -5519,9 +5521,15 @@ static void ShowDemoWindowTables() ImGui::TreePop(); } - if (ImGui::TreeNodeEx("Other:", ImGuiTreeNodeFlags_DefaultOpen)) + if (ImGui::TreeNodeEx("Headers:", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::Checkbox("show_headers", &show_headers); + ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn); + ImGui::TreePop(); + } + + if (ImGui::TreeNodeEx("Other:", ImGuiTreeNodeFlags_DefaultOpen)) + { ImGui::Checkbox("show_wrapped_text", &show_wrapped_text); ImGui::DragFloat2("##OuterSize", &outer_size_value.x); diff --git a/imgui_internal.h b/imgui_internal.h index 19ff1b00..a34a115b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2669,6 +2669,7 @@ struct ImGuiTableCellData }; // 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 { ImGuiID TableInstanceID; @@ -2754,6 +2755,7 @@ struct IMGUI_API ImGuiTable ImGuiTableColumnIdx DeclColumnsCount; // Count calls to TableSetupColumn() ImGuiTableColumnIdx HoveredColumnBody; // Index of column whose visible region is being hovered. Important: == ColumnsCount when hovering empty region after the right-most column! ImGuiTableColumnIdx HoveredColumnBorder; // Index of column whose right-border is being hovered (for resizing). + ImGuiTableColumnIdx HighlightColumnHeader; // Index of column which should be highlighted. ImGuiTableColumnIdx AutoFitSingleColumn; // Index of single column requesting auto-fit. ImGuiTableColumnIdx ResizedColumn; // Index of column being resized. Reset when InstanceCurrent==0. ImGuiTableColumnIdx LastResizedColumn; // Index of column being resized from previous frame. @@ -2786,6 +2788,8 @@ struct IMGUI_API ImGuiTable bool IsResetDisplayOrderRequest; bool IsUnfrozenRows; // Set when we got past the frozen row. bool IsDefaultSizingPolicy; // Set if user didn't explicitly set a sizing policy in BeginTable() + bool IsActiveIdAliveBeforeTable; + bool IsActiveIdInTable; bool HasScrollbarYCurr; // Whether ANY instance of this table had a vertical scrollbar during the current frame. bool HasScrollbarYPrev; // Whether ANY instance of this table had a vertical scrollbar during the previous. bool MemoryCompacted; diff --git a/imgui_tables.cpp b/imgui_tables.cpp index c9acf5e7..0a3a8b0c 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -346,7 +346,8 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG flags = TableFixFlags(flags, outer_window); // Initialize - const int instance_no = (table->LastFrameActive != g.FrameCount) ? 0 : table->InstanceCurrent + 1; + const int previous_frame_active = table->LastFrameActive; + const int instance_no = (previous_frame_active != g.FrameCount) ? 0 : table->InstanceCurrent + 1; table->ID = id; table->Flags = flags; table->LastFrameActive = g.FrameCount; @@ -478,6 +479,8 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->FreezeColumnsRequest = table->FreezeColumnsCount = 0; table->IsUnfrozenRows = true; table->DeclColumnsCount = 0; + if (previous_frame_active + 1 < g.FrameCount) + table->IsActiveIdInTable = false; // Using opaque colors facilitate overlapping lines of the grid, otherwise we'd need to improve TableDrawBorders() table->BorderColorStrong = GetColorU32(ImGuiCol_TableBorderStrong); @@ -974,8 +977,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); table_instance->HoveredRowLast = table_instance->HoveredRowNext; table_instance->HoveredRowNext = -1; - table->HoveredColumnBody = -1; - table->HoveredColumnBorder = -1; + table->HoveredColumnBody = table->HoveredColumnBorder = -1; const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight)); const ImGuiID backup_active_id = g.ActiveId; g.ActiveId = 0; @@ -1122,13 +1124,13 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // because of using _WidthAuto/_WidthStretch). This will hide the resizing option from the context menu. const float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x); if (is_hovering_table && table->HoveredColumnBody == -1) - { if (g.IO.MousePos.x >= unused_x1) table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount; - } if (has_resizable == false && (table->Flags & ImGuiTableFlags_Resizable)) table->Flags &= ~ImGuiTableFlags_Resizable; + table->IsActiveIdAliveBeforeTable = (g.ActiveIdIsAlive != 0); + // [Part 8] Lock actual OuterRect/WorkRect right-most position. // This is done late to handle the case of fixed-columns tables not claiming more widths that they need. // Because of this we are careful with uses of WorkRect and InnerClipRect before this point. @@ -1161,6 +1163,14 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) table->IsLayoutLocked = true; table->IsUsingHeaders = false; + // Highlight header + table->HighlightColumnHeader = -1; + if (table->IsContextPopupOpen && table->ContextPopupColumn != -1 && table->InstanceInteracted == table->InstanceCurrent) + table->HighlightColumnHeader = table->ContextPopupColumn; + else if ((table->Flags & ImGuiTableFlags_HighlightHoveredColumn) && table->HoveredColumnBody != -1 && table->HoveredColumnBody != table->ColumnsCount && table->HoveredColumnBorder == -1) + if (g.ActiveId == 0 || (table->IsActiveIdInTable || g.DragDropActive)) + table->HighlightColumnHeader = table->HoveredColumnBody; + // [Part 11] Context menu if (TableBeginContextMenuPopup(table)) { @@ -1382,6 +1392,8 @@ void ImGui::EndTable() table->ResizedColumnNextWidth = new_width; } + table->IsActiveIdInTable = (g.ActiveIdIsAlive != 0 && table->IsActiveIdAliveBeforeTable == false); + // Pop from id stack IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table_instance->TableInstanceID, "Mismatching PushID/PopID!"); IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= temp_data->HostBackupItemWidthStackSize, "Too many PopItemWidth!"); @@ -2301,6 +2313,7 @@ void ImGui::TableUpdateColumnsWeightFromWidth(ImGuiTable* table) // - TablePopBackgroundChannel() [Internal] // - TableSetupDrawChannels() [Internal] // - TableMergeDrawChannels() [Internal] +// - TableGetColumnBorderCol() [Internal] // - TableDrawBorders() [Internal] //------------------------------------------------------------------------- @@ -2584,6 +2597,18 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) } } +static ImU32 TableGetColumnBorderCol(ImGuiTable* table, int order_n, int column_n) +{ + const bool is_hovered = (table->HoveredColumnBorder == column_n); + const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent); + const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1); + if (is_resized || is_hovered) + return ImGui::GetColorU32(is_resized ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered); + if (is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize))) + return table->BorderColorStrong; + return table->BorderColorLight; +} + // FIXME-TABLE: This is a mess, need to redesign how we render borders (as some are also done in TableEndRow) void ImGui::TableDrawBorders(ImGuiTable* table) { @@ -2626,21 +2651,9 @@ void ImGui::TableDrawBorders(ImGuiTable* table) // Draw in outer window so right-most column won't be clipped // Always draw full height border when being resized/hovered, or on the delimitation of frozen column scrolling. - ImU32 col; - float draw_y2; - if (is_hovered || is_resized || is_frozen_separator) - { - draw_y2 = draw_y2_body; - col = is_resized ? GetColorU32(ImGuiCol_SeparatorActive) : is_hovered ? GetColorU32(ImGuiCol_SeparatorHovered) : table->BorderColorStrong; - } - else - { - draw_y2 = (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) ? draw_y2_head : draw_y2_body; - col = (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) ? table->BorderColorStrong : table->BorderColorLight; - } - + float draw_y2 = (is_hovered || is_resized || is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) == 0) ? draw_y2_body : draw_y2_head; if (draw_y2 > draw_y1) - inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), col, border_size); + inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), TableGetColumnBorderCol(table, order_n, column_n), border_size); } } @@ -2994,7 +3007,6 @@ void ImGui::TableHeader(const char* label) column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x); // Keep header highlighted when context menu is open. - const bool selected = (table->IsContextPopupOpen && table->ContextPopupColumn == column_n && table->InstanceInteracted == table->InstanceCurrent); ImGuiID id = window->GetID(label); ImRect bb(cell_r.Min.x, cell_r.Min.y, cell_r.Max.x, ImMax(cell_r.Max.y, cell_r.Min.y + label_height + g.Style.CellPadding.y * 2.0f)); ItemSize(ImVec2(0.0f, label_height)); // Don't declare unclipped width, it'll be fed ContentMaxPosHeadersIdeal @@ -3005,9 +3017,10 @@ void ImGui::TableHeader(const char* label) //GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG] // Using AllowOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items. + const bool highlight = (table->HighlightColumnHeader == column_n); bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowOverlap); - if (held || hovered || selected) + if (held || hovered || highlight) { const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); //RenderFrame(bb.Min, bb.Max, col, false, 0.0f); From 32228d8add532d7b16ba2de12102b941aba56c4f Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 12 Oct 2023 17:01:42 +0200 Subject: [PATCH 14/14] Tables: added Angled headers support. Added ImGuiTableColumnFlags_AngledHeader, ImGui::TableHeadersAngledRow(), style.TableAngledHeadersAngle. (#2957) --- docs/CHANGELOG.txt | 25 ++++---- imgui.cpp | 1 + imgui.h | 7 ++- imgui_demo.cpp | 79 +++++++++++++++++++++--- imgui_draw.cpp | 8 +++ imgui_internal.h | 9 ++- imgui_tables.cpp | 149 ++++++++++++++++++++++++++++++++++++++++++--- 7 files changed, 250 insertions(+), 28 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index bd4d385f..2ef5e5ef 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -94,17 +94,20 @@ Other changes: - Drag and Drop: Fixed submitting a tooltip from drop target location when using AcceptDragDropPayload() with ImGuiDragDropFlags_AcceptNoPreviewTooltip and submitting a tooltip manually. - TreeNode: Added ImGuiTreeNodeFlags_SpanAllColumns for use in tables. (#3151, #3565, #2451, #2438) -- Tables: Added ImGuiTableFlags_HighlightHoveredColumn flag, currently highlighting column header. -- Tables: Fixed an edge-case when no columns are visible + table scrollbar is visible + user - code is always testing return value of TableSetColumnIndex() to coarse clip. With an active - clipper it would have asserted. Without a clipper, the scrollbar range would be wrong. -- Tables: Request user to submit contents when outer host-window is requesting auto-resize, - so a scrolling table can contribute to initial window size. (#6510) -- Tables: Fixed subtle drawing overlap between borders in some situations. -- Tables: Fixed bottom-most and right-most outer border offset by one. (#6765, #3752) [@v-ein] -- Tables: Fixed top-most outer border being drawn with both TableBorderLight and TableBorderStrong - in some situations, causing the earlier to be visible underneath when alpha is not 1.0f. -- Tables: fixed right-clicking right-most section (past right-most column) from highlighting a column. +- Tables: + - Added angled headers support. You need to set ImGuiTableColumnFlags_AngledHeader on selected + columns and call TableAngledHeadersRow(). Added style.TableAngledHeadersAngle style option. + - Added ImGuiTableFlags_HighlightHoveredColumn flag, currently highlighting column header. + - Fixed an edge-case when no columns are visible + table scrollbar is visible + user + code is always testing return value of TableSetColumnIndex() to coarse clip. With an active + clipper it would have asserted. Without a clipper, the scrollbar range would be wrong. + - Request user to submit contents when outer host-window is requesting auto-resize, + so a scrolling table can contribute to initial window size. (#6510) + - Fixed subtle drawing overlap between borders in some situations. + - Fixed bottom-most and right-most outer border offset by one. (#6765, #3752) [@v-ein] + - Fixed top-most outer border being drawn with both TableBorderLight and TableBorderStrong + in some situations, causing the earlier to be visible underneath when alpha is not 1.0f. + - fixed right-clicking right-most section (past right-most column) from highlighting a column. - TabBar: Fixed position of unsaved document marker (ImGuiTabItemFlags_UnsavedDocument) which was accidentally offset in 1.89.9. (#6862) [@alektron] - Fonts: 'float size_pixels' passed to AddFontXXX() functions is now rounded to lowest integer. diff --git a/imgui.cpp b/imgui.cpp index 3dde2930..63d7d141 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1188,6 +1188,7 @@ ImGuiStyle::ImGuiStyle() 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. diff --git a/imgui.h b/imgui.h index aec90a99..164401cd 100644 --- a/imgui.h +++ b/imgui.h @@ -771,8 +771,9 @@ namespace ImGui // - Use TableSetupScrollFreeze() to lock columns/rows so they stay visible when scrolled. IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_id = 0); IMGUI_API void TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled. - IMGUI_API void TableHeadersRow(); // submit all headers cells based on data provided to TableSetupColumn() + submit context menu IMGUI_API void TableHeader(const char* label); // submit one header cell manually (rarely used) + IMGUI_API void TableHeadersRow(); // submit a row with headers cells based on data provided to TableSetupColumn() + submit context menu + IMGUI_API void TableAngledHeadersRow(); // submit a row with angled headers for every column with the ImGuiTableColumnFlags_AngledHeader flag. MUST BE FIRST ROW. // Tables: Sorting & Miscellaneous functions // - Sorting: call TableGetSortSpecs() to retrieve latest sort specs for the table. NULL when not sorting. @@ -1240,12 +1241,13 @@ enum ImGuiTableColumnFlags_ ImGuiTableColumnFlags_NoSort = 1 << 9, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table). ImGuiTableColumnFlags_NoSortAscending = 1 << 10, // Disable ability to sort in the ascending direction. ImGuiTableColumnFlags_NoSortDescending = 1 << 11, // Disable ability to sort in the descending direction. - ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12, // TableHeadersRow() will not submit label for this column. Convenient for some small columns. Name will still appear in context menu. + ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12, // TableHeadersRow() will not submit horizontal label for this column. Convenient for some small columns. Name will still appear in context menu or in angled headers. ImGuiTableColumnFlags_NoHeaderWidth = 1 << 13, // Disable header text width contribution to automatic column width. ImGuiTableColumnFlags_PreferSortAscending = 1 << 14, // Make the initial sort direction Ascending when first sorting on this column (default). ImGuiTableColumnFlags_PreferSortDescending = 1 << 15, // Make the initial sort direction Descending when first sorting on this column. ImGuiTableColumnFlags_IndentEnable = 1 << 16, // Use current Indent value when entering cell (default for column 0). ImGuiTableColumnFlags_IndentDisable = 1 << 17, // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored. + ImGuiTableColumnFlags_AngledHeader = 1 << 18, // TableHeadersRow() will submit an angled header row for this column. Note this will add an extra row. // Output status flags, read-only via TableGetColumnFlags() ImGuiTableColumnFlags_IsEnabled = 1 << 24, // Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags. @@ -1926,6 +1928,7 @@ struct ImGuiStyle float TabBorderSize; // Thickness of border around tabs. 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). 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. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b0280eb3..8f754db2 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3949,6 +3949,7 @@ static void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags) ImGui::CheckboxFlags("_PreferSortDescending", p_flags, ImGuiTableColumnFlags_PreferSortDescending); ImGui::CheckboxFlags("_IndentEnable", p_flags, ImGuiTableColumnFlags_IndentEnable); ImGui::SameLine(); HelpMarker("Default for column 0"); ImGui::CheckboxFlags("_IndentDisable", p_flags, ImGuiTableColumnFlags_IndentDisable); ImGui::SameLine(); HelpMarker("Default for column >0"); + ImGui::CheckboxFlags("_AngledHeader", p_flags, ImGuiTableColumnFlags_AngledHeader); } static void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags) @@ -4695,8 +4696,14 @@ static void ShowDemoWindowTables() ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 9); if (ImGui::BeginTable("table_columns_flags", column_count, flags, outer_size)) { + bool has_angled_header = false; for (int column = 0; column < column_count; column++) + { + has_angled_header |= (column_flags[column] & ImGuiTableColumnFlags_AngledHeader) != 0; ImGui::TableSetupColumn(column_names[column], column_flags[column]); + } + if (has_angled_header) + ImGui::TableAngledHeadersRow(); ImGui::TableHeadersRow(); for (int column = 0; column < column_count; column++) column_flags_out[column] = ImGui::TableGetColumnFlags(column); @@ -5182,6 +5189,56 @@ static void ShowDemoWindowTables() ImGui::TreePop(); } + // Demonstrate using ImGuiTableColumnFlags_AngledHeader flag to create angled headers + if (open_action != -1) + ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Angled headers"); + if (ImGui::TreeNode("Angled headers")) + { + const char* column_names[] = { "Track", "cabasa", "ride", "smash", "tom-hi", "tom-mid", "tom-low", "hihat-o", "hihat-c", "snare-s", "snare-c", "clap", "rim", "kick" }; + const int columns_count = IM_ARRAYSIZE(column_names); + const int rows_count = 12; + + static ImGuiTableFlags table_flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_Hideable | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_HighlightHoveredColumn; + static bool bools[columns_count * rows_count] = {}; // Dummy storage selection storage + static int frozen_rows = 2; + ImGui::CheckboxFlags("_ScrollX", &table_flags, ImGuiTableFlags_ScrollX); + ImGui::CheckboxFlags("_ScrollY", &table_flags, ImGuiTableFlags_ScrollY); + ImGui::CheckboxFlags("_NoBordersInBody", &table_flags, ImGuiTableFlags_NoBordersInBody); + ImGui::CheckboxFlags("_HighlightHoveredColumn", &table_flags, ImGuiTableFlags_HighlightHoveredColumn); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); + ImGui::SliderInt("Frozen rows", &frozen_rows, 0, 2); + + 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); + for (int n = 1; n < columns_count; n++) + ImGui::TableSetupColumn(column_names[n], ImGuiTableColumnFlags_AngledHeader | ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupScrollFreeze(0, frozen_rows); + + ImGui::TableAngledHeadersRow(); // Draw angled headers for all columns with the ImGuiTableColumnFlags_AngledHeader flag. + ImGui::TableHeadersRow(); // Draw remaining headers and allow access to context-menu and other functions. + for (int row = 0; row < rows_count; row++) + { + ImGui::PushID(row); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::AlignTextToFramePadding(); + ImGui::Text("Track %d", row); + for (int column = 1; column < columns_count; column++) + if (ImGui::TableSetColumnIndex(column)) + { + ImGui::PushID(column); + ImGui::Checkbox("", &bools[row * columns_count + column]); + ImGui::PopID(); + } + ImGui::PopID(); + } + ImGui::EndTable(); + } + ImGui::TreePop(); + } + // Demonstrate creating custom context menus inside columns, while playing it nice with context menus provided by TableHeadersRow()/TableHeader() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); @@ -5428,6 +5485,7 @@ static void ShowDemoWindowTables() | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_SizingFixedFit; + static ImGuiTableColumnFlags columns_base_flags = ImGuiTableColumnFlags_None; enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable, CT_SelectableSpanRow }; static int contents_type = CT_SelectableSpanRow; @@ -5525,6 +5583,8 @@ static void ShowDemoWindowTables() { ImGui::Checkbox("show_headers", &show_headers); ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn); + ImGui::CheckboxFlags("ImGuiTableColumnFlags_AngledHeader", &columns_base_flags, ImGuiTableColumnFlags_AngledHeader); + ImGui::SameLine(); HelpMarker("Enable AngledHeader on all columns. Best enabled on selected narrow columns (see \"Angled headers\" section of the demo)."); ImGui::TreePop(); } @@ -5590,12 +5650,12 @@ static void ShowDemoWindowTables() // Declare columns // We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications. // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index! - ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide, 0.0f, MyItemColumnID_ID); - ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name); - ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action); - ImGui::TableSetupColumn("Quantity", ImGuiTableColumnFlags_PreferSortDescending, 0.0f, MyItemColumnID_Quantity); - ImGui::TableSetupColumn("Description", (flags & ImGuiTableFlags_NoHostExtendX) ? 0 : ImGuiTableColumnFlags_WidthStretch, 0.0f, MyItemColumnID_Description); - ImGui::TableSetupColumn("Hidden", ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort); + ImGui::TableSetupColumn("ID", columns_base_flags | ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide, 0.0f, MyItemColumnID_ID); + ImGui::TableSetupColumn("Name", columns_base_flags | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name); + ImGui::TableSetupColumn("Action", columns_base_flags | ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action); + ImGui::TableSetupColumn("Quantity", columns_base_flags | ImGuiTableColumnFlags_PreferSortDescending, 0.0f, MyItemColumnID_Quantity); + ImGui::TableSetupColumn("Description", columns_base_flags | ((flags & ImGuiTableFlags_NoHostExtendX) ? 0 : ImGuiTableColumnFlags_WidthStretch), 0.0f, MyItemColumnID_Description); + ImGui::TableSetupColumn("Hidden", columns_base_flags | ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort); ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows); // Sort our data if sort specs have been changed! @@ -5614,6 +5674,8 @@ static void ShowDemoWindowTables() const bool sorts_specs_using_quantity = (ImGui::TableGetColumnFlags(3) & ImGuiTableColumnFlags_IsSorted) != 0; // Show headers + if (show_headers && (columns_base_flags & ImGuiTableColumnFlags_AngledHeader) != 0) + ImGui::TableAngledHeadersRow(); if (show_headers) ImGui::TableHeadersRow(); @@ -6400,7 +6462,6 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SeparatorText("Main"); ImGui::SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("CellPadding", (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f"); @@ -6425,6 +6486,10 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f"); ImGui::SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f"); + ImGui::SeparatorText("Tables"); + ImGui::SliderFloat2("CellPadding", (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f"); + ImGui::SliderAngle("TableAngledHeadersAngle", &style.TableAngledHeadersAngle, -50.0f, +50.0f); + ImGui::SeparatorText("Widgets"); ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); int window_menu_button_position = style.WindowMenuButtonPosition + 1; diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 5acd371f..6f518696 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1989,6 +1989,14 @@ void ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int ve } } +void ImGui::ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out) +{ + ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx; + ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx; + for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex) + vertex->pos = ImRotate(vertex->pos- pivot_in, cos_a, sin_a) + pivot_out; +} + //----------------------------------------------------------------------------- // [SECTION] ImFontConfig //----------------------------------------------------------------------------- diff --git a/imgui_internal.h b/imgui_internal.h index a34a115b..f090670b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2731,6 +2731,8 @@ struct IMGUI_API ImGuiTable float ResizedColumnNextWidth; float ResizeLockMinContentsX2; // Lock minimum contents width while resizing down in order to not create feedback loops. But we allow growing the table. float RefScale; // Reference scale to be able to rescale columns on font/dpi changes. + float AngledHeadersHeight; // Set by TableAngledHeadersRow(), used in TableUpdateLayout() + float AngledHeadersSlope; // Set by TableAngledHeadersRow(), used in TableUpdateLayout() ImRect OuterRect; // Note: for non-scrolling table, OuterRect.Max.y is often FLT_MAX until EndTable(), unless a height has been specified in BeginTable(). ImRect InnerRect; // InnerRect but without decoration. As with OuterRect, for non-scrolling tables, InnerRect.Max.y is ImRect WorkRect; @@ -2753,6 +2755,7 @@ struct IMGUI_API ImGuiTable ImGuiTableColumnIdx ColumnsEnabledCount; // Number of enabled columns (<= ColumnsCount) ImGuiTableColumnIdx ColumnsEnabledFixedCount; // Number of enabled columns (<= 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! ImGuiTableColumnIdx HoveredColumnBorder; // Index of column whose right-border is being hovered (for resizing). ImGuiTableColumnIdx HighlightColumnHeader; // Index of column which should be highlighted. @@ -2802,11 +2805,12 @@ 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() ~ 112 bytes. +// sizeof() ~ 120 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() ImVec2 UserOuterSize; // outer_size.x passed to BeginTable() ImDrawListSplitter DrawSplitter; @@ -3177,8 +3181,10 @@ namespace ImGui IMGUI_API int TableGetHoveredColumn(); // May use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead. Return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered. IMGUI_API int TableGetHoveredRow(); // Retrieve *PREVIOUS FRAME* hovered row. This difference with TableGetHoveredColumn() is the reason why this is not public yet. IMGUI_API float TableGetHeaderRowHeight(); + IMGUI_API float TableGetHeaderAngledMaxLabelWidth(); IMGUI_API void TablePushBackgroundChannel(); IMGUI_API void TablePopBackgroundChannel(); + IMGUI_API void TableAngledHeadersRowEx(float angle, float label_width = 0.0f); // Tables: Internals inline ImGuiTable* GetCurrentTable() { ImGuiContext& g = *GImGui; return g.CurrentTable; } @@ -3336,6 +3342,7 @@ namespace ImGui // Shade functions (write over already created vertices) IMGUI_API void ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1); IMGUI_API void ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp); + IMGUI_API void ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out); // Garbage collection IMGUI_API void GcCompactTransientMiscBuffers(); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 0a3a8b0c..1063fa79 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -478,9 +478,10 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->FreezeRowsRequest = table->FreezeRowsCount = 0; // This will be setup by TableSetupScrollFreeze(), if any table->FreezeColumnsRequest = table->FreezeColumnsCount = 0; table->IsUnfrozenRows = true; - table->DeclColumnsCount = 0; + table->DeclColumnsCount = table->AngledHeadersCount = 0; if (previous_frame_active + 1 < g.FrameCount) table->IsActiveIdInTable = false; + temp_data->AngledheadersExtraWidth = 0.0f; // Using opaque colors facilitate overlapping lines of the grid, otherwise we'd need to improve TableDrawBorders() table->BorderColorStrong = GetColorU32(ImGuiCol_TableBorderStrong); @@ -984,6 +985,12 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0, ImGuiItemFlags_None); g.ActiveId = backup_active_id; + // Determine skewed MousePos.x to support angled headers. + float mouse_skewed_x = g.IO.MousePos.x; + if (table->AngledHeadersHeight > 0.0f) + if (g.IO.MousePos.y >= table->OuterRect.Min.y && g.IO.MousePos.y <= table->OuterRect.Min.y + table->AngledHeadersHeight) + mouse_skewed_x += ImTrunc((table->OuterRect.Min.y + table->AngledHeadersHeight - g.IO.MousePos.y) * table->AngledHeadersSlope); + // [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column // Process columns in their visible orders as we are comparing the visible order and adjusting host_clip_rect while looping. int visible_n = 0; @@ -1025,7 +1032,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) } // Detect hovered column - if (is_hovering_table && g.IO.MousePos.x >= column->ClipRect.Min.x && g.IO.MousePos.x < column->ClipRect.Max.x) + if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x) table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n; // Lock start position @@ -1124,7 +1131,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // because of using _WidthAuto/_WidthStretch). This will hide the resizing option from the context menu. const float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x); if (is_hovering_table && table->HoveredColumnBody == -1) - if (g.IO.MousePos.x >= unused_x1) + if (mouse_skewed_x >= unused_x1) table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount; if (has_resizable == false && (table->Flags & ImGuiTableFlags_Resizable)) table->Flags &= ~ImGuiTableFlags_Resizable; @@ -1211,7 +1218,7 @@ void ImGui::TableUpdateBorders(ImGuiTable* table) // 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_y1 = table->OuterRect.Min.y; + 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_head = hit_y1 + table_instance->LastTopHeadersRowHeight; @@ -1316,7 +1323,7 @@ void ImGui::EndTable() max_pos_x = ImMax(max_pos_x, table->Columns[table->RightMostEnabledColumn].WorkMaxX + table->CellPaddingX + table->OuterPaddingX - outer_padding_for_border); if (table->ResizedColumn != -1) max_pos_x = ImMax(max_pos_x, table->ResizeLockMinContentsX2); - table->InnerWindow->DC.CursorMaxPos.x = max_pos_x; + table->InnerWindow->DC.CursorMaxPos.x = max_pos_x + table->TempData->AngledheadersExtraWidth; } // Pop clipping rect @@ -1434,7 +1441,7 @@ void ImGui::EndTable() } else if (temp_data->UserOuterSize.x <= 0.0f) { - const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.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)); } @@ -1501,6 +1508,11 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0 && init_width_or_weight > 0.0f) if ((table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedFit || (table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame) flags |= ImGuiTableColumnFlags_WidthFixed; + if (flags & ImGuiTableColumnFlags_AngledHeader) + { + flags |= ImGuiTableColumnFlags_NoHeaderLabel; + table->AngledHeadersCount++; + } TableSetupColumnFlags(table, column, flags); column->UserID = user_id; @@ -2623,7 +2635,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) // Draw inner border and resizing feedback ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); const float border_size = TABLE_BORDER_SIZE; - const float draw_y1 = table->InnerRect.Min.y + ((table->Flags & ImGuiTableFlags_BordersOuterH) ? 1.0f : 0.0f); + const float draw_y1 = ImMax(table->InnerRect.Min.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight) + ((table->Flags & ImGuiTableFlags_BordersOuterH) ? 1.0f : 0.0f); const float draw_y2_body = table->InnerRect.Max.y; const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastTopHeadersRowHeight) : draw_y1; if (table->Flags & ImGuiTableFlags_BordersInnerV) @@ -2896,6 +2908,8 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table) // - TableGetHeaderRowHeight() [Internal] // - TableHeadersRow() // - TableHeader() +// - TableAngledHeadersRow() +// - TableAngledHeadersRowEx() [Internal] //------------------------------------------------------------------------- float ImGui::TableGetHeaderRowHeight() @@ -2914,6 +2928,18 @@ float ImGui::TableGetHeaderRowHeight() return row_height + g.Style.CellPadding.y * 2.0f; } +float ImGui::TableGetHeaderAngledMaxLabelWidth() +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + float width = 0.0f; + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n)) + if (table->Columns[column_n].Flags & ImGuiTableColumnFlags_AngledHeader) + width = ImMax(width, CalcTextSize(TableGetColumnName(table, column_n), NULL, true).x); + return width + g.Style.CellPadding.x * 2.0f; +} + // [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. @@ -3098,6 +3124,115 @@ void ImGui::TableHeader(const char* label) TableOpenContextMenu(column_n); } +// 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); +} + +void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width) +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + ImGuiWindow* window = g.CurrentWindow; + ImDrawList* draw_list = window->DrawList; + IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!"); + IM_ASSERT(table->CurrentRow == -1 && "Must be first row"); + + if (max_label_width == 0.0f) + max_label_width = TableGetHeaderAngledMaxLabelWidth(); + + // Angle argument expressed in (-IM_PI/2 .. +IM_PI/2) as it is easier to think about for user. + const bool flip_label = (angle < 0.0f); + angle -= IM_PI * 0.5f; + const float cos_a = ImCos(angle); + const float sin_a = ImSin(angle); + const float label_cos_a = flip_label ? ImCos(angle + IM_PI) : cos_a; + const float label_sin_a = flip_label ? ImSin(angle + IM_PI) : sin_a; + const ImVec2 unit_right = ImVec2(cos_a, sin_a); + + // 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 = table->RowCellPaddingY * 2.0f + g.FontSize; + const float row_height = ImFabs(ImRotate(ImVec2(max_label_width, flip_label ? +header_height : -header_height), cos_a, sin_a).y); + const ImVec2 header_angled_vector = unit_right * (row_height / -sin_a); + table->AngledHeadersHeight = row_height; + table->AngledHeadersSlope = (sin_a != 0.0f) ? (cos_a / sin_a) : 0.0f; + + // Declare row, override and draw our own background + TableNextRow(ImGuiTableRowFlags_Headers, row_height); + TableNextColumn(); + table->DrawSplitter->SetCurrentChannel(draw_list, TABLE_DRAW_CHANNEL_BG0); + PushClipRect(table->BgClipRect.Min, table->BgClipRect.Max, false); // Span all columns + TableSetBgColor(ImGuiTableBgTarget_RowBg0, 0); // Cancel + draw_list->AddRectFilled(table->BgClipRect.Min, table->BgClipRect.Max, GetColorU32(ImGuiCol_TableHeaderBg, 0.25f)); // FIXME-STYLE: Change row background with an arbitrary color. + + const ImRect row_r(table->WorkRect.Min.x, table->BgClipRect.Min.y, table->WorkRect.Max.x, window->DC.CursorPos.y + row_height); + 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; + + float max_x = 0.0f; + for (int pass = 0; pass < 2; pass++) + for (int order_n = 0; order_n < table->ColumnsCount; order_n++) + { + if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n)) + continue; + 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; + + ImVec2 bg_shape[4]; + bg_shape[0] = ImVec2(column->MaxX, row_r.Max.y); + bg_shape[1] = ImVec2(column->MinX, row_r.Max.y); + bg_shape[2] = bg_shape[1] + header_angled_vector; + bg_shape[3] = bg_shape[0] + header_angled_vector; + 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->AddQuad(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_TableBorderLight), 1.0f); + max_x = ImMax(max_x, bg_shape[3].x); + + // Draw label (first draw at an offset where RenderTextXXX() function won't meddle with applying current ClipRect, then transform to final offset) + // FIXME: May be worth tidying up all those operations to make them easier to understand. + const char* label_name = TableGetColumnName(table, column_n); + const float clip_width = max_label_width - (sin_a * table->RowCellPaddingY); + ImRect label_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width + (flip_label ? 0.0f : table->CellPaddingX), header_height + table->RowCellPaddingY)); + ImVec2 label_size = CalcTextSize(label_name, NULL, true); + ImVec2 label_off = ImVec2(flip_label ? ImMax(0.0f, max_label_width - label_size.x - table->CellPaddingX) : table->CellPaddingX, table->RowCellPaddingY); + int vtx_idx_begin = draw_list->_VtxCurrentIdx; + RenderTextEllipsis(draw_list, label_r.Min + label_off, label_r.Max, label_r.Max.x, label_r.Max.x, label_name, NULL, &label_size); + //if (g.IO.KeyShift) { draw_list->AddRect(label_r.Min, label_r.Max, IM_COL32(0, 255, 0, 255), 0.0f, 0, 2.0f); } + int vtx_idx_end = draw_list->_VtxCurrentIdx; + + // Rotate and offset label + ImVec2 pivot_in = label_r.GetBL(); + ImVec2 pivot_out = ImVec2(column->WorkMinX, row_r.Max.y) + (flip_label ? (unit_right * clip_width) : ImVec2(header_height, 0.0f)); + ShadeVertsTransformPos(draw_list, vtx_idx_begin, vtx_idx_end, pivot_in, label_cos_a, label_sin_a, pivot_out); // Rotate and offset + } + if (pass == 1) + { + // Draw border + draw_list->AddLine(bg_shape[0], bg_shape[3], TableGetColumnBorderCol(table, order_n, column_n)); + } + } + PopClipRect(); + table->TempData->AngledheadersExtraWidth = ImMax(0.0f, max_x - table->Columns[table->RightMostEnabledColumn].MaxX); +} + //------------------------------------------------------------------------- // [SECTION] Tables: Context Menu //-------------------------------------------------------------------------