Tables: added ImGuiTableFlags_HighlightHoveredColumn.

This commit is contained in:
ocornut 2023-10-12 14:43:34 +02:00
parent be1311cfc1
commit 9f851ebfe4
5 changed files with 52 additions and 24 deletions

View File

@ -94,6 +94,7 @@ Other changes:
- Drag and Drop: Fixed submitting a tooltip from drop target location when using AcceptDragDropPayload() - Drag and Drop: Fixed submitting a tooltip from drop target location when using AcceptDragDropPayload()
with ImGuiDragDropFlags_AcceptNoPreviewTooltip and submitting a tooltip manually. with ImGuiDragDropFlags_AcceptNoPreviewTooltip and submitting a tooltip manually.
- TreeNode: Added ImGuiTreeNodeFlags_SpanAllColumns for use in tables. (#3151, #3565, #2451, #2438) - TreeNode: Added ImGuiTreeNodeFlags_SpanAllColumns for use in tables. (#3151, #3565, #2451, #2438)
- Tables: Added ImGuiTableFlags_HighlightHoveredColumn flag, currently highlighting column header.
- Tables: Fixed an edge-case when no columns are visible + table scrollbar is visible + user - Tables: Fixed an edge-case when no columns are visible + table scrollbar is visible + user
code is always testing return value of TableSetColumnIndex() to coarse clip. With an active code is always testing return value of TableSetColumnIndex() to coarse clip. With an active
clipper it would have asserted. Without a clipper, the scrollbar range would be wrong. clipper it would have asserted. Without a clipper, the scrollbar range would be wrong.

View File

@ -1216,6 +1216,8 @@ enum ImGuiTableFlags_
// Sorting // Sorting
ImGuiTableFlags_SortMulti = 1 << 26, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). ImGuiTableFlags_SortMulti = 1 << 26, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).
ImGuiTableFlags_SortTristate = 1 << 27, // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). ImGuiTableFlags_SortTristate = 1 << 27, // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).
// Miscellaneous
ImGuiTableFlags_HighlightHoveredColumn = 1 << 28, // Highlight column headers when hovered (may evolve into a fuller highlight)
// [Internal] Combinations and masks // [Internal] Combinations and masks
ImGuiTableFlags_SizingMask_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame, ImGuiTableFlags_SizingMask_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame,

View File

