Tables: Added TableSetBgColor() api with color for RowBg and CellBg colors.

This commit is contained in:
omar 2020-07-28 15:53:14 +02:00 committed by ocornut
parent b6405a291d
commit 9d8b40414a
4 changed files with 172 additions and 26 deletions

23
imgui.h
View File

@ -157,6 +157,7 @@ typedef int ImGuiMouseButton; // -> enum ImGuiMouseButton_ // Enum: A
typedef int ImGuiMouseCursor; // -> enum ImGuiMouseCursor_ // Enum: A mouse cursor identifier
typedef int ImGuiSortDirection; // -> enum ImGuiSortDirection_ // Enum: A sorting direction (ascending or descending)
typedef int ImGuiStyleVar; // -> enum ImGuiStyleVar_ // Enum: A variable identifier for styling
typedef int ImGuiTableBgTarget; // -> enum ImGuiTableBgTarget_ // Enum: A color target for TableSetBgColor()
typedef int ImDrawCornerFlags; // -> enum ImDrawCornerFlags_ // Flags: for ImDrawList::AddRect(), AddRectFilled() etc.
typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: for ImDrawList
typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas build
@ -684,6 +685,7 @@ namespace ImGui
IMGUI_API bool TableGetColumnIsVisible(int column_n = -1); // return true if column is visible. Same value is also returned by TableNextCell() and TableSetColumnIndex(). Pass -1 to use current column.
IMGUI_API bool TableGetColumnIsSorted(int column_n = -1); // return true if column is included in the sort specs. Rarely used, can be useful to tell if a data change should trigger resort. Equivalent to test ImGuiTableSortSpecs's ->ColumnsMask & (1 << column_n). Pass -1 to use current column.
IMGUI_API int TableGetHoveredColumn(); // return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered.
IMGUI_API void TableSetBgColor(ImGuiTableBgTarget bg_target, ImU32 color, int column_n = -1); // change the color of a cell, row, or column. See ImGuiTableBgTarget_ flags for details.
// Tables: Headers & Columns declaration
// - Use TableSetupColumn() to specify label, resizing policy, default width, id, various other flags etc.
// - The name passed to TableSetupColumn() is used by TableAutoHeaders() and by the context-menu
@ -1036,7 +1038,7 @@ enum ImGuiTableFlags_
ImGuiTableFlags_MultiSortable = 1 << 4, // Allow sorting on multiple columns by holding Shift (sort_specs_count may be > 1). Call TableGetSortSpecs() to obtain sort specs.
ImGuiTableFlags_NoSavedSettings = 1 << 5, // Disable persisting columns order, width and sort settings in the .ini file.
// Decoration
ImGuiTableFlags_RowBg = 1 << 6, // Use ImGuiCol_TableRowBg and ImGuiCol_TableRowBgAlt colors behind each rows.
ImGuiTableFlags_RowBg = 1 << 6, // Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent to calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually)
ImGuiTableFlags_BordersInnerH = 1 << 7, // Draw horizontal borders between rows.
ImGuiTableFlags_BordersOuterH = 1 << 8, // Draw horizontal borders at the top and bottom.
ImGuiTableFlags_BordersInnerV = 1 << 9, // Draw vertical borders between columns.
@ -1109,6 +1111,25 @@ enum ImGuiTableRowFlags_
ImGuiTableRowFlags_Headers = 1 << 0 // Identify header row (set default background color + width of its contents accounted different for auto column width)
};
// Enum for ImGui::TableSetBgColor()
// Background colors are rendering in 3 layers:
// - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set.
// - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set.
// - Layer 2: draw with CellBg color if set.
// The purpose of the two row/columns layers is to let you decide if a background color changes should override or blend with the existing color.
// When using ImGuiTableFlags_RowBg on the table, each row has the RowBg0 color automatically set for odd/even rows.
// If you set the color of RowBg0 target, your color will override the existing RowBg0 color.
// If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color.
enum ImGuiTableBgTarget_
{
ImGuiTableBgTarget_None = 0,
ImGuiTableBgTarget_ColumnBg0 = 1, // FIXME-TABLE: Todo. Set column background color 0 (generally used for background
ImGuiTableBgTarget_ColumnBg1 = 2, // FIXME-TABLE: Todo. Set column background color 1 (generally used for selection marking)
ImGuiTableBgTarget_RowBg0 = 3, // Set row background color 0 (generally used for background, automatically set when ImGuiTableFlags_RowBg is used)
ImGuiTableBgTarget_RowBg1 = 4, // Set row background color 1 (generally used for selection marking)
ImGuiTableBgTarget_CellBg = 5 // Set cell background color (top-most color)
};
// Flags for ImGui::IsWindowFocused()
enum ImGuiFocusedFlags_
{

View File

@ -3902,14 +3902,11 @@ static void ShowDemoWindowTables()
if (ImGui::TreeNode("Row height"))
{
HelpMarker("You can pass a 'min_row_height' to TableNextRow().\n\nRows are padded with 'style.CellPadding.y' on top and bottom, so effectively the minimum row height will always be >= 'style.CellPadding.y * 2.0f'.\n\nWe cannot honor a _maximum_ row height as that would requires a unique clipping rectangle per row.");
if (ImGui::BeginTable("##2ways", 2, ImGuiTableFlags_Borders))
if (ImGui::BeginTable("##Table", 1, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerV))
{
float min_row_height = ImGui::GetFontSize() + ImGui::GetStyle().CellPadding.y * 2.0f;
ImGui::TableNextRow(ImGuiTableRowFlags_None, min_row_height);
ImGui::Text("min_row_height = %.2f", min_row_height);
for (int row = 0; row < 10; row++)
{
min_row_height = (float)(int)(ImGui::GetFontSize() * 0.30f * row);
float min_row_height = (float)(int)(ImGui::GetFontSize() * 0.30f * row);
ImGui::TableNextRow(ImGuiTableRowFlags_None, min_row_height);
ImGui::Text("min_row_height = %.2f", min_row_height);
}
@ -3918,6 +3915,61 @@ static void ShowDemoWindowTables()
ImGui::TreePop();
}
if (open_action != -1)
ImGui::SetNextItemOpen(open_action != 0);
if (ImGui::TreeNode("Background color"))
{
static ImGuiTableFlags table_flags = ImGuiTableFlags_RowBg;
ImGui::CheckboxFlags("ImGuiTableFlags_Borders", (unsigned int*)&table_flags, ImGuiTableFlags_Borders);
ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", (unsigned int*)&table_flags, ImGuiTableFlags_RowBg);
ImGui::SameLine(); HelpMarker("ImGuiTableFlags_RowBg automatically sets RowBg0 to alternative colors pulled from the Style.");
static int row_bg_type = 1;
static int row_bg_target = 1;
static int cell_bg_type = 1;
ImGui::Combo("row bg type", (int*)&row_bg_type, "None\0Red\0Gradient\0");
ImGui::Combo("row bg target", (int*)&row_bg_target, "RowBg0\0RowBg1\0"); ImGui::SameLine(); HelpMarker("Target RowBg0 to override the alternating odd/even colors,\nTarget RowBg1 to blend with them.");
ImGui::Combo("cell bg type", (int*)&cell_bg_type, "None\0Blue\0"); ImGui::SameLine(); HelpMarker("We are colorizing cells to B1->C2 here.");
IM_ASSERT(row_bg_type >= 0 && row_bg_type <= 2);
IM_ASSERT(row_bg_target >= 0 && row_bg_target <= 1);
IM_ASSERT(cell_bg_type >= 0 && cell_bg_type <= 1);
if (ImGui::BeginTable("##Table", 5, table_flags))
{
for (int row = 0; row < 6; row++)
{
ImGui::TableNextRow();
// Demonstrate setting a row background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBgX, ...)'
// We use a transparent color so we can see the one behind in case our target is RowBg1 and RowBg0 was already targeted by the ImGuiTableFlags_RowBg flag.
if (row_bg_type != 0)
{
ImU32 row_bg_color = ImGui::GetColorU32(row_bg_type == 1 ? ImVec4(0.7f, 0.3f, 0.3f, 0.65f) : ImVec4(0.2f + row * 0.1f, 0.2f, 0.2f, 0.65f)); // Flat or Gradient?
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0 + row_bg_target, row_bg_color);
}
// Fill cells
for (int column = 0; column < 5; column++)
{
ImGui::TableSetColumnIndex(column);
ImGui::Text("%c%c", 'A' + row, '0' + column);
// Change background of Cells B1->C2
// Demonstrate setting a cell background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ...)'
// (the CellBg color will be blended over the RowBg and ColumnBg colors)
// We can also pass a column number as a third parameter to TableSetBgColor() and do this outside the column loop.
if (row >= 1 && row <= 2 && column >= 1 && column <= 2 && cell_bg_type == 1)
{
ImU32 cell_bg_color = ImGui::GetColorU32(ImVec4(0.3f, 0.3f, 0.7f, 0.65f));
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, cell_bg_color);
}
}
}
ImGui::EndTable();
}
ImGui::TreePop();
}
if (open_action != -1)
ImGui::SetNextItemOpen(open_action != 0);
if (ImGui::TreeNode("Tree view"))

