|
@@ -2172,213 +2172,6 @@ void ImGui::TablePopBackgroundChannel()
|
|
table->DrawSplitter.SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
|
|
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
|
|
// 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.
|
|
// 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)
|
|
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)
|
|
void ImGui::TableSortSpecsSanitize(ImGuiTable* table)
|
|
{
|
|
{
|
|
IM_ASSERT(table->Flags & ImGuiTableFlags_Sortable);
|
|
IM_ASSERT(table->Flags & ImGuiTableFlags_Sortable);
|
|
@@ -2587,6 +2391,220 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table)
|
|
table->IsSortSpecsDirty = false; // Mark as not dirty for us
|
|
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
|
|
// [SECTION] Tables: Context Menu
|
|
//-------------------------------------------------------------------------
|
|
//-------------------------------------------------------------------------
|