Tab Bar: Further simplification of section/clip rect handling. (#3291)

This commit is contained in:
ocornut 2020-09-22 16:14:04 +02:00
parent 6b76781c66
commit 1ec464eb9a
3 changed files with 38 additions and 51 deletions

View File

@ -10563,10 +10563,8 @@ void ImGui::ShowMetricsWindow(bool* p_open)
{
ImDrawList* draw_list = ImGui::GetForegroundDrawList();
draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));
if (tab_bar->Sections[0].Width > 0.0f)
draw_list->AddLine(ImVec2(tab_bar->BarRect.Min.x + tab_bar->Sections[0].Width, tab_bar->BarRect.Min.y), ImVec2(tab_bar->BarRect.Min.x + tab_bar->Sections[0].Width, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
if (tab_bar->Sections[2].Width > 0.0f)
draw_list->AddLine(ImVec2(tab_bar->BarRect.Max.x - tab_bar->Sections[2].Width, tab_bar->BarRect.Min.y), ImVec2(tab_bar->BarRect.Max.x - tab_bar->Sections[2].Width, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
}
if (open)
{

View File

@ -1725,16 +1725,6 @@ struct ImGuiTabItem
ImGuiTabItem() { ID = 0; Flags = ImGuiTabItemFlags_None; LastFrameVisible = LastFrameSelected = -1; NameOffset = -1; Offset = Width = ContentWidth = 0.0f; BeginOrder = -1; IndexDuringLayout = -1; WantClose = false; }
};
struct ImGuiTabBarSection
{
int TabStartIndex; // Index of first tab in this section.
int TabCount; // Number of tabs in this section.
float Width; // Sum of width of tabs in this section (after shrinking down)
float Spacing; // Horizontal spacing at the end of the section.
ImGuiTabBarSection(){ memset(this, 0, sizeof(*this)); }
};
// Storage for a tab bar (sizeof() 92~96 bytes)
struct ImGuiTabBar
{
@ -1753,6 +1743,8 @@ struct ImGuiTabBar
float ScrollingTarget;
float ScrollingTargetDistToVisibility;
float ScrollingSpeed;
float ScrollingRectMinX;
float ScrollingRectMaxX;
ImGuiTabBarFlags Flags;
ImGuiID ReorderRequestTabId;
ImS8 ReorderRequestDir;
@ -1762,7 +1754,6 @@ struct ImGuiTabBar
bool TabsAddedNew; // Set to true when a new tab item or button has been added to the tab bar during last frame
short LastTabItemIdx; // Index of last BeginTabItem() tab for use by EndTabItem()
ImVec2 FramePadding; // style.FramePadding locked at the time of BeginTabBar()
ImGuiTabBarSection Sections[3]; // Layout sections: Leading, Central, Trailing
ImGuiTextBuffer TabsNames; // For non-docking tab bar we re-append names in a contiguous buffer.
ImGuiTabBar();

View File

@ -6801,13 +6801,22 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected,
// - TabBarTabListPopupButton() [Internal]
//-------------------------------------------------------------------------
struct ImGuiTabBarSection
{
int TabCount; // Number of tabs in this section.
float Width; // Sum of width of tabs in this section (after shrinking down)
float Spacing; // Horizontal spacing at the end of the section.
ImGuiTabBarSection() { memset(this, 0, sizeof(*this)); }
};
namespace ImGui
{
static void TabBarLayout(ImGuiTabBar* tab_bar);
static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label);
static float TabBarCalcMaxTabWidth();
static float TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling);
static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);
static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, ImGuiTabBarSection* sections);
static ImGuiTabItem* TabBarScrollingButtons(ImGuiTabBar* tab_bar);
static ImGuiTabItem* TabBarTabListPopupButton(ImGuiTabBar* tab_bar);
}
@ -6820,6 +6829,7 @@ ImGuiTabBar::ImGuiTabBar()
LastTabContentHeight = 0.0f;
WidthAllTabs = WidthAllTabsIdeal = 0.0f;
ScrollingAnim = ScrollingTarget = ScrollingTargetDistToVisibility = ScrollingSpeed = 0.0f;
ScrollingRectMinX = ScrollingRectMaxX = 0.0f;
Flags = ImGuiTabBarFlags_None;
ReorderRequestTabId = 0;
ReorderRequestDir = 0;
@ -6860,12 +6870,6 @@ static ImGuiPtrOrIndex GetTabBarRefFromTabBar(ImGuiTabBar* tab_bar)
return ImGuiPtrOrIndex(tab_bar);
}
static ImVec2 GetTabBarScrollingButtonSize()
{
ImGuiContext& g = *GImGui;
return ImVec2(g.FontSize - 2.0f, g.FontSize + g.Style.FramePadding.y * 2.0f);
}
bool ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags)
{
ImGuiContext& g = *GImGui;
@ -6969,14 +6973,13 @@ void ImGui::EndTabBar()
static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
{
ImGuiContext& g = *GImGui;
ImGuiTabBarSection* sections = tab_bar->Sections;
tab_bar->WantLayout = false;
// Garbage collect by compacting list
// Detect if we need to sort out tab list (e.g. in rare case where a tab changed section)
int tab_dst_n = 0;
bool need_sort_by_section = false;
sections[0].TabCount = sections[1].TabCount = sections[2].TabCount = 0;
ImGuiTabBarSection sections[3]; // Layout sections: Leading, Central, Trailing
for (int tab_src_n = 0; tab_src_n < tab_bar->Tabs.Size; tab_src_n++)
{
ImGuiTabItem* tab = &tab_bar->Tabs[tab_src_n];
@ -7015,13 +7018,6 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
// Calculate spacing between sections
sections[0].Spacing = sections[0].TabCount > 0 && (sections[1].TabCount + sections[2].TabCount) > 0 ? g.Style.ItemInnerSpacing.x : 0.0f;
sections[1].Spacing = sections[1].TabCount > 0 && sections[2].TabCount > 0 ? g.Style.ItemInnerSpacing.x : 0.0f;
sections[2].Spacing = 0.0f;
sections[0].TabStartIndex = 0;
sections[1].TabStartIndex = sections[0].TabStartIndex + sections[0].TabCount;
sections[2].TabStartIndex = sections[1].TabStartIndex + sections[1].TabCount;
sections[0].Width = 0.0f;
sections[1].Width = 0.0f;
sections[2].Width = 0.0f;
// Setup next selected tab
ImGuiID scroll_track_selected_tab_id = 0;
@ -7054,6 +7050,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
// Compute ideal tabs widths + store them into shrink buffer
ImGuiTabItem* most_recently_selected_tab = NULL;
int curr_section_n = -1;
bool found_selected_tab_id = false;
for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
{
@ -7076,7 +7073,8 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
int section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;
ImGuiTabBarSection* section = &sections[section_n];
section->Width += tab->ContentWidth + (tab_n > section->TabStartIndex ? g.Style.ItemInnerSpacing.x : 0.0f);
section->Width += tab->ContentWidth + (section_n == curr_section_n ? g.Style.ItemInnerSpacing.x : 0.0f);
curr_section_n = section_n;
// Store data so we can build an array sorted by width if we need to shrink tabs down
int shrink_buffer_index = shrink_buffer_indexes[section_n]++;
@ -7135,23 +7133,24 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
}
// Layout all active tabs
float next_tab_offset = 0.0f;
int section_tab_index = 0;
float tab_offset = 0.0f;
tab_bar->WidthAllTabs = 0.0f;
for (int section_n = 0; section_n < 3; section_n++)
{
// FIXME: The +1.0f is in TabBarScrollingButtons()
ImGuiTabBarSection* section = &sections[section_n];
if (section_n == 2)
next_tab_offset = ImMin(tab_bar->BarRect.GetWidth() - section->Width, next_tab_offset);
tab_offset = ImMin(ImMax(0.0f, tab_bar->BarRect.GetWidth() - section->Width), tab_offset);
for (int tab_n = 0; tab_n < section->TabCount; tab_n++)
{
ImGuiTabItem* tab = &tab_bar->Tabs[section->TabStartIndex + tab_n];
tab->Offset = next_tab_offset;
next_tab_offset += tab->Width + (tab_n < section->TabCount - 1 ? g.Style.ItemInnerSpacing.x : 0.0f);
ImGuiTabItem* tab = &tab_bar->Tabs[section_tab_index + tab_n];
tab->Offset = tab_offset;
tab_offset += tab->Width + (tab_n < section->TabCount - 1 ? g.Style.ItemInnerSpacing.x : 0.0f);
}
tab_bar->WidthAllTabs += ImMax(section->Width + section->Spacing, 0.0f);
next_tab_offset += section->Spacing;
tab_offset += section->Spacing;
section_tab_index += section->TabCount;
}
// If we have lost the selected tab, select the next most recently active one
@ -7167,7 +7166,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
// Update scrolling
if (scroll_track_selected_tab_id)
if (ImGuiTabItem* scroll_track_selected_tab = TabBarFindTabByID(tab_bar, scroll_track_selected_tab_id))
TabBarScrollToTab(tab_bar, scroll_track_selected_tab);
TabBarScrollToTab(tab_bar, scroll_track_selected_tab, sections);
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)
@ -7183,6 +7182,8 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
{
tab_bar->ScrollingSpeed = 0.0f;
}
tab_bar->ScrollingRectMinX = tab_bar->BarRect.Min.x + sections[0].Width + sections[0].Spacing;
tab_bar->ScrollingRectMaxX = tab_bar->BarRect.Max.x - sections[2].Width - sections[1].Spacing;
// Clear name buffers
if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0)
@ -7264,13 +7265,12 @@ static float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling)
return ImMax(scrolling, 0.0f);
}
static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, ImGuiTabBarSection* sections)
{
if (tab->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing))
return;
ImGuiContext& g = *GImGui;
ImGuiTabBarSection* sections = tab_bar->Sections;
float margin = g.FontSize * 1.0f; // When to scroll to make Tab N+1 visible always make a bit of N visible to suggest more scrolling area (since we don't have a scrollbar)
int order = tab_bar->GetTabOrder(tab);
@ -7336,7 +7336,7 @@ static ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar)
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
const ImVec2 arrow_button_size = GetTabBarScrollingButtonSize();
const ImVec2 arrow_button_size(g.FontSize - 2.0f, g.FontSize + g.Style.FramePadding.y * 2.0f);
const float scrolling_buttons_width = arrow_button_size.x * 2.0f;
const ImVec2 backup_cursor_pos = window->DC.CursorPos;
@ -7605,21 +7605,19 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
const ImVec2 backup_main_cursor_pos = window->DC.CursorPos;
// Layout
const bool is_central_section = (tab->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) == 0;
size.x = tab->Width;
if (tab->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing))
window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(tab->Offset, 0.0f);
else
if (is_central_section)
window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_FLOOR(tab->Offset - tab_bar->ScrollingAnim), 0.0f);
else
window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(tab->Offset, 0.0f);
ImVec2 pos = window->DC.CursorPos;
ImRect bb(pos, pos + size);
// We don't have CPU clipping primitives to clip the CloseButton (until it becomes a texture), so need to add an extra draw call (temporary in the case of vertical animation)
// Leading buttons will be clipped by BarRect.Max.x, Trailing buttons will be clipped at BarRect.Min.x + LeadingsWidth (+ spacing if there are some buttons), and central tabs will be clipped inbetween
float offset_trailing = (flags & (ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_Leading)) ? 0.0f : tab_bar->Sections[2].Width + tab_bar->Sections[1].Spacing;
float offset_leading = (flags & ImGuiTabItemFlags_Leading) ? 0.0f : tab_bar->Sections[0].Width + tab_bar->Sections[0].Spacing;
bool want_clip_rect = (bb.Min.x < tab_bar->BarRect.Min.x + offset_leading) || (bb.Max.x > tab_bar->BarRect.Max.x - offset_trailing);
const bool want_clip_rect = is_central_section && (bb.Min.x < tab_bar->ScrollingRectMinX || bb.Max.x > tab_bar->ScrollingRectMaxX);
if (want_clip_rect)
PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->BarRect.Min.x + offset_leading), bb.Min.y - 1), ImVec2(tab_bar->BarRect.Max.x - offset_trailing, bb.Max.y), true);
PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->ScrollingRectMinX), bb.Min.y - 1), ImVec2(tab_bar->ScrollingRectMaxX, bb.Max.y), true);
ImVec2 backup_cursor_max_pos = window->DC.CursorMaxPos;
ItemSize(bb.GetSize(), style.FramePadding.y);