From 5f301914a0bbca018fcbab7065480e1ab2c38e23 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 29 Mar 2023 17:09:16 +0200 Subject: [PATCH] TabBar: Tab-bars with ImGuiTabBarFlags_FittingPolicyScroll can be scrolled with horizontal mouse-wheel (or Shift + WheelY). (#2702) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 2 +- imgui_internal.h | 1 + imgui_widgets.cpp | 25 ++++++++++++++++++++++--- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a079dba9..4f041d3f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -52,6 +52,8 @@ Other changes: - Nav: Made Ctrl+Tab/Ctrl+Shift+Tab windowing register ownership to held modifier so it doesn't interfere with other code when remapping those actions. (#4828, #3255, #5641) - ColorEdit: Fixed shading of S/V triangle in Hue Wheel mode. (#5200, #6254) [@jamesthomasgriffin] +- TabBar: Tab-bars with ImGuiTabBarFlags_FittingPolicyScroll can be scrolled with + horizontal mouse-wheel (or Shift + WheelY). (#2702) - Rendering: Using adaptative tesselation for: RadioButton, ColorEdit preview circles, Windows Close and Collapse Buttons. - Misc: Fixed ImVec2 operator[] violating aliasing rules causing issue with Intel C++ diff --git a/imgui.cpp b/imgui.cpp index 0c85aa48..dbab378e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3867,7 +3867,7 @@ void ImGui::MarkItemEdited(ImGuiID id) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; } -static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) +bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) { // An active popup disable hovering on other windows (apart from its own children) // FIXME-OPT: This could be cached/stored within the window. diff --git a/imgui_internal.h b/imgui_internal.h index 16a30fdb..bc9dd233 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2828,6 +2828,7 @@ namespace ImGui inline void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f) { ItemSize(bb.GetSize(), text_baseline_y); } // FIXME: This is a misleading API since we expect CursorPos to be bb.Min. IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0); IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id); + IMGUI_API bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags = 0); IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id); IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b1e3fb41..eb28738d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7618,6 +7618,12 @@ void ImGui::EndTabBar() g.CurrentTabBar = g.CurrentTabBarStack.empty() ? NULL : GetTabBarFromTabBarRef(g.CurrentTabBarStack.back()); } +// Scrolling happens only in the central section (leading/trailing sections are not scrolling) +static float TabBarCalcScrollableWidth(ImGuiTabBar* tab_bar, ImGuiTabBarSection* sections) +{ + return tab_bar->BarRect.GetWidth() - sections[0].Width - sections[2].Width - sections[1].Spacing; +} + // This is called only once a frame before by the first call to ItemTab() // The reason we're not calling it in BeginTabBar() is to leave a chance to the user to call the SetTabItemClosed() functions. static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) @@ -7820,9 +7826,23 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) tab_bar->VisibleTabId = tab_bar->SelectedTabId; tab_bar->VisibleTabWasSubmitted = false; - // Update scrolling + // Apply request requests if (scroll_to_tab_id != 0) TabBarScrollToTab(tab_bar, scroll_to_tab_id, sections); + else if ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) && IsMouseHoveringRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, true) && IsWindowContentHoverable(g.CurrentWindow)) + { + const float wheel = g.IO.MouseWheelRequestAxisSwap ? g.IO.MouseWheel : g.IO.MouseWheelH; + const ImGuiKey wheel_key = g.IO.MouseWheelRequestAxisSwap ? ImGuiKey_MouseWheelY : ImGuiKey_MouseWheelX; + if (TestKeyOwner(wheel_key, tab_bar->ID) && wheel != 0.0f) + { + const float scroll_step = wheel * TabBarCalcScrollableWidth(tab_bar, sections) / 3.0f; + tab_bar->ScrollingTargetDistToVisibility = 0.0f; + tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget - scroll_step); + } + SetKeyOwner(wheel_key, tab_bar->ID); + } + + // Update scrolling tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim); tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget); if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget) @@ -7959,8 +7979,7 @@ static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGui int order = TabBarGetTabOrder(tab_bar, tab); // Scrolling happens only in the central section (leading/trailing sections are not scrolling) - // FIXME: This is all confusing. - float scrollable_width = tab_bar->BarRect.GetWidth() - sections[0].Width - sections[2].Width - sections[1].Spacing; + float scrollable_width = TabBarCalcScrollableWidth(tab_bar, sections); // We make all tabs positions all relative Sections[0].Width to make code simpler float tab_x1 = tab->Offset - sections[0].Width + (order > sections[0].TabCount - 1 ? -margin : 0.0f);