|
|
|
@ -2172,213 +2172,6 @@ void ImGui::TablePopBackgroundChannel()
|
|
|
|
|
table->DrawSplitter.SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 can create their own! For example: TableHeader() may be preceeded by Checkbox() or other custom widgets.
|
|
|
|
|
// See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this.
|
|
|
|
|
void ImGui::TableHeadersRow()
|
|
|
|
|
{
|
|
|
|
|
ImGuiStyle& style = ImGui::GetStyle();
|
|
|
|
|
|
|
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
|
|
ImGuiTable* table = g.CurrentTable;
|
|
|
|
|
IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!");
|
|
|
|
|
|
|
|
|
|
// Calculate row height (for the unlikely case that labels may be are multi-line)
|
|
|
|
|
// If we didn't do that, uneven header height would work but their highlight won't cover the full row height.
|
|
|
|
|
float row_height = GetTextLineHeight();
|
|
|
|
|
const float row_y1 = GetCursorScreenPos().y;
|
|
|
|
|
const int columns_count = TableGetColumnCount();
|
|
|
|
|
for (int column_n = 0; column_n < columns_count; column_n++)
|
|
|
|
|
if (TableGetColumnIsEnabled(column_n))
|
|
|
|
|
row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(column_n)).y);
|
|
|
|
|
row_height += style.CellPadding.y * 2.0f;
|
|
|
|
|
|
|
|
|
|
// Open row
|
|
|
|
|
TableNextRow(ImGuiTableRowFlags_Headers, row_height);
|
|
|
|
|
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,
|
|
|
|
|
// as this is intended to be a base template to copy and build from.
|
|
|
|
|
for (int column_n = 0; column_n < columns_count; column_n++)
|
|
|
|
|
{
|
|
|
|
|
if (!TableSetColumnIndex(column_n))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// [DEBUG] Test custom user elements
|
|
|
|
|
#if 0
|
|
|
|
|
if (column_n < 2)
|
|
|
|
|
{
|
|
|
|
|
static bool b[2] = {};
|
|
|
|
|
PushID(column_n);
|
|
|
|
|
PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
|
|
|
|
Checkbox("##", &b[column_n]);
|
|
|
|
|
PopStyleVar();
|
|
|
|
|
PopID();
|
|
|
|
|
SameLine(0.0f, style.ItemInnerSpacing.x);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// 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 they won't collide
|
|
|
|
|
// - table->InstanceCurrent is only >0 when we use multiple BeginTable/EndTable calls with same identifier.
|
|
|
|
|
const char* name = TableGetColumnName(column_n);
|
|
|
|
|
PushID(table->InstanceCurrent * table->ColumnsCount + column_n);
|
|
|
|
|
TableHeader(name);
|
|
|
|
|
PopID();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Allow opening popup from the right-most section after the last column.
|
|
|
|
|
// FIXME-TABLE: TableOpenContextMenu() is not public yet.
|
|
|
|
|
ImVec2 mouse_pos = ImGui::GetMousePos();
|
|
|
|
|
if (IsMouseReleased(1) && TableGetHoveredColumn() == columns_count)
|
|
|
|
|
if (mouse_pos.y >= row_y1 && mouse_pos.y < row_y1 + row_height)
|
|
|
|
|
TableOpenContextMenu(-1); // Will open a non-column-specific popup.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Emit a column header (text + optional sort order)
|
|
|
|
|
// We cpu-clip text here so that all columns headers can be merged into a same draw call.
|
|
|
|
|
// Note that because of how we cpu-clip and display sorting indicators, you _cannot_ use SameLine() after a TableHeader()
|
|
|
|
|
// FIXME-TABLE: Style confusion between CellPadding.y and FramePadding.y
|
|
|
|
|
void ImGui::TableHeader(const char* label)
|
|
|
|
|
{
|
|
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
|
|
ImGuiWindow* window = g.CurrentWindow;
|
|
|
|
|
if (window->SkipItems)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ImGuiTable* table = g.CurrentTable;
|
|
|
|
|
IM_ASSERT(table != NULL && "Need to call TableHeader() after BeginTable()!");
|
|
|
|
|
IM_ASSERT(table->CurrentColumn != -1);
|
|
|
|
|
const int column_n = table->CurrentColumn;
|
|
|
|
|
ImGuiTableColumn* column = &table->Columns[column_n];
|
|
|
|
|
|
|
|
|
|
// Label
|
|
|
|
|
if (label == NULL)
|
|
|
|
|
label = "";
|
|
|
|
|
const char* label_end = FindRenderedTextEnd(label);
|
|
|
|
|
ImVec2 label_size = CalcTextSize(label, label_end, true);
|
|
|
|
|
ImVec2 label_pos = window->DC.CursorPos;
|
|
|
|
|
|
|
|
|
|
// If we already got a row height, there's use that.
|
|
|
|
|
// FIXME-TABLE-PADDING: Problem if the correct outer-padding CellBgRect strays off our ClipRect
|
|
|
|
|
ImRect cell_r = TableGetCellBgRect(table, column_n);
|
|
|
|
|
float label_height = ImMax(label_size.y, table->RowMinHeight - table->CellPaddingY * 2.0f);
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
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
|
|
|
|
|
if (!ItemAdd(bb, id))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
//GetForegroundDrawList()->AddRect(cell_r.Min, cell_r.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]
|
|
|
|
|
//GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]
|
|
|
|
|
|
|
|
|
|
bool hovered, held;
|
|
|
|
|
bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);
|
|
|
|
|
if (hovered || selected)
|
|
|
|
|
{
|
|
|
|
|
const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
|
|
|
|
|
//RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
|
|
|
|
|
TableSetBgColor(ImGuiTableBgTarget_CellBg, col, table->CurrentColumn);
|
|
|
|
|
RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
|
|
|
|
|
}
|
|
|
|
|
if (held)
|
|
|
|
|
table->HeldHeaderColumn = (ImS8)column_n;
|
|
|
|
|
window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f;
|
|
|
|
|
|
|
|
|
|
// Drag and drop to re-order columns.
|
|
|
|
|
// FIXME-TABLE: Scroll request while reordering a column and it lands out of the scrolling zone.
|
|
|
|
|
if (held && (table->Flags & ImGuiTableFlags_Reorderable) && IsMouseDragging(0) && !g.DragDropActive)
|
|
|
|
|
{
|
|
|
|
|
// While moving a column it will jump on the other side of the mouse, so we also test for MouseDelta.x
|
|
|
|
|
table->ReorderColumn = (ImS8)column_n;
|
|
|
|
|
table->InstanceInteracted = table->InstanceCurrent;
|
|
|
|
|
|
|
|
|
|
// We don't reorder: through the frozen<>unfrozen line, or through a column that is marked with ImGuiTableColumnFlags_NoReorder.
|
|
|
|
|
if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < cell_r.Min.x)
|
|
|
|
|
if (ImGuiTableColumn* prev_column = (column->PrevEnabledColumn != -1) ? &table->Columns[column->PrevEnabledColumn] : NULL)
|
|
|
|
|
if (!((column->Flags | prev_column->Flags) & ImGuiTableColumnFlags_NoReorder))
|
|
|
|
|
if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (prev_column->IndexWithinEnabledSet < table->FreezeColumnsRequest))
|
|
|
|
|
table->ReorderColumnDir = -1;
|
|
|
|
|
if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > cell_r.Max.x)
|
|
|
|
|
if (ImGuiTableColumn* next_column = (column->NextEnabledColumn != -1) ? &table->Columns[column->NextEnabledColumn] : NULL)
|
|
|
|
|
if (!((column->Flags | next_column->Flags) & ImGuiTableColumnFlags_NoReorder))
|
|
|
|
|
if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (next_column->IndexWithinEnabledSet < table->FreezeColumnsRequest))
|
|
|
|
|
table->ReorderColumnDir = +1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sort order arrow
|
|
|
|
|
float w_arrow = 0.0f;
|
|
|
|
|
float w_sort_text = 0.0f;
|
|
|
|
|
float ellipsis_max = cell_r.Max.x;
|
|
|
|
|
if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort))
|
|
|
|
|
{
|
|
|
|
|
const float ARROW_SCALE = 0.65f;
|
|
|
|
|
w_arrow = ImFloor(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x);// table->CellPadding.x);
|
|
|
|
|
if (column->SortOrder != -1)
|
|
|
|
|
{
|
|
|
|
|
w_sort_text = 0.0f;
|
|
|
|
|
|
|
|
|
|
char sort_order_suf[8];
|
|
|
|
|
if (column->SortOrder > 0)
|
|
|
|
|
{
|
|
|
|
|
ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), "%d", column->SortOrder + 1);
|
|
|
|
|
w_sort_text = g.Style.ItemInnerSpacing.x + CalcTextSize(sort_order_suf).x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float x = ImMax(cell_r.Min.x, cell_r.Max.x - w_arrow - w_sort_text);
|
|
|
|
|
ellipsis_max -= w_arrow + w_sort_text;
|
|
|
|
|
|
|
|
|
|
float y = label_pos.y;
|
|
|
|
|
ImU32 col = GetColorU32(ImGuiCol_Text);
|
|
|
|
|
if (column->SortOrder > 0)
|
|
|
|
|
{
|
|
|
|
|
PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_Text, 0.70f));
|
|
|
|
|
RenderText(ImVec2(x + g.Style.ItemInnerSpacing.x, y), sort_order_suf);
|
|
|
|
|
PopStyleColor();
|
|
|
|
|
x += w_sort_text;
|
|
|
|
|
}
|
|
|
|
|
RenderArrow(window->DrawList, ImVec2(x, y), col, column->SortDirection == ImGuiSortDirection_Ascending ? ImGuiDir_Up : ImGuiDir_Down, ARROW_SCALE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle clicking on column header to adjust Sort Order
|
|
|
|
|
if (pressed && table->ReorderColumn != column_n)
|
|
|
|
|
{
|
|
|
|
|
// Set new sort direction
|
|
|
|
|
// - If the PreferSortDescending flag is set, we will default to a Descending direction on the first click.
|
|
|
|
|
// - Note that the PreferSortAscending flag is never checked, it is essentially the default and therefore a no-op.
|
|
|
|
|
ImGuiSortDirection sort_direction;
|
|
|
|
|
if (column->SortOrder == -1)
|
|
|
|
|
sort_direction = (column->Flags & ImGuiTableColumnFlags_PreferSortDescending) ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending;
|
|
|
|
|
else
|
|
|
|
|
sort_direction = (column->SortDirection == ImGuiSortDirection_Ascending) ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending;
|
|
|
|
|
TableSetColumnSortDirection(column_n, sort_direction, g.IO.KeyShift);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will
|
|
|
|
|
// be merged into a single draw call.
|
|
|
|
|
//window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);
|
|
|
|
|
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size);
|
|
|
|
|
|
|
|
|
|
const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
|
|
|
|
|
if (text_clipped && hovered && g.HoveredIdNotActiveTimer > g.TooltipSlowDelay)
|
|
|
|
|
SetTooltip("%.*s", (int)(label_end - label), label);
|
|
|
|
|
|
|
|
|
|
// We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considering for merging.
|
|
|
|
|
float max_pos_x = label_pos.x + label_size.x + w_sort_text + w_arrow;
|
|
|
|
|
column->ContentMaxXHeadersUsed = ImMax(column->ContentMaxXHeadersUsed, column->WorkMaxX);
|
|
|
|
|
column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x);
|
|
|
|
|
|
|
|
|
|
// We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden
|
|
|
|
|
if (IsMouseReleased(1) && IsItemHovered())
|
|
|
|
|
TableOpenContextMenu(column_n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs)
|
|
|
|
@ -2497,6 +2290,17 @@ void ImGui::TableSetBgColor(ImGuiTableBgTarget bg_target, ImU32 color, int colum
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
|
// [SECTION] Tables: Sorting
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
|
// - TableGetSortSpecs()
|
|
|
|
|
// - TableGetColumnIsSorted()
|
|
|
|
|
// - TableFixColumnSortDirection() [Internal]
|
|
|
|
|
// - TableSetColumnSortDirection() [Internal]
|
|
|
|
|
// - TableSortSpecsSanitize() [Internal]
|
|
|
|
|
// - TableSortSpecsBuild() [Internal]
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void ImGui::TableSortSpecsSanitize(ImGuiTable* table)
|
|
|
|
|
{
|
|
|
|
|
IM_ASSERT(table->Flags & ImGuiTableFlags_Sortable);
|
|
|
|
@ -2587,6 +2391,220 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table)
|
|
|
|
|
table->IsSortSpecsDirty = false; // Mark as not dirty for us
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
|
// [SECTION] Tables: Headers
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
|
// - TableHeadersRow()
|
|
|
|
|
// - TableHeader()
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
// 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 can create their own! For example: TableHeader() may be preceeded by Checkbox() or other custom widgets.
|
|
|
|
|
// See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this.
|
|
|
|
|
void ImGui::TableHeadersRow()
|
|
|
|
|
{
|
|
|
|
|
ImGuiStyle& style = ImGui::GetStyle();
|
|
|
|
|
|
|
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
|
|
ImGuiTable* table = g.CurrentTable;
|
|
|
|
|
IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!");
|
|
|
|
|
|
|
|
|
|
// Calculate row height (for the unlikely case that labels may be are multi-line)
|
|
|
|
|
// If we didn't do that, uneven header height would work but their highlight won't cover the full row height.
|
|
|
|
|
float row_height = GetTextLineHeight();
|
|
|
|
|
const float row_y1 = GetCursorScreenPos().y;
|
|
|
|
|
const int columns_count = TableGetColumnCount();
|
|
|
|
|
for (int column_n = 0; column_n < columns_count; column_n++)
|
|
|
|
|
if (TableGetColumnIsEnabled(column_n))
|
|
|
|
|
row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(column_n)).y);
|
|
|
|
|
row_height += style.CellPadding.y * 2.0f;
|
|
|
|
|
|
|
|
|
|
// Open row
|
|
|
|
|
TableNextRow(ImGuiTableRowFlags_Headers, row_height);
|
|
|
|
|
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,
|
|
|
|
|
// as this is intended to be a base template to copy and build from.
|
|
|
|
|
for (int column_n = 0; column_n < columns_count; column_n++)
|
|
|
|
|
{
|
|
|
|
|
if (!TableSetColumnIndex(column_n))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// [DEBUG] Test custom user elements
|
|
|
|
|
#if 0
|
|
|
|
|
if (column_n < 2)
|
|
|
|
|
{
|
|
|
|
|
static bool b[2] = {};
|
|
|
|
|
PushID(column_n);
|
|
|
|
|
PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
|
|
|
|
Checkbox("##", &b[column_n]);
|
|
|
|
|
PopStyleVar();
|
|
|
|
|
PopID();
|
|
|
|
|
SameLine(0.0f, style.ItemInnerSpacing.x);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// 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 they won't collide
|
|
|
|
|
// - table->InstanceCurrent is only >0 when we use multiple BeginTable/EndTable calls with same identifier.
|
|
|
|
|
const char* name = TableGetColumnName(column_n);
|
|
|
|
|
PushID(table->InstanceCurrent * table->ColumnsCount + column_n);
|
|
|
|
|
TableHeader(name);
|
|
|
|
|
PopID();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Allow opening popup from the right-most section after the last column.
|
|
|
|
|
// FIXME-TABLE: TableOpenContextMenu() is not public yet.
|
|
|
|
|
ImVec2 mouse_pos = ImGui::GetMousePos();
|
|
|
|
|
if (IsMouseReleased(1) && TableGetHoveredColumn() == columns_count)
|
|
|
|
|
if (mouse_pos.y >= row_y1 && mouse_pos.y < row_y1 + row_height)
|
|
|
|
|
TableOpenContextMenu(-1); // Will open a non-column-specific popup.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Emit a column header (text + optional sort order)
|
|
|
|
|
// We cpu-clip text here so that all columns headers can be merged into a same draw call.
|
|
|
|
|
// Note that because of how we cpu-clip and display sorting indicators, you _cannot_ use SameLine() after a TableHeader()
|
|
|
|
|
// FIXME-TABLE: Style confusion between CellPadding.y and FramePadding.y
|
|
|
|
|
void ImGui::TableHeader(const char* label)
|
|
|
|
|
{
|
|
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
|
|
ImGuiWindow* window = g.CurrentWindow;
|
|
|
|
|
if (window->SkipItems)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ImGuiTable* table = g.CurrentTable;
|
|
|
|
|
IM_ASSERT(table != NULL && "Need to call TableHeader() after BeginTable()!");
|
|
|
|
|
IM_ASSERT(table->CurrentColumn != -1);
|
|
|
|
|
const int column_n = table->CurrentColumn;
|
|
|
|
|
ImGuiTableColumn* column = &table->Columns[column_n];
|
|
|
|
|
|
|
|
|
|
// Label
|
|
|
|
|
if (label == NULL)
|
|
|
|
|
label = "";
|
|
|
|
|
const char* label_end = FindRenderedTextEnd(label);
|
|
|
|
|
ImVec2 label_size = CalcTextSize(label, label_end, true);
|
|
|
|
|
ImVec2 label_pos = window->DC.CursorPos;
|
|
|
|
|
|
|
|
|
|
// If we already got a row height, there's use that.
|
|
|
|
|
// FIXME-TABLE-PADDING: Problem if the correct outer-padding CellBgRect strays off our ClipRect
|
|
|
|
|
ImRect cell_r = TableGetCellBgRect(table, column_n);
|
|
|
|
|
float label_height = ImMax(label_size.y, table->RowMinHeight - table->CellPaddingY * 2.0f);
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
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
|
|
|
|
|
if (!ItemAdd(bb, id))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
//GetForegroundDrawList()->AddRect(cell_r.Min, cell_r.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]
|
|
|
|
|
//GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]
|
|
|
|
|
|
|
|
|
|
bool hovered, held;
|
|
|
|
|
bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);
|
|
|
|
|
if (hovered || selected)
|
|
|
|
|
{
|
|
|
|
|
const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
|
|
|
|
|
//RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
|
|
|
|
|
TableSetBgColor(ImGuiTableBgTarget_CellBg, col, table->CurrentColumn);
|
|
|
|
|
RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
|
|
|
|
|
}
|
|
|
|
|
if (held)
|
|
|
|
|
table->HeldHeaderColumn = (ImS8)column_n;
|
|
|
|
|
window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f;
|
|
|
|
|
|
|
|
|
|
// Drag and drop to re-order columns.
|
|
|
|
|
// FIXME-TABLE: Scroll request while reordering a column and it lands out of the scrolling zone.
|
|
|
|
|
if (held && (table->Flags & ImGuiTableFlags_Reorderable) && IsMouseDragging(0) && !g.DragDropActive)
|
|
|
|
|
{
|
|
|
|
|
// While moving a column it will jump on the other side of the mouse, so we also test for MouseDelta.x
|
|
|
|
|
table->ReorderColumn = (ImS8)column_n;
|
|
|
|
|
table->InstanceInteracted = table->InstanceCurrent;
|
|
|
|
|
|
|
|
|
|
// We don't reorder: through the frozen<>unfrozen line, or through a column that is marked with ImGuiTableColumnFlags_NoReorder.
|
|
|
|
|
if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < cell_r.Min.x)
|
|
|
|
|
if (ImGuiTableColumn* prev_column = (column->PrevEnabledColumn != -1) ? &table->Columns[column->PrevEnabledColumn] : NULL)
|
|
|
|
|
if (!((column->Flags | prev_column->Flags) & ImGuiTableColumnFlags_NoReorder))
|
|
|
|
|
if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (prev_column->IndexWithinEnabledSet < table->FreezeColumnsRequest))
|
|
|
|
|
table->ReorderColumnDir = -1;
|
|
|
|
|
if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > cell_r.Max.x)
|
|
|
|
|
if (ImGuiTableColumn* next_column = (column->NextEnabledColumn != -1) ? &table->Columns[column->NextEnabledColumn] : NULL)
|
|
|
|
|
if (!((column->Flags | next_column->Flags) & ImGuiTableColumnFlags_NoReorder))
|
|
|
|
|
if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (next_column->IndexWithinEnabledSet < table->FreezeColumnsRequest))
|
|
|
|
|
table->ReorderColumnDir = +1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sort order arrow
|
|
|
|
|
float w_arrow = 0.0f;
|
|
|
|
|
float w_sort_text = 0.0f;
|
|
|
|
|
float ellipsis_max = cell_r.Max.x;
|
|
|
|
|
if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort))
|
|
|
|
|
{
|
|
|
|
|
const float ARROW_SCALE = 0.65f;
|
|
|
|
|
w_arrow = ImFloor(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x);// table->CellPadding.x);
|
|
|
|
|
if (column->SortOrder != -1)
|
|
|
|
|
{
|
|
|
|
|
w_sort_text = 0.0f;
|
|
|
|
|
|
|
|
|
|
char sort_order_suf[8];
|
|
|
|
|
if (column->SortOrder > 0)
|
|
|
|
|
{
|
|
|
|
|
ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), "%d", column->SortOrder + 1);
|
|
|
|
|
w_sort_text = g.Style.ItemInnerSpacing.x + CalcTextSize(sort_order_suf).x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float x = ImMax(cell_r.Min.x, cell_r.Max.x - w_arrow - w_sort_text);
|
|
|
|
|
ellipsis_max -= w_arrow + w_sort_text;
|
|
|
|
|
|
|
|
|
|
float y = label_pos.y;
|
|
|
|
|
ImU32 col = GetColorU32(ImGuiCol_Text);
|
|
|
|
|
if (column->SortOrder > 0)
|
|
|
|
|
{
|
|
|
|
|
PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_Text, 0.70f));
|
|
|
|
|
RenderText(ImVec2(x + g.Style.ItemInnerSpacing.x, y), sort_order_suf);
|
|
|
|
|
PopStyleColor();
|
|
|
|
|
x += w_sort_text;
|
|
|
|
|
}
|
|
|
|
|
RenderArrow(window->DrawList, ImVec2(x, y), col, column->SortDirection == ImGuiSortDirection_Ascending ? ImGuiDir_Up : ImGuiDir_Down, ARROW_SCALE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle clicking on column header to adjust Sort Order
|
|
|
|
|
if (pressed && table->ReorderColumn != column_n)
|
|
|
|
|
{
|
|
|
|
|
// Set new sort direction
|
|
|
|
|
// - If the PreferSortDescending flag is set, we will default to a Descending direction on the first click.
|
|
|
|
|
// - Note that the PreferSortAscending flag is never checked, it is essentially the default and therefore a no-op.
|
|
|
|
|
ImGuiSortDirection sort_direction;
|
|
|
|
|
if (column->SortOrder == -1)
|
|
|
|
|
sort_direction = (column->Flags & ImGuiTableColumnFlags_PreferSortDescending) ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending;
|
|
|
|
|
else
|
|
|
|
|
sort_direction = (column->SortDirection == ImGuiSortDirection_Ascending) ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending;
|
|
|
|
|
TableSetColumnSortDirection(column_n, sort_direction, g.IO.KeyShift);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will
|
|
|
|
|
// be merged into a single draw call.
|
|
|
|
|
//window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);
|
|
|
|
|
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size);
|
|
|
|
|
|
|
|
|
|
const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
|
|
|
|
|
if (text_clipped && hovered && g.HoveredIdNotActiveTimer > g.TooltipSlowDelay)
|
|
|
|
|
SetTooltip("%.*s", (int)(label_end - label), label);
|
|
|
|
|
|
|
|
|
|
// We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considering for merging.
|
|
|
|
|
float max_pos_x = label_pos.x + label_size.x + w_sort_text + w_arrow;
|
|
|
|
|
column->ContentMaxXHeadersUsed = ImMax(column->ContentMaxXHeadersUsed, column->WorkMaxX);
|
|
|
|
|
column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x);
|
|
|
|
|
|
|
|
|
|
// We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden
|
|
|
|
|
if (IsMouseReleased(1) && IsItemHovered())
|
|
|
|
|
TableOpenContextMenu(column_n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
|
// [SECTION] Tables: Context Menu
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
|