Tables: clarify assumption that rendering of bg/borders in bg0/bg1 are cpu-clipped allowing frozen/unfrozen to share drawcmd + remove offset on outer borders of scrolling tables.

This commit is contained in:
ocornut 2020-12-18 14:14:15 +01:00
parent 30468829c2
commit 22ace4438c
2 changed files with 22 additions and 23 deletions

View File

@ -2019,7 +2019,8 @@ struct ImGuiTable
ImRect WorkRect; ImRect WorkRect;
ImRect InnerClipRect; ImRect InnerClipRect;
ImRect BgClipRect; // We use this to cpu-clip cell background color fill ImRect BgClipRect; // We use this to cpu-clip cell background color fill
ImRect BgClipRectForDrawCmd; ImRect Bg0ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG0/1 channel. This tends to be == OuterWindow->ClipRect at BeginTable() because output in BG0/BG1 is cpu-clipped
ImRect Bg2ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG2 channel. This tends to be a correct, tight-fit, because output to BG2 are done by widgets relying on regular ClipRect.
ImRect HostClipRect; // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window. ImRect HostClipRect; // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window.
ImRect HostBackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable() ImRect HostBackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable()
ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable() ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable()

View File

@ -1601,7 +1601,7 @@ void ImGui::TableEndRow(ImGuiTable* table)
// In theory we could call SetWindowClipRectBeforeSetChannel() but since we know TableEndRow() is // In theory we could call SetWindowClipRectBeforeSetChannel() but since we know TableEndRow() is
// always followed by a change of clipping rectangle we perform the smallest overwrite possible here. // always followed by a change of clipping rectangle we perform the smallest overwrite possible here.
if ((table->Flags & ImGuiTableFlags_NoClip) == 0) if ((table->Flags & ImGuiTableFlags_NoClip) == 0)
window->DrawList->_CmdHeader.ClipRect = table->BgClipRectForDrawCmd.ToVec4(); window->DrawList->_CmdHeader.ClipRect = table->Bg0ClipRectForDrawCmd.ToVec4();
table->DrawSplitter.SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0); table->DrawSplitter.SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0);
} }
@ -1657,10 +1657,10 @@ void ImGui::TableEndRow(ImGuiTable* table)
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y); float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y);
table->BgClipRect.Min.y = table->BgClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y); table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y);
table->BgClipRect.Max.y = table->BgClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y; table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y;
table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen; table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
IM_ASSERT(table->BgClipRectForDrawCmd.Min.y <= table->BgClipRectForDrawCmd.Max.y); IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);
float row_height = table->RowPosY2 - table->RowPosY1; float row_height = table->RowPosY2 - table->RowPosY1;
table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y; table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y;
@ -1669,7 +1669,7 @@ void ImGui::TableEndRow(ImGuiTable* table)
{ {
ImGuiTableColumn* column = &table->Columns[column_n]; ImGuiTableColumn* column = &table->Columns[column_n];
column->DrawChannelCurrent = column->DrawChannelUnfrozen; column->DrawChannelCurrent = column->DrawChannelUnfrozen;
column->ClipRect.Min.y = table->BgClipRectForDrawCmd.Min.y; column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y;
} }
// Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y // Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y
@ -2019,7 +2019,7 @@ void ImGui::TablePushBackgroundChannel()
// Optimization: avoid SetCurrentChannel() + PushClipRect() // Optimization: avoid SetCurrentChannel() + PushClipRect()
table->HostBackupInnerClipRect = window->ClipRect; table->HostBackupInnerClipRect = window->ClipRect;
SetWindowClipRectBeforeSetChannel(window, table->BgClipRectForDrawCmd); SetWindowClipRectBeforeSetChannel(window, table->Bg2ClipRectForDrawCmd);
table->DrawSplitter.SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent); table->DrawSplitter.SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent);
} }
@ -2046,7 +2046,7 @@ void ImGui::TablePopBackgroundChannel()
// - We allocate 1 or 2 background draw channels. This is because we know TablePushBackgroundChannel() is only used for // - We allocate 1 or 2 background draw channels. This is because we know TablePushBackgroundChannel() is only used for
// horizontal spanning. If we allowed vertical spanning we'd need one background draw channel per merge group (1-4). // horizontal spanning. If we allowed vertical spanning we'd need one background draw channel per merge group (1-4).
// Draw channel allocation (before merging): // Draw channel allocation (before merging):
// - NoClip --> 2+D+1 channels: bg0/1 + bg2 + foreground (same clip rect == 1 draw call) (FIXME-TABLE: could merge bg2 and foreground?) // - NoClip --> 2+D+1 channels: bg0/1 + bg2 + foreground (same clip rect == always 1 draw call)
// - Clip --> 2+D+N channels // - Clip --> 2+D+N channels
// - FreezeRows --> 2+D+N*2 (unless scrolling value is zero) // - FreezeRows --> 2+D+N*2 (unless scrolling value is zero)
// - FreezeRows || FreezeColunns --> 3+D+N*2 (unless scrolling value is zero) // - FreezeRows || FreezeColunns --> 3+D+N*2 (unless scrolling value is zero)
@ -2085,7 +2085,8 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table)
// All our cell highlight are manually clipped with BgClipRect. When unfreezing it will be made smaller to fit scrolling rect. // All our cell highlight are manually clipped with BgClipRect. When unfreezing it will be made smaller to fit scrolling rect.
// (This technically isn't part of setting up draw channels, but is reasonably related to be done here) // (This technically isn't part of setting up draw channels, but is reasonably related to be done here)
table->BgClipRect = table->InnerClipRect; table->BgClipRect = table->InnerClipRect;
table->BgClipRectForDrawCmd = table->HostClipRect; table->Bg0ClipRectForDrawCmd = table->OuterWindow->ClipRect;
table->Bg2ClipRectForDrawCmd = table->HostClipRect;
IM_ASSERT(table->BgClipRect.Min.y <= table->BgClipRect.Max.y); IM_ASSERT(table->BgClipRect.Min.y <= table->BgClipRect.Max.y);
} }
@ -2288,19 +2289,17 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
void ImGui::TableDrawBorders(ImGuiTable* table) void ImGui::TableDrawBorders(ImGuiTable* table)
{ {
ImGuiWindow* inner_window = table->InnerWindow; ImGuiWindow* inner_window = table->InnerWindow;
ImGuiWindow* outer_window = table->OuterWindow;
table->DrawSplitter.SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_BG0); table->DrawSplitter.SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_BG0);
if (inner_window->Hidden || !table->HostClipRect.Overlaps(table->InnerClipRect)) if (inner_window->Hidden || !table->HostClipRect.Overlaps(table->InnerClipRect))
return; return;
ImDrawList* inner_drawlist = inner_window->DrawList; ImDrawList* inner_drawlist = inner_window->DrawList;
ImDrawList* outer_drawlist = outer_window->DrawList; inner_drawlist->PushClipRect(table->Bg0ClipRectForDrawCmd.Min, table->Bg0ClipRectForDrawCmd.Max, false);
// Draw inner border and resizing feedback // Draw inner border and resizing feedback
const float border_size = TABLE_BORDER_SIZE; const float border_size = TABLE_BORDER_SIZE;
const float draw_y1 = table->InnerRect.Min.y; const float draw_y1 = table->InnerRect.Min.y;
const float draw_y2_body = table->InnerRect.Max.y; const float draw_y2_body = table->InnerRect.Max.y;
const float draw_y2_head = table->IsUsingHeaders ? ((table->FreezeRowsCount >= 1 ? table->OuterRect.Min.y : table->WorkRect.Min.y) + table->LastFirstRowHeight) : draw_y1; const float draw_y2_head = table->IsUsingHeaders ? ((table->FreezeRowsCount >= 1 ? table->OuterRect.Min.y : table->WorkRect.Min.y) + table->LastFirstRowHeight) : draw_y1;
if (table->Flags & ImGuiTableFlags_BordersInnerV) if (table->Flags & ImGuiTableFlags_BordersInnerV)
{ {
for (int order_n = 0; order_n < table->ColumnsCount; order_n++) for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
@ -2351,23 +2350,21 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
// parent. In inner_window, it won't reach out over scrollbars. Another weird solution would be to display part // parent. In inner_window, it won't reach out over scrollbars. Another weird solution would be to display part
// of it in inner window, and the part that's over scrollbars in the outer window..) // of it in inner window, and the part that's over scrollbars in the outer window..)
// Either solution currently won't allow us to use a larger border size: the border would clipped. // Either solution currently won't allow us to use a larger border size: the border would clipped.
ImRect outer_border = table->OuterRect; const ImRect outer_border = table->OuterRect;
const ImU32 outer_col = table->BorderColorStrong; const ImU32 outer_col = table->BorderColorStrong;
if (inner_window != outer_window) // FIXME-TABLE
outer_border.Expand(1.0f);
if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter) if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter)
{ {
outer_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, ~0, border_size); inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, ~0, border_size);
} }
else if (table->Flags & ImGuiTableFlags_BordersOuterV) else if (table->Flags & ImGuiTableFlags_BordersOuterV)
{ {
outer_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Min.x, outer_border.Max.y), outer_col, border_size); inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Min.x, outer_border.Max.y), outer_col, border_size);
outer_drawlist->AddLine(ImVec2(outer_border.Max.x, outer_border.Min.y), outer_border.Max, outer_col, border_size); inner_drawlist->AddLine(ImVec2(outer_border.Max.x, outer_border.Min.y), outer_border.Max, outer_col, border_size);
} }
else if (table->Flags & ImGuiTableFlags_BordersOuterH) else if (table->Flags & ImGuiTableFlags_BordersOuterH)
{ {
outer_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Max.x, outer_border.Min.y), outer_col, border_size); inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Max.x, outer_border.Min.y), outer_col, border_size);
outer_drawlist->AddLine(ImVec2(outer_border.Min.x, outer_border.Max.y), outer_border.Max, outer_col, border_size); inner_drawlist->AddLine(ImVec2(outer_border.Min.x, outer_border.Max.y), outer_border.Max, outer_col, border_size);
} }
} }
if ((table->Flags & ImGuiTableFlags_BordersInnerH) && table->RowPosY2 < table->OuterRect.Max.y) if ((table->Flags & ImGuiTableFlags_BordersInnerH) && table->RowPosY2 < table->OuterRect.Max.y)
@ -2377,6 +2374,8 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
if (border_y >= table->BgClipRect.Min.y && border_y < table->BgClipRect.Max.y) if (border_y >= table->BgClipRect.Min.y && border_y < table->BgClipRect.Max.y)
inner_drawlist->AddLine(ImVec2(table->BorderX1, border_y), ImVec2(table->BorderX2, border_y), table->BorderColorLight, border_size); inner_drawlist->AddLine(ImVec2(table->BorderX1, border_y), ImVec2(table->BorderX2, border_y), table->BorderColorLight, border_size);
} }
inner_drawlist->PopClipRect();
} }
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
@ -2638,7 +2637,6 @@ void ImGui::TableHeadersRow()
// Emit a column header (text + optional sort order) // Emit a column header (text + optional sort order)
// We cpu-clip text here so that all columns headers can be merged into a same draw call. // We cpu-clip text here so that all columns headers can be merged into a same draw call.
// Note that because of how we cpu-clip and display sorting indicators, you _cannot_ use SameLine() after a TableHeader() // Note that because of how we cpu-clip and display sorting indicators, you _cannot_ use SameLine() after a TableHeader()
// FIXME-TABLE: Style confusion between CellPadding.y and FramePadding.y
void ImGui::TableHeader(const char* label) void ImGui::TableHeader(const char* label)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
@ -2660,7 +2658,7 @@ void ImGui::TableHeader(const char* label)
ImVec2 label_pos = window->DC.CursorPos; ImVec2 label_pos = window->DC.CursorPos;
// If we already got a row height, there's use that. // If we already got a row height, there's use that.
// FIXME-TABLE-PADDING: Problem if the correct outer-padding CellBgRect strays off our ClipRect // FIXME-TABLE: Padding problem if the correct outer-padding CellBgRect strays off our ClipRect?
ImRect cell_r = TableGetCellBgRect(table, column_n); ImRect cell_r = TableGetCellBgRect(table, column_n);
float label_height = ImMax(label_size.y, table->RowMinHeight - table->CellPaddingY * 2.0f); float label_height = ImMax(label_size.y, table->RowMinHeight - table->CellPaddingY * 2.0f);
@ -2671,7 +2669,7 @@ void ImGui::TableHeader(const char* label)
const float ARROW_SCALE = 0.65f; const float ARROW_SCALE = 0.65f;
if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort)) if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort))
{ {
w_arrow = ImFloor(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x);// table->CellPadding.x); w_arrow = ImFloor(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x);
if (column->SortOrder > 0) if (column->SortOrder > 0)
{ {
ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), "%d", column->SortOrder + 1); ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), "%d", column->SortOrder + 1);