mirror of
https://github.com/ocornut/imgui.git
synced 2024-11-23 21:09:01 +08:00
Tables: Added TableGetHoveredColumn(), extracted some context menu code out, simplifying TableAutoHeaders() toward aim of it being a user-land function.
This commit is contained in:
parent
4c4882ffe4
commit
798aed729a
1
imgui.h
1
imgui.h
@ -683,6 +683,7 @@ namespace ImGui
|
||||
IMGUI_API const char* TableGetColumnName(int column_n = -1); // return NULL if column didn't have a name declared by TableSetupColumn(). Pass -1 to use current column.
|
||||
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.
|
||||
// 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
|
||||
|
@ -2002,7 +2002,7 @@ struct ImGuiTable
|
||||
ImGuiTableSortSpecs SortSpecs; // Public facing sorts specs, this is what we return in TableGetSortSpecs()
|
||||
ImS8 SortSpecsCount;
|
||||
ImS8 DeclColumnsCount; // Count calls to TableSetupColumn()
|
||||
ImS8 HoveredColumnBody; // [DEBUG] Unlike HoveredColumnBorder this doesn't fulfill all Hovering rules properly. Used for debugging/tools for now.
|
||||
ImS8 HoveredColumnBody; // Index of column whose visible region is being hovered. Important: == ColumnsCount when hovering empty region after the right-most column!
|
||||
ImS8 HoveredColumnBorder; // Index of column whose right-border is being hovered (for resizing).
|
||||
ImS8 ResizedColumn; // Index of column being resized. Reset when InstanceCurrent==0.
|
||||
ImS8 LastResizedColumn; // Index of column being resized from previous frame.
|
||||
@ -2041,6 +2041,7 @@ struct ImGuiTable
|
||||
ContextPopupColumn = -1;
|
||||
ReorderColumn = -1;
|
||||
ResizedColumn = -1;
|
||||
HoveredColumnBody = HoveredColumnBorder = -1;
|
||||
}
|
||||
};
|
||||
|
||||
@ -2263,7 +2264,8 @@ namespace ImGui
|
||||
IMGUI_API void TableSetColumnWidth(int column_n, float width);
|
||||
IMGUI_API void TableSetColumnWidth(ImGuiTable* table, ImGuiTableColumn* column, float width);
|
||||
IMGUI_API void TableDrawBorders(ImGuiTable* table);
|
||||
IMGUI_API void TableDrawContextMenu(ImGuiTable* table, int column_n);
|
||||
IMGUI_API void TableDrawContextMenu(ImGuiTable* table);
|
||||
IMGUI_API void TableOpenContextMenu(ImGuiTable* table, int column_n);
|
||||
IMGUI_API void TableReorderDrawChannelsForMerge(ImGuiTable* table);
|
||||
IMGUI_API void TableSortSpecsClickColumn(ImGuiTable* table, ImGuiTableColumn* column, bool add_to_existing_sort_orders);
|
||||
IMGUI_API void TableSortSpecsSanitize(ImGuiTable* table);
|
||||
@ -2278,13 +2280,15 @@ namespace ImGui
|
||||
IMGUI_API void TableSetColumnAutofit(ImGuiTable* table, int column_n);
|
||||
IMGUI_API void PushTableBackground();
|
||||
IMGUI_API void PopTableBackground();
|
||||
|
||||
// Tables - Settings
|
||||
IMGUI_API void TableLoadSettings(ImGuiTable* table);
|
||||
IMGUI_API void TableSaveSettings(ImGuiTable* table);
|
||||
IMGUI_API ImGuiTableSettings* TableGetBoundSettings(ImGuiTable* table);
|
||||
IMGUI_API void TableSettingsInstallHandler(ImGuiContext* context);
|
||||
IMGUI_API ImGuiTableSettings* TableSettingsCreate(ImGuiID id, int columns_count);
|
||||
IMGUI_API ImGuiTableSettings* TableSettingsFindByID(ImGuiID id);
|
||||
IMGUI_API void TableSettingsClearByID(ImGuiID id);
|
||||
IMGUI_API void TableLoadSettings(ImGuiTable* table);
|
||||
IMGUI_API void TableSaveSettings(ImGuiTable* table);
|
||||
IMGUI_API ImGuiTableSettings* TableGetBoundSettings(ImGuiTable* table);
|
||||
|
||||
// Tab Bars
|
||||
IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags);
|
||||
|
137
imgui_tables.cpp
137
imgui_tables.cpp
@ -284,8 +284,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
table->FreezeColumnsCount = (inner_window->Scroll.x != 0.0f) ? table->FreezeColumnsRequest : 0;
|
||||
table->IsFreezeRowsPassed = (table->FreezeRowsCount == 0);
|
||||
table->DeclColumnsCount = 0;
|
||||
table->HoveredColumnBody = -1;
|
||||
table->HoveredColumnBorder = -1;
|
||||
table->RightMostVisibleColumn = -1;
|
||||
|
||||
// Using opaque colors facilitate overlapping elements of the grid
|
||||
@ -571,8 +569,12 @@ static float TableGetMinColumnWidth()
|
||||
// for WidthAlwaysAutoResize columns?
|
||||
void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
IM_ASSERT(table->IsLayoutLocked == false);
|
||||
|
||||
table->HoveredColumnBody = -1;
|
||||
table->HoveredColumnBorder = -1;
|
||||
|
||||
// Compute offset, clip rect for the frame
|
||||
// (can't make auto padding larger than what WorkRect knows about so right-alignment matches)
|
||||
const ImRect work_rect = table->WorkRect;
|
||||
@ -741,6 +743,10 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
width_remaining_for_stretched_columns -= 1.0f;
|
||||
}
|
||||
|
||||
// Detect hovered column
|
||||
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->LastOuterHeight));
|
||||
const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0);
|
||||
|
||||
// Setup final position, offset and clipping rectangles
|
||||
int visible_n = 0;
|
||||
float offset_x = (table->FreezeColumnsCount > 0) ? table->OuterRect.Min.x : work_rect.Min.x;
|
||||
@ -803,6 +809,10 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
|
||||
column->SkipItems = !column->IsVisible || table->HostSkipItems;
|
||||
|
||||
// Detect hovered column
|
||||
if (is_hovering_table && g.IO.MousePos.x >= column->ClipRect.Min.x && g.IO.MousePos.x < column->ClipRect.Max.x)
|
||||
table->HoveredColumnBody = (ImS8)column_n;
|
||||
|
||||
// Starting cursor position
|
||||
column->StartXRows = column->StartXHeaders = column->MinX + table->CellPaddingX1;
|
||||
|
||||
@ -834,6 +844,16 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
visible_n++;
|
||||
}
|
||||
|
||||
// Detect/store when we are hovering the unused space after the right-most column (so e.g. context menus can react on it)
|
||||
if (is_hovering_table && table->HoveredColumnBody == -1)
|
||||
{
|
||||
float unused_x1 = table->WorkRect.Min.x;
|
||||
if (table->RightMostVisibleColumn != -1)
|
||||
unused_x1 = ImMax(unused_x1, table->Columns[table->RightMostVisibleColumn].ClipRect.Max.x);
|
||||
if (g.IO.MousePos.x >= unused_x1)
|
||||
table->HoveredColumnBody = (ImS8)table->ColumnsCount;
|
||||
}
|
||||
|
||||
// Clear Resizable flag if none of our column are actually resizable (either via an explicit _NoResize flag,
|
||||
// either because of using _WidthAlwaysAutoResize/_WidthStretch).
|
||||
// This will hide the resizing option from the context menu.
|
||||
@ -857,7 +877,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
{
|
||||
if (BeginPopup("##TableContextMenu"))
|
||||
{
|
||||
TableDrawContextMenu(table, table->ContextPopupColumn);
|
||||
TableDrawContextMenu(table);
|
||||
EndPopup();
|
||||
}
|
||||
else
|
||||
@ -897,7 +917,6 @@ void ImGui::TableUpdateBorders(ImGuiTable* table)
|
||||
const float hit_y1 = table->OuterRect.Min.y;
|
||||
const float hit_y2_full = ImMax(table->OuterRect.Max.y, hit_y1 + table->LastOuterHeight);
|
||||
const float hit_y2 = borders_full_height ? hit_y2_full : (hit_y1 + table->LastFirstRowHeight);
|
||||
const float mouse_x_hover_body = (g.IO.MousePos.y >= hit_y1 && g.IO.MousePos.y < hit_y2_full) ? g.IO.MousePos.x : FLT_MAX;
|
||||
|
||||
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
|
||||
{
|
||||
@ -906,13 +925,6 @@ void ImGui::TableUpdateBorders(ImGuiTable* table)
|
||||
|
||||
const int column_n = table->DisplayOrderToIndex[order_n];
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
|
||||
// Detect hovered column:
|
||||
// - we perform an unusually low-level check here.. not using IsMouseHoveringRect() to avoid touch padding.
|
||||
// - we don't care about the full set of IsItemHovered() feature either.
|
||||
if (mouse_x_hover_body >= column->MinX && mouse_x_hover_body < column->MaxX)
|
||||
table->HoveredColumnBody = (ImS8)column_n;
|
||||
|
||||
if (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_))
|
||||
continue;
|
||||
|
||||
@ -1798,9 +1810,12 @@ bool ImGui::TableNextCell()
|
||||
{
|
||||
TableNextRow();
|
||||
}
|
||||
int column_n = table->CurrentColumn;
|
||||
|
||||
// FIXME-TABLE: Need to clarify if we want to allow IsItemHovered() here
|
||||
//g.CurrentWindow->DC.LastItemStatusFlags = (column_n == table->HoveredColumn) ? ImGuiItemStatusFlags_HoveredRect : ImGuiItemStatusFlags_None;
|
||||
|
||||
// FIXME-TABLE: it is likely to alter layout if user skips a columns contents based on clipping.
|
||||
int column_n = table->CurrentColumn;
|
||||
return (table->VisibleUnclippedMaskByIndex & ((ImU64)1 << column_n)) != 0;
|
||||
}
|
||||
|
||||
@ -1851,6 +1866,9 @@ bool ImGui::TableSetColumnIndex(int column_idx)
|
||||
TableBeginCell(table, column_idx);
|
||||
}
|
||||
|
||||
// FIXME-TABLE: Need to clarify if we want to allow IsItemHovered() here
|
||||
//g.CurrentWindow->DC.LastItemStatusFlags = (column_n == table->HoveredColumn) ? ImGuiItemStatusFlags_HoveredRect : ImGuiItemStatusFlags_None;
|
||||
|
||||
// FIXME-TABLE: it is likely to alter layout if user skips a columns contents based on clipping.
|
||||
return (table->VisibleUnclippedMaskByIndex & ((ImU64)1 << column_idx)) != 0;
|
||||
}
|
||||
@ -1917,7 +1935,7 @@ void ImGui::PopTableBackground()
|
||||
|
||||
// Output context menu into current window (generally a popup)
|
||||
// FIXME-TABLE: Ideally this should be writable by the user. Full programmatic access to that data?
|
||||
void ImGui::TableDrawContextMenu(ImGuiTable* table, int selected_column_n)
|
||||
void ImGui::TableDrawContextMenu(ImGuiTable* table)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
@ -1925,7 +1943,7 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table, int selected_column_n)
|
||||
return;
|
||||
|
||||
bool want_separator = false;
|
||||
selected_column_n = ImClamp(selected_column_n, -1, table->ColumnsCount - 1);
|
||||
const int selected_column_n = (table->ContextPopupColumn >= 0 && table->ContextPopupColumn < table->ColumnsCount) ? table->ContextPopupColumn : -1;
|
||||
|
||||
// Sizing
|
||||
if (table->Flags & ImGuiTableFlags_Resizable)
|
||||
@ -1983,28 +2001,44 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table, int selected_column_n)
|
||||
}
|
||||
}
|
||||
|
||||
// Use -1 to open menu not specific to a given column.
|
||||
void ImGui::TableOpenContextMenu(ImGuiTable* table, int column_n)
|
||||
{
|
||||
IM_ASSERT(column_n >= -1 && column_n < table->ColumnsCount);
|
||||
if (table->Flags & (ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
|
||||
{
|
||||
table->IsContextPopupOpen = true;
|
||||
table->ContextPopupColumn = (ImS8)column_n;
|
||||
table->InstanceInteracted = table->InstanceCurrent;
|
||||
OpenPopup("##TableContextMenu");
|
||||
}
|
||||
}
|
||||
|
||||
// This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn().
|
||||
// The intent is that advanced users willing to create customized headers would not need to use this helper and may
|
||||
// create their own. However presently this function uses too many internal structures/calls.
|
||||
// The intent is that advanced users willing to create customized headers would not need to use this helper
|
||||
// and can create their own! For example: TableHeader() may be preceeded by Checkbox() or other custom widgets.
|
||||
// FIXME-TABLE: However presently this function uses too many internal structures/calls.
|
||||
void ImGui::TableAutoHeaders()
|
||||
{
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
IM_ASSERT(table != NULL && "Need to call TableAutoHeaders() after BeginTable()!");
|
||||
const int columns_count = table->ColumnsCount;
|
||||
|
||||
// Calculate row height (for the unlikely case that labels may be are multi-line)
|
||||
float row_y1 = GetCursorScreenPos().y;
|
||||
float row_height = GetTextLineHeight();
|
||||
for (int column_n = 0; column_n < columns_count; column_n++)
|
||||
if (TableGetColumnIsVisible(column_n))
|
||||
row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(column_n)).y);
|
||||
row_height += g.Style.CellPadding.y * 2.0f;
|
||||
row_height += style.CellPadding.y * 2.0f;
|
||||
|
||||
// Open row
|
||||
TableNextRow(ImGuiTableRowFlags_Headers, row_height);
|
||||
if (table->HostSkipItems) // Merely an optimization
|
||||
if (table->HostSkipItems) // Merely an optimization, you may skip in your own code.
|
||||
return;
|
||||
|
||||
// This for loop is constructed to not make use of internal functions,
|
||||
@ -2027,64 +2061,35 @@ void ImGui::TableAutoHeaders()
|
||||
Checkbox("##", &b[column_n]);
|
||||
PopStyleVar();
|
||||
PopID();
|
||||
SameLine(0.0f, g.Style.ItemInnerSpacing.x);
|
||||
SameLine(0.0f, style.ItemInnerSpacing.x);
|
||||
}
|
||||
#endif
|
||||
|
||||
// [DEBUG]
|
||||
//if (g.IO.KeyCtrl) { static char buf[32]; name = buf; ImGuiTableColumn* c = &table->Columns[column_n]; if (c->Flags & ImGuiTableColumnFlags_WidthStretch) ImFormatString(buf, 32, "%.3f>%.1f", c->ResizeWeight, c->WidthGiven); else ImFormatString(buf, 32, "%.1f", c->WidthGiven); }
|
||||
|
||||
// Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them)
|
||||
// - in your own code you may omit the PushID/PopID all-together, provided you know you know they won't collide
|
||||
// - table->InstanceCurrent is only >0 when we use multiple BeginTable/EndTable calls with same identifier.
|
||||
PushID(table->InstanceCurrent * table->ColumnsCount + column_n);
|
||||
TableHeader(name);
|
||||
PopID();
|
||||
|
||||
// We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden
|
||||
if (IsMouseReleased(1) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
|
||||
if (IsMouseReleased(1) && IsItemHovered())
|
||||
open_context_popup = column_n;
|
||||
}
|
||||
|
||||
// FIXME-TABLE: This is not user-land code any more + need to explain WHY this is here!
|
||||
// FIXME-TABLE: This is not user-land code any more + need to explain WHY this is here! (added in fa88f023)
|
||||
window->SkipItems = table->HostSkipItems;
|
||||
|
||||
// Allow opening popup from the right-most section after the last column
|
||||
// FIXME-TABLE: This is not user-land code any more... perhaps instead we should expose hovered column.
|
||||
// and allow some sort of row-centric IsItemHovered() for full flexibility?
|
||||
float unused_x1 = table->WorkRect.Min.x;
|
||||
if (table->RightMostVisibleColumn != -1)
|
||||
unused_x1 = ImMax(unused_x1, table->Columns[table->RightMostVisibleColumn].MaxX);
|
||||
if (unused_x1 < table->WorkRect.Max.x)
|
||||
{
|
||||
// FIXME: We inherit ClipRect/SkipItem from last submitted column (active or not), let's temporarily override it.
|
||||
// Because we don't perform any rendering here we just overwrite window->ClipRect used by logic.
|
||||
window->ClipRect = table->InnerClipRect;
|
||||
|
||||
ImVec2 backup_cursor_max_pos = window->DC.CursorMaxPos;
|
||||
window->DC.CursorPos = ImVec2(unused_x1, table->RowPosY1);
|
||||
ImVec2 size = ImVec2(table->WorkRect.Max.x - window->DC.CursorPos.x, table->RowPosY2 - table->RowPosY1);
|
||||
if (size.x > 0.0f && size.y > 0.0f)
|
||||
{
|
||||
InvisibleButton("##RemainingSpace", size);
|
||||
window->DC.CursorPos.y -= g.Style.ItemSpacing.y;
|
||||
window->DC.CursorMaxPos = backup_cursor_max_pos; // Don't feed back into the width of the Header row
|
||||
|
||||
// We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden.
|
||||
if (IsMouseReleased(1) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
|
||||
open_context_popup = -1;
|
||||
}
|
||||
|
||||
window->ClipRect = window->DrawList->_ClipRectStack.back();
|
||||
}
|
||||
// (We don't actually need to ImGuiHoveredFlags_AllowWhenBlockedByPopup because in reality this is generally
|
||||
// not required anymore.. because popup opening code tends to be reacting on IsMouseReleased() and the click
|
||||
// would already have closed any other popups!)
|
||||
if (IsMouseReleased(1) && TableGetHoveredColumn() == columns_count && g.IO.MousePos.y >= row_y1 && g.IO.MousePos.y < row_y1 + row_height)
|
||||
open_context_popup = -1; // Will open a non-column-specific popup.
|
||||
|
||||
// Open Context Menu
|
||||
if (open_context_popup != INT_MAX)
|
||||
if (table->Flags & (ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
|
||||
{
|
||||
table->IsContextPopupOpen = true;
|
||||
table->ContextPopupColumn = (ImS8)open_context_popup;
|
||||
table->InstanceInteracted = table->InstanceCurrent;
|
||||
OpenPopup("##TableContextMenu");
|
||||
}
|
||||
TableOpenContextMenu(table, open_context_popup);
|
||||
}
|
||||
|
||||
// Emit a column header (text + optional sort order)
|
||||
@ -2281,6 +2286,15 @@ bool ImGui::TableGetColumnIsSorted(int column_n)
|
||||
return (column->SortOrder != -1);
|
||||
}
|
||||
|
||||
int ImGui::TableGetHoveredColumn()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
if (!table)
|
||||
return -1;
|
||||
return (int)table->HoveredColumnBody;
|
||||
}
|
||||
|
||||
void ImGui::TableSortSpecsSanitize(ImGuiTable* table)
|
||||
{
|
||||
IM_ASSERT(table->Flags & ImGuiTableFlags_Sortable);
|
||||
@ -2679,7 +2693,10 @@ void ImGui::DebugNodeTable(ImGuiTable* table)
|
||||
GetForegroundDrawList()->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255));
|
||||
if (!open)
|
||||
return;
|
||||
BulletText("OuterWidth: %.1f, InnerWidth: %.1f%s, ColumnsWidth: %.1f, AutoFitWidth: %.1f", table->OuterRect.GetWidth(), table->InnerWidth, table->InnerWidth == 0.0f ? " (auto)" : "", table->ColumnsTotalWidth, table->ColumnsAutoFitWidth);
|
||||
BulletText("OuterWidth: %.1f, InnerWidth: %.1f%s", table->OuterRect.GetWidth(), table->InnerWidth, table->InnerWidth == 0.0f ? " (auto)" : "");
|
||||
BulletText("ColumnsWidth: %.1f, AutoFitWidth: %.1f", table->ColumnsTotalWidth, table->ColumnsAutoFitWidth);
|
||||
BulletText("HoveredColumnBody: %d, HoveredColumnBorder: %d", table->HoveredColumnBody, table->HoveredColumnBorder);
|
||||
BulletText("ResizedColumn: %d, ReorderColumn: %d, HeldHeaderColumn: %d", table->ResizedColumn, table->ReorderColumn, table->HeldHeaderColumn);
|
||||
for (int n = 0; n < table->ColumnsCount; n++)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[n];
|
||||
|
Loading…
Reference in New Issue
Block a user