mirror of
https://github.com/Drezil/imgui.git
synced 2024-11-26 05:27:01 +00:00
Tables: moved + renamed TableSetupDrawChannels(), TableMergeDrawChannels() to their own section.
This commit is contained in:
parent
b7c83e4bac
commit
5180025de5
@ -2283,12 +2283,12 @@ namespace ImGui
|
||||
IMGUI_API ImGuiTable* TableFindByID(ImGuiID id);
|
||||
IMGUI_API bool BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f);
|
||||
IMGUI_API void TableBeginApplyRequests(ImGuiTable* table);
|
||||
IMGUI_API void TableUpdateDrawChannels(ImGuiTable* table);
|
||||
IMGUI_API void TableSetupDrawChannels(ImGuiTable* table);
|
||||
IMGUI_API void TableUpdateLayout(ImGuiTable* table);
|
||||
IMGUI_API void TableUpdateBorders(ImGuiTable* table);
|
||||
IMGUI_API void TableDrawBorders(ImGuiTable* table);
|
||||
IMGUI_API void TableDrawContextMenu(ImGuiTable* table);
|
||||
IMGUI_API void TableReorderDrawChannelsForMerge(ImGuiTable* table);
|
||||
IMGUI_API void TableMergeDrawChannels(ImGuiTable* table);
|
||||
IMGUI_API void TableSortSpecsSanitize(ImGuiTable* table);
|
||||
IMGUI_API void TableSortSpecsBuild(ImGuiTable* table);
|
||||
IMGUI_API void TableFixColumnSortDirection(ImGuiTableColumn* column);
|
||||
|
500
imgui_tables.cpp
500
imgui_tables.cpp
@ -6,6 +6,7 @@
|
||||
Index of this file:
|
||||
|
||||
// [SECTION] Tables: BeginTable, EndTable, etc.
|
||||
// [SECTION] Tables: Drawing
|
||||
// [SECTION] Tables: Sorting
|
||||
// [SECTION] Tables: Headers
|
||||
// [SECTION] Tables: Context Menu
|
||||
@ -81,7 +82,7 @@ Index of this file:
|
||||
// - TableSetupColumn() user submit columns details (optional)
|
||||
// - TableSetupScrollFreeze() user submit scroll freeze information (optional)
|
||||
// - TableUpdateLayout() [Internal] automatically called by the FIRST call to TableNextRow() or TableHeadersRow(): lock all widths, columns positions, clipping rectangles
|
||||
// | TableUpdateDrawChannels() - setup ImDrawList channels
|
||||
// | TableSetupDrawChannels() - setup ImDrawList channels
|
||||
// | TableUpdateBorders() - detect hovering columns for resize, ahead of contents submission
|
||||
// | TableDrawContextMenu() - draw right-click context menu
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -99,7 +100,7 @@ Index of this file:
|
||||
//-----------------------------------------------------------------------------
|
||||
// - EndTable() user ends the table
|
||||
// | TableDrawBorders() - draw outer borders, inner vertical borders
|
||||
// | TableReorderDrawChannelsForMerge() - merge draw channels if clipping isn't required
|
||||
// | TableMergeDrawChannels() - merge draw channels if clipping isn't required
|
||||
// | EndChild() - (if ScrollX/ScrollY is set)
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@ -938,7 +939,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
table->Flags &= ~ImGuiTableFlags_Resizable;
|
||||
|
||||
// Allocate draw channels
|
||||
TableUpdateDrawChannels(table);
|
||||
TableSetupDrawChannels(table);
|
||||
|
||||
// Borders
|
||||
if (table->Flags & ImGuiTableFlags_Resizable)
|
||||
@ -1111,7 +1112,7 @@ void ImGui::EndTable()
|
||||
|
||||
// Flatten channels and merge draw calls
|
||||
if ((table->Flags & ImGuiTableFlags_NoClip) == 0)
|
||||
TableReorderDrawChannelsForMerge(table);
|
||||
TableMergeDrawChannels(table);
|
||||
table->DrawSplitter.Merge(inner_window->DrawList);
|
||||
|
||||
if (!(table->Flags & ImGuiTableFlags_ScrollX) && inner_window != outer_window)
|
||||
@ -1394,247 +1395,6 @@ void ImGui::TableSetColumnWidth(int column_n, float width)
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate draw channels. Called by TableUpdateLayout()
|
||||
// - We allocate them following storage order instead of display order so reordering columns won't needlessly
|
||||
// increase overall dormant memory cost.
|
||||
// - We isolate headers draw commands in their own channels instead of just altering clip rects.
|
||||
// This is in order to facilitate merging of draw commands.
|
||||
// - After crossing FreezeRowsCount, all columns see their current draw channel changed to a second set of channels.
|
||||
// - We only use the dummy draw channel so we can push a null clipping rectangle into it without affecting other
|
||||
// channels, while simplifying per-row/per-cell overhead. It will be empty and discarded when merged.
|
||||
// - We allocate 1 or 2 background draw channels. This is because we know PushTableBackground() is only used for
|
||||
// horizontal spanning. If we allowed vertical spanning we'd need one background draw channel per merge group (1-4).
|
||||
// Draw channel allocation (before merging):
|
||||
// - NoClip --> 2+D+1 channels: bg0 + bg1 + foreground (same clip rect == 1 draw call) (FIXME-TABLE: could merge bg1 and foreground?)
|
||||
// - Clip --> 2+D+N channels
|
||||
// - FreezeRows --> 2+D+N*2 (unless scrolling value is zero)
|
||||
// - FreezeRows || FreezeColunns --> 3+D+N*2 (unless scrolling value is zero)
|
||||
// Where D is 1 if any column is clipped or hidden (dummy channel) otherwise 0.
|
||||
void ImGui::TableUpdateDrawChannels(ImGuiTable* table)
|
||||
{
|
||||
const int freeze_row_multiplier = (table->FreezeRowsCount > 0) ? 2 : 1;
|
||||
const int channels_for_row = (table->Flags & ImGuiTableFlags_NoClip) ? 1 : table->ColumnsEnabledCount;
|
||||
const int channels_for_bg = 1 + 1 * freeze_row_multiplier;
|
||||
const int channels_for_dummy = (table->ColumnsEnabledCount < table->ColumnsCount || table->EnabledUnclippedMaskByIndex != table->EnabledMaskByIndex) ? +1 : 0;
|
||||
const int channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy;
|
||||
table->DrawSplitter.Split(table->InnerWindow->DrawList, channels_total);
|
||||
table->DummyDrawChannel = (ImU8)((channels_for_dummy > 0) ? channels_total - 1 : -1);
|
||||
table->Bg1DrawChannelCurrent = TABLE_DRAW_CHANNEL_BG1_FROZEN;
|
||||
table->Bg1DrawChannelUnfrozen = (ImU8)((table->FreezeRowsCount > 0) ? 2 + channels_for_row : TABLE_DRAW_CHANNEL_BG1_FROZEN);
|
||||
|
||||
int draw_channel_current = 2;
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
if (!column->IsClipped)
|
||||
{
|
||||
column->DrawChannelFrozen = (ImU8)(draw_channel_current);
|
||||
column->DrawChannelUnfrozen = (ImU8)(draw_channel_current + (table->FreezeRowsCount > 0 ? channels_for_row + 1 : 0));
|
||||
if (!(table->Flags & ImGuiTableFlags_NoClip))
|
||||
draw_channel_current++;
|
||||
}
|
||||
else
|
||||
{
|
||||
column->DrawChannelFrozen = column->DrawChannelUnfrozen = table->DummyDrawChannel;
|
||||
}
|
||||
column->DrawChannelCurrent = column->DrawChannelFrozen;
|
||||
}
|
||||
}
|
||||
|
||||
// This function reorder draw channels based on matching clip rectangle, to facilitate merging them. Called by EndTable().
|
||||
//
|
||||
// Columns where the contents didn't stray off their local clip rectangle can be merged. To achieve
|
||||
// this we merge their clip rect and make them contiguous in the channel list, so they can be merged
|
||||
// by the call to DrawSplitter.Merge() following to the call to this function.
|
||||
//
|
||||
// We reorder draw commands by arranging them into a maximum of 4 distinct groups:
|
||||
//
|
||||
// 1 group: 2 groups: 2 groups: 4 groups:
|
||||
// [ 0. ] no freeze [ 0. ] row freeze [ 01 ] col freeze [ 01 ] row+col freeze
|
||||
// [ .. ] or no scroll [ 2. ] and v-scroll [ .. ] and h-scroll [ 23 ] and v+h-scroll
|
||||
//
|
||||
// Each column itself can use 1 channel (row freeze disabled) or 2 channels (row freeze enabled).
|
||||
// When the contents of a column didn't stray off its limit, we move its channels into the corresponding group
|
||||
// based on its position (within frozen rows/columns groups or not).
|
||||
// At the end of the operation our 1-4 groups will each have a ImDrawCmd using the same ClipRect.
|
||||
//
|
||||
// This function assume that each column are pointing to a distinct draw channel,
|
||||
// otherwise merge_group->ChannelsCount will not match set bit count of merge_group->ChannelsMask.
|
||||
//
|
||||
// Column channels will not be merged into one of the 1-4 groups in the following cases:
|
||||
// - The contents stray off its clipping rectangle (we only compare the MaxX value, not the MinX value).
|
||||
// Direct ImDrawList calls won't be taken into account by default, if you use them make sure the ImGui:: bounds
|
||||
// matches, by e.g. calling SetCursorScreenPos().
|
||||
// - The channel uses more than one draw command itself. We drop all our attempt at merging stuff here..
|
||||
// we could do better but it's going to be rare and probably not worth the hassle.
|
||||
// Columns for which the draw channel(s) haven't been merged with other will use their own ImDrawCmd.
|
||||
//
|
||||
// This function is particularly tricky to understand.. take a breath.
|
||||
void ImGui::TableReorderDrawChannelsForMerge(ImGuiTable* table)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImDrawListSplitter* splitter = &table->DrawSplitter;
|
||||
const bool has_freeze_v = (table->FreezeRowsCount > 0);
|
||||
const bool has_freeze_h = (table->FreezeColumnsCount > 0);
|
||||
|
||||
// Track which groups we are going to attempt to merge, and which channels goes into each group.
|
||||
struct MergeGroup
|
||||
{
|
||||
ImRect ClipRect;
|
||||
int ChannelsCount;
|
||||
ImBitArray<IMGUI_TABLE_MAX_DRAW_CHANNELS> ChannelsMask;
|
||||
};
|
||||
int merge_group_mask = 0x00;
|
||||
MergeGroup merge_groups[4];
|
||||
memset(merge_groups, 0, sizeof(merge_groups));
|
||||
|
||||
// 1. Scan channels and take note of those which can be merged
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
{
|
||||
if (!(table->EnabledUnclippedMaskByIndex & ((ImU64)1 << column_n)))
|
||||
continue;
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
|
||||
const int merge_group_sub_count = has_freeze_v ? 2 : 1;
|
||||
for (int merge_group_sub_n = 0; merge_group_sub_n < merge_group_sub_count; merge_group_sub_n++)
|
||||
{
|
||||
const int channel_no = (merge_group_sub_n == 0) ? column->DrawChannelFrozen : column->DrawChannelUnfrozen;
|
||||
|
||||
// Don't attempt to merge if there are multiple draw calls within the column
|
||||
ImDrawChannel* src_channel = &splitter->_Channels[channel_no];
|
||||
if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0)
|
||||
src_channel->_CmdBuffer.pop_back();
|
||||
if (src_channel->_CmdBuffer.Size != 1)
|
||||
continue;
|
||||
|
||||
// Find out the width of this merge group and check if it will fit in our column
|
||||
// (note that we assume that rendering didn't stray on the left direction. we should need a CursorMinPos to detect it)
|
||||
if (!(column->Flags & ImGuiTableColumnFlags_NoClip))
|
||||
{
|
||||
float content_max_x;
|
||||
if (!has_freeze_v)
|
||||
content_max_x = ImMax(column->ContentMaxXUnfrozen, column->ContentMaxXHeadersUsed); // No row freeze
|
||||
else if (merge_group_sub_n == 0)
|
||||
content_max_x = ImMax(column->ContentMaxXFrozen, column->ContentMaxXHeadersUsed); // Row freeze: use width before freeze
|
||||
else
|
||||
content_max_x = column->ContentMaxXUnfrozen; // Row freeze: use width after freeze
|
||||
if (content_max_x > column->ClipRect.Max.x)
|
||||
continue;
|
||||
}
|
||||
|
||||
const int merge_group_n = (has_freeze_h && column_n < table->FreezeColumnsCount ? 0 : 1) + (has_freeze_v && merge_group_sub_n == 0 ? 0 : 2);
|
||||
IM_ASSERT(channel_no < IMGUI_TABLE_MAX_DRAW_CHANNELS);
|
||||
MergeGroup* merge_group = &merge_groups[merge_group_n];
|
||||
if (merge_group->ChannelsCount == 0)
|
||||
merge_group->ClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||
merge_group->ChannelsMask.SetBit(channel_no);
|
||||
merge_group->ChannelsCount++;
|
||||
merge_group->ClipRect.Add(src_channel->_CmdBuffer[0].ClipRect);
|
||||
merge_group_mask |= (1 << merge_group_n);
|
||||
}
|
||||
|
||||
// Invalidate current draw channel
|
||||
// (we don't clear DrawChannelFrozen/DrawChannelUnfrozen solely to facilitate debugging/later inspection of data)
|
||||
column->DrawChannelCurrent = (ImU8)-1;
|
||||
}
|
||||
|
||||
// [DEBUG] Display merge groups
|
||||
#if 0
|
||||
if (g.IO.KeyShift)
|
||||
for (int merge_group_n = 0; merge_group_n < IM_ARRAYSIZE(merge_groups); merge_group_n++)
|
||||
{
|
||||
MergeGroup* merge_group = &merge_groups[merge_group_n];
|
||||
if (merge_group->ChannelsCount == 0)
|
||||
continue;
|
||||
char buf[32];
|
||||
ImFormatString(buf, 32, "MG%d:%d", merge_group_n, merge_group->ChannelsCount);
|
||||
ImVec2 text_pos = merge_group->ClipRect.Min + ImVec2(4, 4);
|
||||
ImVec2 text_size = CalcTextSize(buf, NULL);
|
||||
GetForegroundDrawList()->AddRectFilled(text_pos, text_pos + text_size, IM_COL32(0, 0, 0, 255));
|
||||
GetForegroundDrawList()->AddText(text_pos, IM_COL32(255, 255, 0, 255), buf, NULL);
|
||||
GetForegroundDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 255, 0, 255));
|
||||
}
|
||||
#endif
|
||||
|
||||
// 2. Rewrite channel list in our preferred order
|
||||
if (merge_group_mask != 0)
|
||||
{
|
||||
// We skip channel 0 (Bg0) and 1 (Bg1 frozen) from the shuffling since they won't move - see channels allocation in TableUpdateDrawChannels().
|
||||
const int LEADING_DRAW_CHANNELS = 2;
|
||||
g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized
|
||||
ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data;
|
||||
ImBitArray<IMGUI_TABLE_MAX_DRAW_CHANNELS> remaining_mask; // We need 132-bit of storage
|
||||
remaining_mask.ClearBits();
|
||||
remaining_mask.SetBitRange(LEADING_DRAW_CHANNELS, splitter->_Count - 1);
|
||||
remaining_mask.ClearBit(table->Bg1DrawChannelUnfrozen);
|
||||
IM_ASSERT(has_freeze_v == false || table->Bg1DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG1_FROZEN);
|
||||
int remaining_count = splitter->_Count - (has_freeze_v ? LEADING_DRAW_CHANNELS + 1 : LEADING_DRAW_CHANNELS);
|
||||
//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)
|
||||
{
|
||||
MergeGroup* merge_group = &merge_groups[merge_group_n];
|
||||
ImRect merge_clip_rect = merge_group->ClipRect;
|
||||
|
||||
// Extend outer-most clip limits to match those of host, so draw calls can be merged even if
|
||||
// outer-most columns have some outer padding offsetting them from their parent ClipRect.
|
||||
// The principal cases this is dealing with are:
|
||||
// - On a same-window table (not scrolling = single group), all fitting columns ClipRect -> will extend and match host ClipRect -> will merge
|
||||
// - Columns can use padding and have left-most ClipRect.Min.x and right-most ClipRect.Max.x != from host ClipRect -> will extend and match host ClipRect -> will merge
|
||||
// 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, host_rect.Min.x);
|
||||
if ((merge_group_n & 2) == 0 || !has_freeze_v)
|
||||
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, 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, 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));
|
||||
GetOverlayDrawList()->AddLine(merge_group->ClipRect.Max, merge_clip_rect.Max, IM_COL32(255, 100, 0, 200));
|
||||
#endif
|
||||
remaining_count -= merge_group->ChannelsCount;
|
||||
for (int n = 0; n < IM_ARRAYSIZE(remaining_mask.Storage); n++)
|
||||
remaining_mask.Storage[n] &= ~merge_group->ChannelsMask.Storage[n];
|
||||
for (int n = 0; n < splitter->_Count && merge_channels_count != 0; n++)
|
||||
{
|
||||
// Copy + overwrite new clip rect
|
||||
if (!merge_group->ChannelsMask.TestBit(n))
|
||||
continue;
|
||||
merge_group->ChannelsMask.ClearBit(n);
|
||||
merge_channels_count--;
|
||||
|
||||
ImDrawChannel* channel = &splitter->_Channels[n];
|
||||
IM_ASSERT(channel->_CmdBuffer.Size == 1 && merge_clip_rect.Contains(ImRect(channel->_CmdBuffer[0].ClipRect)));
|
||||
channel->_CmdBuffer[0].ClipRect = merge_clip_rect.ToVec4();
|
||||
memcpy(dst_tmp++, channel, sizeof(ImDrawChannel));
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure Bg1DrawChannelUnfrozen appears in the middle of our groups (whereas Bg0 and Bg1 frozen are fixed to 0 and 1)
|
||||
if (merge_group_n == 1 && has_freeze_v)
|
||||
memcpy(dst_tmp++, &splitter->_Channels[table->Bg1DrawChannelUnfrozen], sizeof(ImDrawChannel));
|
||||
}
|
||||
|
||||
// Append unmergeable channels that we didn't reorder at the end of the list
|
||||
for (int n = 0; n < splitter->_Count && remaining_count != 0; n++)
|
||||
{
|
||||
if (!remaining_mask.TestBit(n))
|
||||
continue;
|
||||
ImDrawChannel* channel = &splitter->_Channels[n];
|
||||
memcpy(dst_tmp++, channel, sizeof(ImDrawChannel));
|
||||
remaining_count--;
|
||||
}
|
||||
IM_ASSERT(dst_tmp == g.DrawChannelsTempMergeBuffer.Data + g.DrawChannelsTempMergeBuffer.Size);
|
||||
memcpy(splitter->_Channels.Data + LEADING_DRAW_CHANNELS, g.DrawChannelsTempMergeBuffer.Data, (splitter->_Count - LEADING_DRAW_CHANNELS) * sizeof(ImDrawChannel));
|
||||
}
|
||||
}
|
||||
|
||||
// We use a default parameter of 'init_width_or_weight == -1',
|
||||
// - with ImGuiTableColumnFlags_WidthFixed, width <= 0 --> init width == auto
|
||||
// - with ImGuiTableColumnFlags_WidthFixed, width > 0 --> init width == manual
|
||||
@ -2212,6 +1972,256 @@ void ImGui::TableSetBgColor(ImGuiTableBgTarget bg_target, ImU32 color, int colum
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] Tables: Drawing
|
||||
//-------------------------------------------------------------------------
|
||||
// - TablePushBackgroundChannel() [Internal]
|
||||
// - TablePopBackgroundChannel() [Internal]
|
||||
// - TableSetupDrawChannels() [Internal]
|
||||
// - TableReorderDrawChannelsForMerge() [Internal]
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Allocate draw channels. Called by TableUpdateLayout()
|
||||
// - We allocate them following storage order instead of display order so reordering columns won't needlessly
|
||||
// increase overall dormant memory cost.
|
||||
// - We isolate headers draw commands in their own channels instead of just altering clip rects.
|
||||
// This is in order to facilitate merging of draw commands.
|
||||
// - After crossing FreezeRowsCount, all columns see their current draw channel changed to a second set of channels.
|
||||
// - We only use the dummy draw channel so we can push a null clipping rectangle into it without affecting other
|
||||
// channels, while simplifying per-row/per-cell overhead. It will be empty and discarded when merged.
|
||||
// - We allocate 1 or 2 background draw channels. This is because we know PushTableBackground() is only used for
|
||||
// horizontal spanning. If we allowed vertical spanning we'd need one background draw channel per merge group (1-4).
|
||||
// Draw channel allocation (before merging):
|
||||
// - NoClip --> 2+D+1 channels: bg0 + bg1 + foreground (same clip rect == 1 draw call) (FIXME-TABLE: could merge bg1 and foreground?)
|
||||
// - Clip --> 2+D+N channels
|
||||
// - FreezeRows --> 2+D+N*2 (unless scrolling value is zero)
|
||||
// - FreezeRows || FreezeColunns --> 3+D+N*2 (unless scrolling value is zero)
|
||||
// Where D is 1 if any column is clipped or hidden (dummy channel) otherwise 0.
|
||||
void ImGui::TableSetupDrawChannels(ImGuiTable* table)
|
||||
{
|
||||
const int freeze_row_multiplier = (table->FreezeRowsCount > 0) ? 2 : 1;
|
||||
const int channels_for_row = (table->Flags & ImGuiTableFlags_NoClip) ? 1 : table->ColumnsEnabledCount;
|
||||
const int channels_for_bg = 1 + 1 * freeze_row_multiplier;
|
||||
const int channels_for_dummy = (table->ColumnsEnabledCount < table->ColumnsCount || table->EnabledUnclippedMaskByIndex != table->EnabledMaskByIndex) ? +1 : 0;
|
||||
const int channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy;
|
||||
table->DrawSplitter.Split(table->InnerWindow->DrawList, channels_total);
|
||||
table->DummyDrawChannel = (ImU8)((channels_for_dummy > 0) ? channels_total - 1 : -1);
|
||||
table->Bg1DrawChannelCurrent = TABLE_DRAW_CHANNEL_BG1_FROZEN;
|
||||
table->Bg1DrawChannelUnfrozen = (ImU8)((table->FreezeRowsCount > 0) ? 2 + channels_for_row : TABLE_DRAW_CHANNEL_BG1_FROZEN);
|
||||
|
||||
int draw_channel_current = 2;
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
if (!column->IsClipped)
|
||||
{
|
||||
column->DrawChannelFrozen = (ImU8)(draw_channel_current);
|
||||
column->DrawChannelUnfrozen = (ImU8)(draw_channel_current + (table->FreezeRowsCount > 0 ? channels_for_row + 1 : 0));
|
||||
if (!(table->Flags & ImGuiTableFlags_NoClip))
|
||||
draw_channel_current++;
|
||||
}
|
||||
else
|
||||
{
|
||||
column->DrawChannelFrozen = column->DrawChannelUnfrozen = table->DummyDrawChannel;
|
||||
}
|
||||
column->DrawChannelCurrent = column->DrawChannelFrozen;
|
||||
}
|
||||
}
|
||||
|
||||
// This function reorder draw channels based on matching clip rectangle, to facilitate merging them. Called by EndTable().
|
||||
// For simplicity we call it TableMergeDrawChannels() but in fact it only reorder channels + overwrite ClipRect,
|
||||
// actual merging is done by table->DrawSplitter.Merge() which is called right after TableMergeDrawChannels().
|
||||
//
|
||||
// Columns where the contents didn't stray off their local clip rectangle can be merged. To achieve
|
||||
// this we merge their clip rect and make them contiguous in the channel list, so they can be merged
|
||||
// by the call to DrawSplitter.Merge() following to the call to this function.
|
||||
// We reorder draw commands by arranging them into a maximum of 4 distinct groups:
|
||||
//
|
||||
// 1 group: 2 groups: 2 groups: 4 groups:
|
||||
// [ 0. ] no freeze [ 0. ] row freeze [ 01 ] col freeze [ 01 ] row+col freeze
|
||||
// [ .. ] or no scroll [ 2. ] and v-scroll [ .. ] and h-scroll [ 23 ] and v+h-scroll
|
||||
//
|
||||
// Each column itself can use 1 channel (row freeze disabled) or 2 channels (row freeze enabled).
|
||||
// When the contents of a column didn't stray off its limit, we move its channels into the corresponding group
|
||||
// based on its position (within frozen rows/columns groups or not).
|
||||
// At the end of the operation our 1-4 groups will each have a ImDrawCmd using the same ClipRect.
|
||||
// This function assume that each column are pointing to a distinct draw channel,
|
||||
// otherwise merge_group->ChannelsCount will not match set bit count of merge_group->ChannelsMask.
|
||||
//
|
||||
// Column channels will not be merged into one of the 1-4 groups in the following cases:
|
||||
// - The contents stray off its clipping rectangle (we only compare the MaxX value, not the MinX value).
|
||||
// Direct ImDrawList calls won't be taken into account by default, if you use them make sure the ImGui:: bounds
|
||||
// matches, by e.g. calling SetCursorScreenPos().
|
||||
// - The channel uses more than one draw command itself. We drop all our attempt at merging stuff here..
|
||||
// we could do better but it's going to be rare and probably not worth the hassle.
|
||||
// Columns for which the draw channel(s) haven't been merged with other will use their own ImDrawCmd.
|
||||
//
|
||||
// This function is particularly tricky to understand.. take a breath.
|
||||
void ImGui::TableMergeDrawChannels(ImGuiTable* table)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImDrawListSplitter* splitter = &table->DrawSplitter;
|
||||
const bool has_freeze_v = (table->FreezeRowsCount > 0);
|
||||
const bool has_freeze_h = (table->FreezeColumnsCount > 0);
|
||||
|
||||
// Track which groups we are going to attempt to merge, and which channels goes into each group.
|
||||
struct MergeGroup
|
||||
{
|
||||
ImRect ClipRect;
|
||||
int ChannelsCount;
|
||||
ImBitArray<IMGUI_TABLE_MAX_DRAW_CHANNELS> ChannelsMask;
|
||||
};
|
||||
int merge_group_mask = 0x00;
|
||||
MergeGroup merge_groups[4];
|
||||
memset(merge_groups, 0, sizeof(merge_groups));
|
||||
|
||||
// 1. Scan channels and take note of those which can be merged
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
{
|
||||
if (!(table->EnabledUnclippedMaskByIndex & ((ImU64)1 << column_n)))
|
||||
continue;
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
|
||||
const int merge_group_sub_count = has_freeze_v ? 2 : 1;
|
||||
for (int merge_group_sub_n = 0; merge_group_sub_n < merge_group_sub_count; merge_group_sub_n++)
|
||||
{
|
||||
const int channel_no = (merge_group_sub_n == 0) ? column->DrawChannelFrozen : column->DrawChannelUnfrozen;
|
||||
|
||||
// Don't attempt to merge if there are multiple draw calls within the column
|
||||
ImDrawChannel* src_channel = &splitter->_Channels[channel_no];
|
||||
if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0)
|
||||
src_channel->_CmdBuffer.pop_back();
|
||||
if (src_channel->_CmdBuffer.Size != 1)
|
||||
continue;
|
||||
|
||||
// Find out the width of this merge group and check if it will fit in our column
|
||||
// (note that we assume that rendering didn't stray on the left direction. we should need a CursorMinPos to detect it)
|
||||
if (!(column->Flags & ImGuiTableColumnFlags_NoClip))
|
||||
{
|
||||
float content_max_x;
|
||||
if (!has_freeze_v)
|
||||
content_max_x = ImMax(column->ContentMaxXUnfrozen, column->ContentMaxXHeadersUsed); // No row freeze
|
||||
else if (merge_group_sub_n == 0)
|
||||
content_max_x = ImMax(column->ContentMaxXFrozen, column->ContentMaxXHeadersUsed); // Row freeze: use width before freeze
|
||||
else
|
||||
content_max_x = column->ContentMaxXUnfrozen; // Row freeze: use width after freeze
|
||||
if (content_max_x > column->ClipRect.Max.x)
|
||||
continue;
|
||||
}
|
||||
|
||||
const int merge_group_n = (has_freeze_h && column_n < table->FreezeColumnsCount ? 0 : 1) + (has_freeze_v && merge_group_sub_n == 0 ? 0 : 2);
|
||||
IM_ASSERT(channel_no < IMGUI_TABLE_MAX_DRAW_CHANNELS);
|
||||
MergeGroup* merge_group = &merge_groups[merge_group_n];
|
||||
if (merge_group->ChannelsCount == 0)
|
||||
merge_group->ClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||
merge_group->ChannelsMask.SetBit(channel_no);
|
||||
merge_group->ChannelsCount++;
|
||||
merge_group->ClipRect.Add(src_channel->_CmdBuffer[0].ClipRect);
|
||||
merge_group_mask |= (1 << merge_group_n);
|
||||
}
|
||||
|
||||
// Invalidate current draw channel
|
||||
// (we don't clear DrawChannelFrozen/DrawChannelUnfrozen solely to facilitate debugging/later inspection of data)
|
||||
column->DrawChannelCurrent = (ImU8)-1;
|
||||
}
|
||||
|
||||
// [DEBUG] Display merge groups
|
||||
#if 0
|
||||
if (g.IO.KeyShift)
|
||||
for (int merge_group_n = 0; merge_group_n < IM_ARRAYSIZE(merge_groups); merge_group_n++)
|
||||
{
|
||||
MergeGroup* merge_group = &merge_groups[merge_group_n];
|
||||
if (merge_group->ChannelsCount == 0)
|
||||
continue;
|
||||
char buf[32];
|
||||
ImFormatString(buf, 32, "MG%d:%d", merge_group_n, merge_group->ChannelsCount);
|
||||
ImVec2 text_pos = merge_group->ClipRect.Min + ImVec2(4, 4);
|
||||
ImVec2 text_size = CalcTextSize(buf, NULL);
|
||||
GetForegroundDrawList()->AddRectFilled(text_pos, text_pos + text_size, IM_COL32(0, 0, 0, 255));
|
||||
GetForegroundDrawList()->AddText(text_pos, IM_COL32(255, 255, 0, 255), buf, NULL);
|
||||
GetForegroundDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 255, 0, 255));
|
||||
}
|
||||
#endif
|
||||
|
||||
// 2. Rewrite channel list in our preferred order
|
||||
if (merge_group_mask != 0)
|
||||
{
|
||||
// We skip channel 0 (Bg0) and 1 (Bg1 frozen) from the shuffling since they won't move - see channels allocation in TableUpdateDrawChannels().
|
||||
const int LEADING_DRAW_CHANNELS = 2;
|
||||
g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized
|
||||
ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data;
|
||||
ImBitArray<IMGUI_TABLE_MAX_DRAW_CHANNELS> remaining_mask; // We need 132-bit of storage
|
||||
remaining_mask.ClearBits();
|
||||
remaining_mask.SetBitRange(LEADING_DRAW_CHANNELS, splitter->_Count - 1);
|
||||
remaining_mask.ClearBit(table->Bg1DrawChannelUnfrozen);
|
||||
IM_ASSERT(has_freeze_v == false || table->Bg1DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG1_FROZEN);
|
||||
int remaining_count = splitter->_Count - (has_freeze_v ? LEADING_DRAW_CHANNELS + 1 : LEADING_DRAW_CHANNELS);
|
||||
//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)
|
||||
{
|
||||
MergeGroup* merge_group = &merge_groups[merge_group_n];
|
||||
ImRect merge_clip_rect = merge_group->ClipRect;
|
||||
|
||||
// Extend outer-most clip limits to match those of host, so draw calls can be merged even if
|
||||
// outer-most columns have some outer padding offsetting them from their parent ClipRect.
|
||||
// The principal cases this is dealing with are:
|
||||
// - On a same-window table (not scrolling = single group), all fitting columns ClipRect -> will extend and match host ClipRect -> will merge
|
||||
// - Columns can use padding and have left-most ClipRect.Min.x and right-most ClipRect.Max.x != from host ClipRect -> will extend and match host ClipRect -> will merge
|
||||
// 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, host_rect.Min.x);
|
||||
if ((merge_group_n & 2) == 0 || !has_freeze_v)
|
||||
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, 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, 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));
|
||||
GetOverlayDrawList()->AddLine(merge_group->ClipRect.Max, merge_clip_rect.Max, IM_COL32(255, 100, 0, 200));
|
||||
#endif
|
||||
remaining_count -= merge_group->ChannelsCount;
|
||||
for (int n = 0; n < IM_ARRAYSIZE(remaining_mask.Storage); n++)
|
||||
remaining_mask.Storage[n] &= ~merge_group->ChannelsMask.Storage[n];
|
||||
for (int n = 0; n < splitter->_Count && merge_channels_count != 0; n++)
|
||||
{
|
||||
// Copy + overwrite new clip rect
|
||||
if (!merge_group->ChannelsMask.TestBit(n))
|
||||
continue;
|
||||
merge_group->ChannelsMask.ClearBit(n);
|
||||
merge_channels_count--;
|
||||
|
||||
ImDrawChannel* channel = &splitter->_Channels[n];
|
||||
IM_ASSERT(channel->_CmdBuffer.Size == 1 && merge_clip_rect.Contains(ImRect(channel->_CmdBuffer[0].ClipRect)));
|
||||
channel->_CmdBuffer[0].ClipRect = merge_clip_rect.ToVec4();
|
||||
memcpy(dst_tmp++, channel, sizeof(ImDrawChannel));
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure Bg1DrawChannelUnfrozen appears in the middle of our groups (whereas Bg0 and Bg1 frozen are fixed to 0 and 1)
|
||||
if (merge_group_n == 1 && has_freeze_v)
|
||||
memcpy(dst_tmp++, &splitter->_Channels[table->Bg1DrawChannelUnfrozen], sizeof(ImDrawChannel));
|
||||
}
|
||||
|
||||
// Append unmergeable channels that we didn't reorder at the end of the list
|
||||
for (int n = 0; n < splitter->_Count && remaining_count != 0; n++)
|
||||
{
|
||||
if (!remaining_mask.TestBit(n))
|
||||
continue;
|
||||
ImDrawChannel* channel = &splitter->_Channels[n];
|
||||
memcpy(dst_tmp++, channel, sizeof(ImDrawChannel));
|
||||
remaining_count--;
|
||||
}
|
||||
IM_ASSERT(dst_tmp == g.DrawChannelsTempMergeBuffer.Data + g.DrawChannelsTempMergeBuffer.Size);
|
||||
memcpy(splitter->_Channels.Data + LEADING_DRAW_CHANNELS, g.DrawChannelsTempMergeBuffer.Data, (splitter->_Count - LEADING_DRAW_CHANNELS) * sizeof(ImDrawChannel));
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] Tables: Sorting
|
||||
//-------------------------------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user