@ -3973,10 +3973,10 @@ static void ShowDemoWindowTables()
ImGui::PushID("Tables"); ImGui::PushID("Tables");
int open_action = -1; int open_action = -1;
if (ImGui::Button("Open all")) if (ImGui::Button("Expand all"))
open_action = 1; open_action = 1;
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Close all")) if (ImGui::Button("Collapse all"))
open_action = 0; open_action = 0;
ImGui::SameLine(); ImGui::SameLine();
@ -4262,6 +4262,7 @@ static void ShowDemoWindowTables()
ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", &flags, ImGuiTableFlags_Hideable); ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", &flags, ImGuiTableFlags_Hideable);
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody);
ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)"); ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)");
ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn);
PopStyleCompact(); PopStyleCompact();
if (ImGui::BeginTable("table1", 3, flags)) if (ImGui::BeginTable("table1", 3, flags))
@ -5299,6 +5300,7 @@ static void ShowDemoWindowTables()
static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings; static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings;
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
ImGui::CheckboxFlags("ImGuiTableFlags_SizingFixedFit", &flags, ImGuiTableFlags_SizingFixedFit); ImGui::CheckboxFlags("ImGuiTableFlags_SizingFixedFit", &flags, ImGuiTableFlags_SizingFixedFit);
ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn);
for (int n = 0; n < 3; n++) for (int n = 0; n < 3; n++)
{ {
char buf[32]; char buf[32];
@ -5519,9 +5521,15 @@ static void ShowDemoWindowTables()
ImGui::TreePop(); ImGui::TreePop();
} }
if (ImGui::TreeNodeEx("Other:", ImGuiTreeNodeFlags_DefaultOpen)) if (ImGui::TreeNodeEx("Headers:", ImGuiTreeNodeFlags_DefaultOpen))
{ {
ImGui::Checkbox("show_headers", &show_headers); ImGui::Checkbox("show_headers", &show_headers);
ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn);
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Other:", ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::Checkbox("show_wrapped_text", &show_wrapped_text); ImGui::Checkbox("show_wrapped_text", &show_wrapped_text);
ImGui::DragFloat2("##OuterSize", &outer_size_value.x); ImGui::DragFloat2("##OuterSize", &outer_size_value.x);

View File

@ -2669,6 +2669,7 @@ struct ImGuiTableCellData
}; };
// Per-instance data that needs preserving across frames (seemingly most others do not need to be preserved aside from debug needs. Does that means they could be moved to ImGuiTableTempData?) // Per-instance data that needs preserving across frames (seemingly most others do not need to be preserved aside from debug needs. Does that means they could be moved to ImGuiTableTempData?)
// sizeof() ~ 24 bytes
struct ImGuiTableInstanceData struct ImGuiTableInstanceData
{ {
ImGuiID TableInstanceID; ImGuiID TableInstanceID;
@ -2754,6 +2755,7 @@ struct IMGUI_API ImGuiTable
ImGuiTableColumnIdx DeclColumnsCount; // Count calls to TableSetupColumn() ImGuiTableColumnIdx DeclColumnsCount; // Count calls to TableSetupColumn()
ImGuiTableColumnIdx HoveredColumnBody; // Index of column whose visible region is being hovered. Important: == ColumnsCount when hovering empty region after the right-most column! ImGuiTableColumnIdx HoveredColumnBody; // Index of column whose visible region is being hovered. Important: == ColumnsCount when hovering empty region after the right-most column!
ImGuiTableColumnIdx HoveredColumnBorder; // Index of column whose right-border is being hovered (for resizing). ImGuiTableColumnIdx HoveredColumnBorder; // Index of column whose right-border is being hovered (for resizing).
ImGuiTableColumnIdx HighlightColumnHeader; // Index of column which should be highlighted.
ImGuiTableColumnIdx AutoFitSingleColumn; // Index of single column requesting auto-fit. ImGuiTableColumnIdx AutoFitSingleColumn; // Index of single column requesting auto-fit.
ImGuiTableColumnIdx ResizedColumn; // Index of column being resized. Reset when InstanceCurrent==0. ImGuiTableColumnIdx ResizedColumn; // Index of column being resized. Reset when InstanceCurrent==0.
ImGuiTableColumnIdx LastResizedColumn; // Index of column being resized from previous frame. ImGuiTableColumnIdx LastResizedColumn; // Index of column being resized from previous frame.
@ -2786,6 +2788,8 @@ struct IMGUI_API ImGuiTable
bool IsResetDisplayOrderRequest; bool IsResetDisplayOrderRequest;
bool IsUnfrozenRows; // Set when we got past the frozen row. bool IsUnfrozenRows; // Set when we got past the frozen row.
bool IsDefaultSizingPolicy; // Set if user didn't explicitly set a sizing policy in BeginTable() bool IsDefaultSizingPolicy; // Set if user didn't explicitly set a sizing policy in BeginTable()
bool IsActiveIdAliveBeforeTable;
bool IsActiveIdInTable;
bool HasScrollbarYCurr; // Whether ANY instance of this table had a vertical scrollbar during the current frame. bool HasScrollbarYCurr; // Whether ANY instance of this table had a vertical scrollbar during the current frame.
bool HasScrollbarYPrev; // Whether ANY instance of this table had a vertical scrollbar during the previous. bool HasScrollbarYPrev; // Whether ANY instance of this table had a vertical scrollbar during the previous.
bool MemoryCompacted; bool MemoryCompacted;

View File

@ -346,7 +346,8 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
flags = TableFixFlags(flags, outer_window); flags = TableFixFlags(flags, outer_window);
// Initialize // Initialize
const int instance_no = (table->LastFrameActive != g.FrameCount) ? 0 : table->InstanceCurrent + 1; const int previous_frame_active = table->LastFrameActive;
const int instance_no = (previous_frame_active != g.FrameCount) ? 0 : table->InstanceCurrent + 1;
table->ID = id; table->ID = id;
table->Flags = flags; table->Flags = flags;
table->LastFrameActive = g.FrameCount; table->LastFrameActive = g.FrameCount;
@ -478,6 +479,8 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
table->FreezeColumnsRequest = table->FreezeColumnsCount = 0; table->FreezeColumnsRequest = table->FreezeColumnsCount = 0;
table->IsUnfrozenRows = true; table->IsUnfrozenRows = true;
table->DeclColumnsCount = 0; table->DeclColumnsCount = 0;
if (previous_frame_active + 1 < g.FrameCount)
table->IsActiveIdInTable = false;
// Using opaque colors facilitate overlapping lines of the grid, otherwise we'd need to improve TableDrawBorders() // Using opaque colors facilitate overlapping lines of the grid, otherwise we'd need to improve TableDrawBorders()
table->BorderColorStrong = GetColorU32(ImGuiCol_TableBorderStrong); table->BorderColorStrong = GetColorU32(ImGuiCol_TableBorderStrong);
@ -974,8 +977,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
table_instance->HoveredRowLast = table_instance->HoveredRowNext; table_instance->HoveredRowLast = table_instance->HoveredRowNext;
table_instance->HoveredRowNext = -1; table_instance->HoveredRowNext = -1;
table->HoveredColumnBody = -1; table->HoveredColumnBody = table->HoveredColumnBorder = -1;
table->HoveredColumnBorder = -1;
const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight)); const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight));
const ImGuiID backup_active_id = g.ActiveId; const ImGuiID backup_active_id = g.ActiveId;
g.ActiveId = 0; g.ActiveId = 0;
@ -1122,13 +1124,13 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// because of using _WidthAuto/_WidthStretch). This will hide the resizing option from the context menu. // because of using _WidthAuto/_WidthStretch). This will hide the resizing option from the context menu.
const float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x); const float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x);
if (is_hovering_table && table->HoveredColumnBody == -1) if (is_hovering_table && table->HoveredColumnBody == -1)
{
if (g.IO.MousePos.x >= unused_x1) if (g.IO.MousePos.x >= unused_x1)
table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount; table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount;
}
if (has_resizable == false && (table->Flags & ImGuiTableFlags_Resizable)) if (has_resizable == false && (table->Flags & ImGuiTableFlags_Resizable))
table->Flags &= ~ImGuiTableFlags_Resizable; table->Flags &= ~ImGuiTableFlags_Resizable;
table->IsActiveIdAliveBeforeTable = (g.ActiveIdIsAlive != 0);
// [Part 8] Lock actual OuterRect/WorkRect right-most position. // [Part 8] Lock actual OuterRect/WorkRect right-most position.
// This is done late to handle the case of fixed-columns tables not claiming more widths that they need. // This is done late to handle the case of fixed-columns tables not claiming more widths that they need.
// Because of this we are careful with uses of WorkRect and InnerClipRect before this point. // Because of this we are careful with uses of WorkRect and InnerClipRect before this point.
@ -1161,6 +1163,14 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
table->IsLayoutLocked = true; table->IsLayoutLocked = true;
table->IsUsingHeaders = false; table->IsUsingHeaders = false;
// Highlight header
table->HighlightColumnHeader = -1;
if (table->IsContextPopupOpen && table->ContextPopupColumn != -1 && table->InstanceInteracted == table->InstanceCurrent)
table->HighlightColumnHeader = table->ContextPopupColumn;
else if ((table->Flags & ImGuiTableFlags_HighlightHoveredColumn) && table->HoveredColumnBody != -1 && table->HoveredColumnBody != table->ColumnsCount && table->HoveredColumnBorder == -1)
if (g.ActiveId == 0 || (table->IsActiveIdInTable || g.DragDropActive))
table->HighlightColumnHeader = table->HoveredColumnBody;
// [Part 11] Context menu // [Part 11] Context menu
if (TableBeginContextMenuPopup(table)) if (TableBeginContextMenuPopup(table))
{ {
@ -1382,6 +1392,8 @@ void ImGui::EndTable()
table->ResizedColumnNextWidth = new_width; table->ResizedColumnNextWidth = new_width;
} }
table->IsActiveIdInTable = (g.ActiveIdIsAlive != 0 && table->IsActiveIdAliveBeforeTable == false);
// Pop from id stack // Pop from id stack
IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table_instance->TableInstanceID, "Mismatching PushID/PopID!"); IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table_instance->TableInstanceID, "Mismatching PushID/PopID!");
IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= temp_data->HostBackupItemWidthStackSize, "Too many PopItemWidth!"); IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= temp_data->HostBackupItemWidthStackSize, "Too many PopItemWidth!");
@ -2301,6 +2313,7 @@ void ImGui::TableUpdateColumnsWeightFromWidth(ImGuiTable* table)
// - TablePopBackgroundChannel() [Internal] // - TablePopBackgroundChannel() [Internal]
// - TableSetupDrawChannels() [Internal] // - TableSetupDrawChannels() [Internal]
// - TableMergeDrawChannels() [Internal] // - TableMergeDrawChannels() [Internal]
// - TableGetColumnBorderCol() [Internal]
// - TableDrawBorders() [Internal] // - TableDrawBorders() [Internal]
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
@ -2584,6 +2597,18 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
} }
} }
static ImU32 TableGetColumnBorderCol(ImGuiTable* table, int order_n, int column_n)
{
const bool is_hovered = (table->HoveredColumnBorder == column_n);
const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent);
const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1);
if (is_resized || is_hovered)
return ImGui::GetColorU32(is_resized ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered);
if (is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)))
return table->BorderColorStrong;
return table->BorderColorLight;
}
// FIXME-TABLE: This is a mess, need to redesign how we render borders (as some are also done in TableEndRow) // FIXME-TABLE: This is a mess, need to redesign how we render borders (as some are also done in TableEndRow)
void ImGui::TableDrawBorders(ImGuiTable* table) void ImGui::TableDrawBorders(ImGuiTable* table)
{ {
@ -2626,21 +2651,9 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
// Draw in outer window so right-most column won't be clipped // Draw in outer window so right-most column won't be clipped
// Always draw full height border when being resized/hovered, or on the delimitation of frozen column scrolling. // Always draw full height border when being resized/hovered, or on the delimitation of frozen column scrolling.
ImU32 col; float draw_y2 = (is_hovered || is_resized || is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) == 0) ? draw_y2_body : draw_y2_head;
float draw_y2;
if (is_hovered || is_resized || is_frozen_separator)
{
draw_y2 = draw_y2_body;
col = is_resized ? GetColorU32(ImGuiCol_SeparatorActive) : is_hovered ? GetColorU32(ImGuiCol_SeparatorHovered) : table->BorderColorStrong;
}
else
{
draw_y2 = (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) ? draw_y2_head : draw_y2_body;
col = (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) ? table->BorderColorStrong : table->BorderColorLight;
}
if (draw_y2 > draw_y1) if (draw_y2 > draw_y1)
inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), col, border_size); inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), TableGetColumnBorderCol(table, order_n, column_n), border_size);
} }
} }
@ -2994,7 +3007,6 @@ void ImGui::TableHeader(const char* label)
column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x); column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x);
// Keep header highlighted when context menu is open. // Keep header highlighted when context menu is open.
const bool selected = (table->IsContextPopupOpen && table->ContextPopupColumn == column_n && table->InstanceInteracted == table->InstanceCurrent);
ImGuiID id = window->GetID(label); ImGuiID id = window->GetID(label);
ImRect bb(cell_r.Min.x, cell_r.Min.y, cell_r.Max.x, ImMax(cell_r.Max.y, cell_r.Min.y + label_height + g.Style.CellPadding.y * 2.0f)); ImRect bb(cell_r.Min.x, cell_r.Min.y, cell_r.Max.x, ImMax(cell_r.Max.y, cell_r.Min.y + label_height + g.Style.CellPadding.y * 2.0f));
ItemSize(ImVec2(0.0f, label_height)); // Don't declare unclipped width, it'll be fed ContentMaxPosHeadersIdeal ItemSize(ImVec2(0.0f, label_height)); // Don't declare unclipped width, it'll be fed ContentMaxPosHeadersIdeal
@ -3005,9 +3017,10 @@ void ImGui::TableHeader(const char* label)
//GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG] //GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]
// Using AllowOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items. // Using AllowOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items.
const bool highlight = (table->HighlightColumnHeader == column_n);
bool hovered, held; bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowOverlap); bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowOverlap);
if (held || hovered || selected) if (held || hovered || highlight)
{ {
const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
//RenderFrame(bb.Min, bb.Max, col, false, 0.0f); //RenderFrame(bb.Min, bb.Max, col, false, 0.0f);