View File

@ -1883,11 +1883,11 @@ struct ImGuiTabBar
#ifdef IMGUI_HAS_TABLE
#define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code
#define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code which cannot be used as a regular color.
#define IMGUI_TABLE_MAX_COLUMNS 64 // sizeof(ImU64) * 8. This is solely because we frequently encode columns set in a ImU64.
#define IMGUI_TABLE_MAX_DRAW_CHANNELS (2 + 64 * 2) // See TableUpdateDrawChannels()
// [Internal] sizeof() ~ 100
// [Internal] sizeof() ~ 104
// We use the terminology "Visible" to refer to a column that is not Hidden by user or settings. However it may still be out of view and clipped (see IsClipped).
struct ImGuiTableColumn
{
@ -1943,6 +1943,14 @@ struct ImGuiTableColumn
}
};
// Transient cell data stored per row.
// sizeof() ~ 6
struct ImGuiTableCellData
{
ImU32 BgColor; // Actual color
ImS8 Column; // Column number
};
struct ImGuiTable
{
ImGuiID ID;
@ -1950,6 +1958,7 @@ struct ImGuiTable
ImVector<char> RawData;
ImSpan<ImGuiTableColumn> Columns; // Point within RawData[]
ImSpan<ImS8> DisplayOrderToIndex; // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1)
ImSpan<ImGuiTableCellData> RowCellData; // Point within RawData[]. Store cells background requests for current row.
ImU64 VisibleMaskByIndex; // Column Index -> IsVisible map (== not hidden by user/api) in a format adequate for iterating column without touching cold data
ImU64 VisibleMaskByDisplayOrder; // Column DisplayOrder -> IsVisible map
ImU64 VisibleUnclippedMaskByIndex;// Visible and not Clipped, aka "actually visible" "not hidden by some scrolling"
@ -1970,7 +1979,7 @@ struct ImGuiTable
ImGuiTableRowFlags RowFlags : 16; // Current row flags, see ImGuiTableRowFlags_
ImGuiTableRowFlags LastRowFlags : 16;
int RowBgColorCounter; // Counter for alternating background colors (can be fast-forwarded by e.g clipper)
ImU32 RowBgColor; // Request for current row background color
ImU32 RowBgColor[2]; // Background color override for current row.
ImU32 BorderColorStrong;
ImU32 BorderColorLight;
float BorderX1;
@ -2018,6 +2027,7 @@ struct ImGuiTable
ImS8 FreezeRowsCount; // Actual frozen row count (== FreezeRowsRequest, or == 0 when no scrolling offset)
ImS8 FreezeColumnsRequest; // Requested frozen columns count
ImS8 FreezeColumnsCount; // Actual frozen columns count (== FreezeColumnsRequest, or == 0 when no scrolling offset)
ImS8 RowCellDataCount; // Number of RowCellData[] entries in current row
bool IsLayoutLocked; // Set by TableUpdateLayout() which is called when beginning the first row.
bool IsInsideRow; // Set when inside TableBeginRow()/TableEndRow().
bool IsInitializing;

View File

@ -307,13 +307,15 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
if (table->RawData.Size == 0)
{
// Allocate single buffer for our arrays
ImSpanAllocator<2> span_allocator;
ImSpanAllocator<3> span_allocator;
span_allocator.ReserveBytes(0, columns_count * sizeof(ImGuiTableColumn));
span_allocator.ReserveBytes(1, columns_count * sizeof(ImS8));
span_allocator.ReserveBytes(2, columns_count * sizeof(ImGuiTableCellData));
table->RawData.resize(span_allocator.GetArenaSizeInBytes());
span_allocator.SetArenaBasePtr(table->RawData.Data);
span_allocator.GetSpan(0, &table->Columns);
span_allocator.GetSpan(1, &table->DisplayOrderToIndex);
span_allocator.GetSpan(2, &table->RowCellData);
for (int n = 0; n < columns_count; n++)
{
@ -1609,7 +1611,8 @@ void ImGui::TableBeginRow(ImGuiTable* table)
// New row
table->CurrentRow++;
table->CurrentColumn = -1;
table->RowBgColor = IM_COL32_DISABLE;
table->RowBgColor[0] = table->RowBgColor[1] = IM_COL32_DISABLE;
table->RowCellDataCount = 0;
table->IsInsideRow = true;
// Begin frozen rows
@ -1626,7 +1629,7 @@ void ImGui::TableBeginRow(ImGuiTable* table)
// Making the header BG color non-transparent will allow us to overlay it multiple times when handling smooth dragging.
if (table->RowFlags & ImGuiTableRowFlags_Headers)
{
table->RowBgColor = GetColorU32(ImGuiCol_TableHeaderBg);
TableSetBgColor(ImGuiTableBgTarget_RowBg0, GetColorU32(ImGuiCol_TableHeaderBg));
if (table->CurrentRow == 0)
table->IsUsingHeaders = true;
}
@ -1655,14 +1658,18 @@ void ImGui::TableEndRow(ImGuiTable* table)
if (table->CurrentRow == 0)
table->LastFirstRowHeight = bg_y2 - bg_y1;
if (table->CurrentRow >= 0 && bg_y2 >= table->InnerClipRect.Min.y && bg_y1 <= table->InnerClipRect.Max.y)
const bool is_visible = table->CurrentRow >= 0 && bg_y2 >= table->InnerClipRect.Min.y && bg_y1 <= table->InnerClipRect.Max.y;
if (is_visible)
{
// Decide of background color for the row
ImU32 bg_col = 0;
if (table->RowBgColor != IM_COL32_DISABLE)
bg_col = table->RowBgColor;
ImU32 bg_col0 = 0;
ImU32 bg_col1 = 0;
if (table->RowBgColor[0] != IM_COL32_DISABLE)
bg_col0 = table->RowBgColor[0];
else if (table->Flags & ImGuiTableFlags_RowBg)
bg_col = GetColorU32((table->RowBgColorCounter & 1) ? ImGuiCol_TableRowBgAlt : ImGuiCol_TableRowBg);
bg_col0 = GetColorU32((table->RowBgColorCounter & 1) ? ImGuiCol_TableRowBgAlt : ImGuiCol_TableRowBg);
if (table->RowBgColor[1] != IM_COL32_DISABLE)
bg_col1 = table->RowBgColor[1];
// Decide of top border color
ImU32 border_col = 0;
@ -1684,8 +1691,9 @@ void ImGui::TableEndRow(ImGuiTable* table)
}
}
const bool draw_stong_bottom_border = unfreeze_rows;// || (table->RowFlags & ImGuiTableRowFlags_Headers);
if (bg_col != 0 || border_col != 0 || draw_stong_bottom_border)
const bool draw_cell_bg_color = table->RowCellDataCount > 0;
const bool draw_strong_bottom_border = unfreeze_rows;// || (table->RowFlags & ImGuiTableRowFlags_Headers);
if ((bg_col0 | bg_col1 | border_col) != 0 || draw_strong_bottom_border || draw_cell_bg_color)
{
// In theory we could call SetWindowClipRectBeforeChannelChange() but since we know TableEndRow() is
// always followed by a change of clipping rectangle we perform the smallest overwrite possible here.
@ -1693,14 +1701,29 @@ void ImGui::TableEndRow(ImGuiTable* table)
table->DrawSplitter.SetCurrentChannel(window->DrawList, 0);
}
// Draw background
// Draw row background
// We soft/cpu clip this so all backgrounds and borders can share the same clipping rectangle
if (bg_col)
if (bg_col0 || bg_col1)
{
ImRect bg_rect(table->WorkRect.Min.x, bg_y1, table->WorkRect.Max.x, bg_y2);
bg_rect.ClipWith(table->BackgroundClipRect);
if (bg_rect.Min.y < bg_rect.Max.y)
window->DrawList->AddRectFilledMultiColor(bg_rect.Min, bg_rect.Max, bg_col, bg_col, bg_col, bg_col);
ImRect row_rect(table->WorkRect.Min.x, bg_y1, table->WorkRect.Max.x, bg_y2);
row_rect.ClipWith(table->BackgroundClipRect);
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)
window->DrawList->AddRectFilled(row_rect.Min, row_rect.Max, bg_col1);
}
// Draw cell background color
if (draw_cell_bg_color)
{
ImGuiTableCellData* cell_data_end = &table->RowCellData[table->RowCellDataCount - 1];
for (ImGuiTableCellData* cell_data = &table->RowCellData[0]; cell_data <= cell_data_end; cell_data++)
{
ImGuiTableColumn* column = &table->Columns[cell_data->Column];
ImRect cell_rect(column->MinX - table->CellSpacingX, bg_y1, column->MaxX, bg_y2); // FIXME-TABLE: Padding currently wrong until we finish the padding refactor
cell_rect.ClipWith(table->BackgroundClipRect);
window->DrawList->AddRectFilled(cell_rect.Min, cell_rect.Max, cell_data->BgColor);
}
}
// Draw top border
@ -1708,7 +1731,7 @@ void ImGui::TableEndRow(ImGuiTable* table)
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_stong_bottom_border)
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);
}
@ -2305,6 +2328,46 @@ int ImGui::TableGetHoveredColumn()
return (int)table->HoveredColumnBody;
}
void ImGui::TableSetBgColor(ImGuiTableBgTarget bg_target, ImU32 color, int column_n)
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
IM_ASSERT(bg_target != ImGuiTableBgTarget_None);
if (color == IM_COL32_DISABLE)
color = 0;
// We cannot draw neither the cell or row background immediately as we don't know the row height at this point in time.
switch (bg_target)
{
case ImGuiTableBgTarget_CellBg:
{
if (table->RowPosY1 > table->InnerClipRect.Max.y) // Discard
return;
if (column_n == -1)
column_n = table->CurrentColumn;
if ((table->VisibleUnclippedMaskByIndex & ((ImU64)1 << column_n)) == 0)
return;
ImGuiTableCellData* cell_data = &table->RowCellData[table->RowCellDataCount++];
cell_data->BgColor = color;
cell_data->Column = (ImS8)column_n;
break;
}
case ImGuiTableBgTarget_RowBg0:
case ImGuiTableBgTarget_RowBg1:
{
if (table->RowPosY1 > table->InnerClipRect.Max.y) // Discard
return;
IM_ASSERT(column_n == -1);
int bg_idx = (bg_target == ImGuiTableBgTarget_RowBg1) ? 1 : 0;
table->RowBgColor[bg_idx] = color;
break;
}
default:
IM_ASSERT(0);
}
}
void ImGui::TableSortSpecsSanitize(ImGuiTable* table)
{
IM_ASSERT(table->Flags & ImGuiTableFlags_Sortable);