From 507db499e4c8b05cdfa00a912bfde60de98bcb31 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 4 Nov 2020 22:17:58 +0100 Subject: [PATCH] Tables: work on background draw channel handling (amend "create a separate background draw channel") + Selectable disable spacing when spanning. --- imgui.cpp | 2 +- imgui_internal.h | 3 ++- imgui_tables.cpp | 48 ++++++++++++++++++++++++++--------------------- imgui_widgets.cpp | 2 +- 4 files changed, 31 insertions(+), 24 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ea7b752c8..a8a56cb23 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10522,7 +10522,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) else if (rect_type == TRT_WorkRect) { return table->WorkRect; } else if (rect_type == TRT_HostClipRect) { return table->HostClipRect; } else if (rect_type == TRT_InnerClipRect) { return table->InnerClipRect; } - else if (rect_type == TRT_BackgroundClipRect) { return table->BackgroundClipRect; } + else if (rect_type == TRT_BackgroundClipRect) { return table->BgClipRect; } 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->LastOuterHeight); } 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->ContentMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table->LastFirstRowHeight); } // Note: y1/y2 not always accurate diff --git a/imgui_internal.h b/imgui_internal.h index e87cf2835..47269a457 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2000,7 +2000,8 @@ struct ImGuiTable ImRect OuterRect; // Note: OuterRect.Max.y is often FLT_MAX until EndTable(), unless a height has been specified in BeginTable(). ImRect WorkRect; ImRect InnerClipRect; - ImRect BackgroundClipRect; // 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 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 HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable() diff --git a/imgui_tables.cpp b/imgui_tables.cpp index e1bedea06..0af329d4d 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -319,8 +319,14 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->InnerClipRect.ClipWith(table->WorkRect); // We need this to honor inner_width table->InnerClipRect.ClipWithFull(table->HostClipRect); table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : inner_window->ClipRect.Max.y; - table->BackgroundClipRect = table->InnerClipRect; - IM_ASSERT(table->BackgroundClipRect.Min.y <= table->BackgroundClipRect.Max.y); + + // Initial draw cmd starts with a BgClipRect that matches the one of its host, to facilitate merge draw commands by default. + // This is because all our cell highlight are manually clipped with BgClipRect + // Larger at first, if/after unfreezing will become same as tight + table->BgClipRect = table->InnerClipRect; + table->BgClipRectForDrawCmd = table->HostClipRect; + IM_ASSERT(table->BgClipRect.Min.y <= table->BgClipRect.Max.y); + table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow() table->FreezeRowsRequest = table->FreezeRowsCount = 0; // This will be setup by TableSetupScrollFreeze(), if any @@ -1203,7 +1209,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) // Draw bottom-most row border // FIXME-TABLE: could use AddRect or explicit VLine/HLine helper? const float border_y = table->RowPosY2; - if (border_y >= table->BackgroundClipRect.Min.y && border_y < table->BackgroundClipRect.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); } } @@ -1495,6 +1501,8 @@ void ImGui::TableReorderDrawChannelsForMerge(ImGuiTable* table) remaining_mask.SetBitRange(1, splitter->_Count - 1); // Background channel 0 == table->BgDrawChannlFrozen, not part of the merge (see channel allocation in TableUpdateDrawChannels) remaining_mask.ClearBit(table->BgDrawChannelUnfrozen); int remaining_count = splitter->_Count - ((table->BgDrawChannelUnfrozen == 0) ? 1 : 2); + //ImRect host_rect = (table->InnerWindow == table->OuterWindow) ? table->InnerClipRect : table->HostClipRect; + ImRect host_rect = table->HostClipRect; for (int merge_group_n = 0; merge_group_n < IM_ARRAYSIZE(merge_groups); merge_group_n++) { if (int merge_channels_count = merge_groups[merge_group_n].ChannelsCount) @@ -1510,13 +1518,13 @@ void ImGui::TableReorderDrawChannelsForMerge(ImGuiTable* table) // FIXME-TABLE FIXME-WORKRECT: We are wasting a merge opportunity on tables without scrolling if column doesn't fit // within host clip rect, solely because of the half-padding difference between window->WorkRect and window->InnerClipRect. if ((merge_group_n & 1) == 0 || !has_freeze_h) - merge_clip_rect.Min.x = ImMin(merge_clip_rect.Min.x, table->HostClipRect.Min.x); + merge_clip_rect.Min.x = ImMin(merge_clip_rect.Min.x, host_rect.Min.x); if ((merge_group_n & 2) == 0 || !has_freeze_v) - merge_clip_rect.Min.y = ImMin(merge_clip_rect.Min.y, table->HostClipRect.Min.y); + merge_clip_rect.Min.y = ImMin(merge_clip_rect.Min.y, host_rect.Min.y); if ((merge_group_n & 1) != 0) - merge_clip_rect.Max.x = ImMax(merge_clip_rect.Max.x, table->HostClipRect.Max.x); + merge_clip_rect.Max.x = ImMax(merge_clip_rect.Max.x, host_rect.Max.x); if ((merge_group_n & 2) != 0 && (table->Flags & ImGuiTableFlags_NoHostExtendY) == 0) - merge_clip_rect.Max.y = ImMax(merge_clip_rect.Max.y, table->HostClipRect.Max.y); + merge_clip_rect.Max.y = ImMax(merge_clip_rect.Max.y, host_rect.Max.y); #if 0 GetOverlayDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 0, 0, 200), 0.0f, ~0, 1.0f); GetOverlayDrawList()->AddLine(merge_group->ClipRect.Min, merge_clip_rect.Min, IM_COL32(255, 100, 0, 200)); @@ -1757,7 +1765,7 @@ void ImGui::TableEndRow(ImGuiTable* table) // 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. if ((table->Flags & ImGuiTableFlags_NoClip) == 0) - window->DrawList->_CmdHeader.ClipRect = ((table->IsUnfrozen && table->BgDrawChannelUnfrozen != 0) ? table->BackgroundClipRect : table->HostClipRect).ToVec4(); + window->DrawList->_CmdHeader.ClipRect = table->BgClipRectForDrawCmd.ToVec4(); table->DrawSplitter.SetCurrentChannel(window->DrawList, table->IsUnfrozen ? table->BgDrawChannelUnfrozen : 0); } @@ -1766,7 +1774,7 @@ void ImGui::TableEndRow(ImGuiTable* table) if (bg_col0 || bg_col1) { ImRect row_rect(table->WorkRect.Min.x, bg_y1, table->WorkRect.Max.x, bg_y2); - row_rect.ClipWith(table->BackgroundClipRect); + row_rect.ClipWith(table->BgClipRect); if (bg_col0 != 0 && row_rect.Min.y < row_rect.Max.y) window->DrawList->AddRectFilled(row_rect.Min, row_rect.Max, bg_col0); if (bg_col1 != 0 && row_rect.Min.y < row_rect.Max.y) @@ -1780,7 +1788,7 @@ void ImGui::TableEndRow(ImGuiTable* table) for (ImGuiTableCellData* cell_data = &table->RowCellData[0]; cell_data <= cell_data_end; cell_data++) { ImRect cell_bg_rect = TableGetCellBgRect(table, cell_data->Column); - cell_bg_rect.ClipWith(table->BackgroundClipRect); + cell_bg_rect.ClipWith(table->BgClipRect); cell_bg_rect.Min.x = ImMax(cell_bg_rect.Min.x, table->Columns[cell_data->Column].ClipRect.Min.x); cell_bg_rect.Max.x = ImMin(cell_bg_rect.Max.x, table->Columns[cell_data->Column].ClipRect.Max.x); window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor); @@ -1788,13 +1796,12 @@ void ImGui::TableEndRow(ImGuiTable* table) } // Draw top border - if (border_col && bg_y1 >= table->BackgroundClipRect.Min.y && bg_y1 < table->BackgroundClipRect.Max.y) + if (border_col && bg_y1 >= table->BgClipRect.Min.y && bg_y1 < table->BgClipRect.Max.y) window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y1), ImVec2(table->BorderX2, bg_y1), border_col, border_size); // Draw bottom border at the row unfreezing mark (always strong) - if (draw_strong_bottom_border) - if (bg_y2 >= table->BackgroundClipRect.Min.y && bg_y2 < table->BackgroundClipRect.Max.y) - window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y2), ImVec2(table->BorderX2, bg_y2), table->BorderColorStrong, border_size); + if (draw_strong_bottom_border && bg_y2 >= table->BgClipRect.Min.y && bg_y2 < table->BgClipRect.Max.y) + window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y2), ImVec2(table->BorderX2, bg_y2), table->BorderColorStrong, border_size); } // End frozen rows (when we are past the last frozen row line, teleport cursor and alter clipping rectangle) @@ -1812,11 +1819,11 @@ void ImGui::TableEndRow(ImGuiTable* table) table->IsUnfrozen = true; table->DrawSplitter.SetCurrentChannel(window->DrawList, table->BgDrawChannelUnfrozen); - // BackgroundClipRect starts as table->InnerClipRect, reduce it now + // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y); - table->BackgroundClipRect.Min.y = ImMin(y0, window->InnerClipRect.Max.y); - table->BackgroundClipRect.Max.y = window->InnerClipRect.Max.y; - IM_ASSERT(table->BackgroundClipRect.Min.y <= table->BackgroundClipRect.Max.y); + table->BgClipRect.Min.y = table->BgClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y); + table->BgClipRect.Max.y = table->BgClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y; + IM_ASSERT(table->BgClipRectForDrawCmd.Min.y <= table->BgClipRectForDrawCmd.Max.y); float row_height = table->RowPosY2 - table->RowPosY1; table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y; @@ -1825,7 +1832,7 @@ void ImGui::TableEndRow(ImGuiTable* table) { ImGuiTableColumn* column = &table->Columns[column_n]; column->DrawChannelCurrent = column->DrawChannelUnfrozen; - column->ClipRect.Min.y = table->BackgroundClipRect.Min.y; + column->ClipRect.Min.y = table->BgClipRectForDrawCmd.Min.y; } // Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y @@ -2034,8 +2041,7 @@ void ImGui::PushTableBackground() // Optimization: avoid SetCurrentChannel() + PushClipRect() table->HostBackupClipRect = window->ClipRect; - SetWindowClipRectBeforeSetChannel(window, table->BackgroundClipRect); - //SetWindowClipRectBeforeSetChannel(window, table->HostClipRect); + SetWindowClipRectBeforeSetChannel(window, table->BgClipRectForDrawCmd); table->DrawSplitter.SetCurrentChannel(window->DrawList, table->IsUnfrozen ? table->BgDrawChannelUnfrozen : 0); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3df58daa9..262969b65 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5953,7 +5953,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl ImRect bb(min_x, pos.y, text_max.x, text_max.y); if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0) { - const float spacing_x = style.ItemSpacing.x; + const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x; const float spacing_y = style.ItemSpacing.y; const float spacing_L = IM_FLOOR(spacing_x * 0.50f); const float spacing_U = IM_FLOOR(spacing_y * 0.50f);