diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 80da9102..3a80c397 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4456,9 +4456,9 @@ static void ShowDemoWindowTables() | ImGuiTableFlags_SizingPolicyFixedX ; - enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable }; + enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable, CT_SelectableSpanRow }; static int contents_type = CT_Button; - const char* contents_type_names[] = { "Text", "Button", "SmallButton", "FillButton", "Selectable" }; + const char* contents_type_names[] = { "Text", "Button", "SmallButton", "FillButton", "Selectable", "Selectable (span row)" }; static int freeze_cols = 1; static int freeze_rows = 1; static int items_count = IM_ARRAYSIZE(template_items_names); @@ -4662,9 +4662,10 @@ static void ShowDemoWindowTables() ImGui::SmallButton(label); else if (contents_type == CT_FillButton) ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); - else if (contents_type == CT_Selectable) + else if (contents_type == CT_Selectable || contents_type == CT_SelectableSpanRow) { - if (ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap, ImVec2(0, row_min_height))) + ImGuiSelectableFlags selectable_flags = (contents_type == CT_SelectableSpanRow) ? ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap : ImGuiSelectableFlags_None; + if (ImGui::Selectable(label, item_is_selected, selectable_flags, ImVec2(0, row_min_height))) { if (ImGui::GetIO().KeyCtrl) { diff --git a/imgui_internal.h b/imgui_internal.h index d0af472e..c49d0a61 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1957,7 +1957,7 @@ struct ImGuiTableCellData struct ImGuiTable { ImGuiID ID; - ImGuiTableFlags Flags; + ImGuiTableFlags Flags; void* RawData; // Single allocation to hold Columns[], DisplayOrderToIndex[] and RowCellData[] ImSpan Columns; // Point within RawData[] ImSpan DisplayOrderToIndex; // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1) @@ -2271,12 +2271,12 @@ namespace ImGui IMGUI_API void TableUpdateLayout(ImGuiTable* table); IMGUI_API void TableUpdateBorders(ImGuiTable* table); IMGUI_API void TableSetColumnWidth(int column_n, float width); - IMGUI_API void TableSetColumnWidth(ImGuiTable* table, ImGuiTableColumn* column, float width); + IMGUI_API void TableSetColumnVisible(int column_n, bool visible); IMGUI_API void TableDrawBorders(ImGuiTable* table); IMGUI_API void TableDrawContextMenu(ImGuiTable* table); IMGUI_API void TableOpenContextMenu(int column_n = -1); IMGUI_API void TableReorderDrawChannelsForMerge(ImGuiTable* table); - IMGUI_API void TableSetColumnSortDirection(ImGuiTable* table, int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs); + IMGUI_API void TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs); IMGUI_API void TableSortSpecsSanitize(ImGuiTable* table); IMGUI_API void TableSortSpecsBuild(ImGuiTable* table); IMGUI_API void TableBeginRow(ImGuiTable* table); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 74a8026c..c39016aa 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -69,7 +69,7 @@ // | - TableUpdateColumnsWeightFromWidth()- recompute columns weights (of stretch columns) from their respective width // - TableSetupColumn() user submit columns details (optional) // - TableSetupScrollFreeze() user submit scroll freeze information (optional) -// - TableUpdateLayout() [Internal] automatically called by the FIRST call to TableNextRow() or Table*Header(): lock all widths, columns positions, clipping rectangles +// - TableUpdateLayout() [Internal] automatically called by the FIRST call to TableNextRow() or TableHeadersRow(): lock all widths, columns positions, clipping rectangles // | TableUpdateDrawChannels() - setup ImDrawList channels // | TableUpdateBorders() - detect hovering columns for resize, ahead of contents submission // | TableDrawContextMenu() - draw right-click context menu @@ -78,8 +78,11 @@ // | TableSortSpecsClickColumn() - when left-clicked: alter sort order and sort direction // | TableOpenContextMenu() - when right-clicked: trigger opening of the default context menu // - TableGetSortSpecs() user queries updated sort specs (optional, generally after submitting headers) -// - TableNextRow() / TableNextColumn() user begin into the first row, also automatically called by TableHeadersRow() -// | TableEndCell() - close existing cell if not the first time +// - TableNextRow() user begin into a new row (also automatically called by TableHeadersRow()) +// | TableEndRow() - finish existing row +// | TableBeginRow() - add a new row +// - TableSetColumnIndex() / TableNextColumn() user begin into a cell +// | TableEndCell() - close existing cell // | TableBeginCell() - enter into current cell // - [...] user emit contents //----------------------------------------------------------------------------- @@ -430,7 +433,7 @@ void ImGui::TableBeginUpdateColumns(ImGuiTable* table) if (table->InstanceCurrent == 0) { if (table->ResizedColumn != -1 && table->ResizedColumnNextWidth != FLT_MAX) - TableSetColumnWidth(table, &table->Columns[table->ResizedColumn], table->ResizedColumnNextWidth); + TableSetColumnWidth(table->ResizedColumn, table->ResizedColumnNextWidth); table->LastResizedColumn = table->ResizedColumn; table->ResizedColumnNextWidth = FLT_MAX; table->ResizedColumn = -1; @@ -1215,21 +1218,16 @@ static void TableUpdateColumnsWeightFromWidth(ImGuiTable* table) } } -// Public wrapper // 'width' = inner column width, without padding void ImGui::TableSetColumnWidth(int column_n, float width) { ImGuiContext& g = *GImGui; ImGuiTable* table = g.CurrentTable; - IM_ASSERT(table != NULL); - IM_ASSERT(table->IsLayoutLocked == false); + IM_ASSERT(table != NULL && table->IsLayoutLocked == false); IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); - TableSetColumnWidth(table, &table->Columns[column_n], width); -} + ImGuiTableColumn* column_0 = &table->Columns[column_n]; + float column_0_width = width; -// [Internal] -void ImGui::TableSetColumnWidth(ImGuiTable* table, ImGuiTableColumn* column_0, float column_0_width) -{ // Constraints const float min_width = TableGetMinColumnWidth(); float max_width_0 = FLT_MAX; @@ -1303,6 +1301,16 @@ void ImGui::TableSetColumnWidth(ImGuiTable* table, ImGuiTableColumn* column_0, f } } +// Public wrapper +void ImGui::TableSetColumnVisible(int column_n, bool visible) +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + IM_ASSERT(table != NULL && table->IsLayoutLocked == false); + IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); + table->Columns[column_n].IsVisibleNextFrame = visible; +} + // 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. @@ -1314,10 +1322,11 @@ void ImGui::TableSetColumnWidth(ImGuiTable* table, ImGuiTableColumn* column_0, f // - 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 --> 1+1 channels: background + foreground (same clip rect == 1 draw call) -// - Clip --> 1+N channels -// - FreezeRows --> 1+N*2 (unless scrolling value is zero) -// - FreezeRows || FreezeColunns --> 2+N*2 (unless scrolling value is zero) +// - NoClip --> 1+D+1 channels: background + foreground (same clip rect == 1 draw call) +// - Clip --> 1+D+N channels +// - FreezeRows --> 1+D+N*2 (unless scrolling value is zero) +// - FreezeRows || FreezeColunns --> 2+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; @@ -1466,12 +1475,13 @@ void ImGui::TableReorderDrawChannelsForMerge(ImGuiTable* table) // 2. Rewrite channel list in our preferred order if (merge_group_mask != 0) { - // Use shared temporary storage so the allocation gets amortized - g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - 1); + // We skip background channel 0 from the shuffling since it won't move (see channels allocation in TableUpdateDrawChannels()). + const int LEADING_DRAW_CHANNELS = 1; + g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data; - ImBitArray remaining_mask; // We need 130-bit of storage + ImBitArray remaining_mask; // We need 131-bit of storage remaining_mask.ClearBits(); - remaining_mask.SetBitRange(1, splitter->_Count - 1); // Background channel 0 == table->BgDrawChannlFrozen, not part of the merge (see channel allocation in TableUpdateDrawChannels) + remaining_mask.SetBitRange(LEADING_DRAW_CHANNELS, splitter->_Count - 1); 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; @@ -1536,7 +1546,7 @@ void ImGui::TableReorderDrawChannelsForMerge(ImGuiTable* table) remaining_count--; } IM_ASSERT(dst_tmp == g.DrawChannelsTempMergeBuffer.Data + g.DrawChannelsTempMergeBuffer.Size); - memcpy(splitter->_Channels.Data + 1, g.DrawChannelsTempMergeBuffer.Data, (splitter->_Count - 1) * sizeof(ImDrawChannel)); + memcpy(splitter->_Channels.Data + LEADING_DRAW_CHANNELS, g.DrawChannelsTempMergeBuffer.Data, (splitter->_Count - LEADING_DRAW_CHANNELS) * sizeof(ImDrawChannel)); } } @@ -1726,10 +1736,9 @@ void ImGui::TableEndRow(ImGuiTable* table) // Decide of top border color ImU32 border_col = 0; const float border_size = TABLE_BORDER_SIZE; - if (table->CurrentRow != 0 || table->InnerWindow == table->OuterWindow) + if (table->CurrentRow > 0 || table->InnerWindow == table->OuterWindow) if (table->Flags & ImGuiTableFlags_BordersInnerH) - if (table->CurrentRow > 0) - border_col = (table->LastRowFlags & ImGuiTableRowFlags_Headers) ? table->BorderColorStrong : table->BorderColorLight; + border_col = (table->LastRowFlags & ImGuiTableRowFlags_Headers) ? table->BorderColorStrong : table->BorderColorLight; const bool draw_cell_bg_color = table->RowCellDataCurrent >= 0; const bool draw_strong_bottom_border = unfreeze_rows_actual; @@ -1791,7 +1800,6 @@ void ImGui::TableEndRow(ImGuiTable* table) { IM_ASSERT(table->IsUnfrozen == false); table->IsUnfrozen = true; - table->DrawSplitter.SetCurrentChannel(window->DrawList, table->BgDrawChannelUnfrozen); // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y); @@ -2322,7 +2330,7 @@ void ImGui::TableHeader(const char* label) sort_direction = (column->Flags & ImGuiTableColumnFlags_PreferSortDescending) ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending; else sort_direction = (column->SortDirection == ImGuiSortDirection_Ascending) ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending; - TableSetColumnSortDirection(table, column_n, sort_direction, g.IO.KeyShift); + TableSetColumnSortDirection(column_n, sort_direction, g.IO.KeyShift); } } @@ -2347,8 +2355,11 @@ void ImGui::TableHeader(const char* label) // Note that the NoSortAscending/NoSortDescending flags are processed in TableSortSpecsSanitize(), and they may change/revert // the value of SortDirection. We could technically also do it here but it would be unnecessary and duplicate code. -void ImGui::TableSetColumnSortDirection(ImGuiTable* table, int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs) +void ImGui::TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs) { + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + if (!(table->Flags & ImGuiTableFlags_MultiSortable)) append_to_sort_specs = false;