Tables: added Angled headers support. Added ImGuiTableColumnFlags_AngledHeader, ImGui::TableHeadersAngledRow(), style.TableAngledHeadersAngle. (#2957)

This commit is contained in:
ocornut 2023-10-12 17:01:42 +02:00
parent 9f851ebfe4
commit 32228d8add
7 changed files with 250 additions and 28 deletions

View File

@ -94,17 +94,20 @@ 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:
- Tables: Fixed an edge-case when no columns are visible + table scrollbar is visible + user - Added angled headers support. You need to set ImGuiTableColumnFlags_AngledHeader on selected
columns and call TableAngledHeadersRow(). Added style.TableAngledHeadersAngle style option.
- Added ImGuiTableFlags_HighlightHoveredColumn flag, currently highlighting column header.
- 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.
- Tables: Request user to submit contents when outer host-window is requesting auto-resize, - Request user to submit contents when outer host-window is requesting auto-resize,
so a scrolling table can contribute to initial window size. (#6510) so a scrolling table can contribute to initial window size. (#6510)
- Tables: Fixed subtle drawing overlap between borders in some situations. - Fixed subtle drawing overlap between borders in some situations.
- Tables: Fixed bottom-most and right-most outer border offset by one. (#6765, #3752) [@v-ein] - Fixed bottom-most and right-most outer border offset by one. (#6765, #3752) [@v-ein]
- Tables: Fixed top-most outer border being drawn with both TableBorderLight and TableBorderStrong - Fixed top-most outer border being drawn with both TableBorderLight and TableBorderStrong
in some situations, causing the earlier to be visible underneath when alpha is not 1.0f. in some situations, causing the earlier to be visible underneath when alpha is not 1.0f.
- Tables: fixed right-clicking right-most section (past right-most column) from highlighting a column. - fixed right-clicking right-most section (past right-most column) from highlighting a column.
- TabBar: Fixed position of unsaved document marker (ImGuiTabItemFlags_UnsavedDocument) which was - TabBar: Fixed position of unsaved document marker (ImGuiTabItemFlags_UnsavedDocument) which was
accidentally offset in 1.89.9. (#6862) [@alektron] accidentally offset in 1.89.9. (#6862) [@alektron]
- Fonts: 'float size_pixels' passed to AddFontXXX() functions is now rounded to lowest integer. - Fonts: 'float size_pixels' passed to AddFontXXX() functions is now rounded to lowest integer.

View File

@ -1188,6 +1188,7 @@ ImGuiStyle::ImGuiStyle()
TabBorderSize = 0.0f; // Thickness of border around tabs. TabBorderSize = 0.0f; // Thickness of border around tabs.
TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected.
TabBarBorderSize = 1.0f; // Thickness of tab-bar separator, which takes on the tab active color to denote focus. TabBarBorderSize = 1.0f; // Thickness of tab-bar separator, which takes on the tab active color to denote focus.
TableAngledHeadersAngle = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees).
ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.

View File

@ -771,8 +771,9 @@ namespace ImGui
// - Use TableSetupScrollFreeze() to lock columns/rows so they stay visible when scrolled. // - Use TableSetupScrollFreeze() to lock columns/rows so they stay visible when scrolled.
IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_id = 0); IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_id = 0);
IMGUI_API void TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled. IMGUI_API void TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled.
IMGUI_API void TableHeadersRow(); // submit all headers cells based on data provided to TableSetupColumn() + submit context menu
IMGUI_API void TableHeader(const char* label); // submit one header cell manually (rarely used) IMGUI_API void TableHeader(const char* label); // submit one header cell manually (rarely used)
IMGUI_API void TableHeadersRow(); // submit a row with headers cells based on data provided to TableSetupColumn() + submit context menu
IMGUI_API void TableAngledHeadersRow(); // submit a row with angled headers for every column with the ImGuiTableColumnFlags_AngledHeader flag. MUST BE FIRST ROW.
// Tables: Sorting & Miscellaneous functions // Tables: Sorting & Miscellaneous functions
// - Sorting: call TableGetSortSpecs() to retrieve latest sort specs for the table. NULL when not sorting. // - Sorting: call TableGetSortSpecs() to retrieve latest sort specs for the table. NULL when not sorting.
@ -1240,12 +1241,13 @@ enum ImGuiTableColumnFlags_
ImGuiTableColumnFlags_NoSort = 1 << 9, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table). ImGuiTableColumnFlags_NoSort = 1 << 9, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table).
ImGuiTableColumnFlags_NoSortAscending = 1 << 10, // Disable ability to sort in the ascending direction. ImGuiTableColumnFlags_NoSortAscending = 1 << 10, // Disable ability to sort in the ascending direction.
ImGuiTableColumnFlags_NoSortDescending = 1 << 11, // Disable ability to sort in the descending direction. ImGuiTableColumnFlags_NoSortDescending = 1 << 11, // Disable ability to sort in the descending direction.
ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12, // TableHeadersRow() will not submit label for this column. Convenient for some small columns. Name will still appear in context menu. ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12, // TableHeadersRow() will not submit horizontal label for this column. Convenient for some small columns. Name will still appear in context menu or in angled headers.
ImGuiTableColumnFlags_NoHeaderWidth = 1 << 13, // Disable header text width contribution to automatic column width. ImGuiTableColumnFlags_NoHeaderWidth = 1 << 13, // Disable header text width contribution to automatic column width.
ImGuiTableColumnFlags_PreferSortAscending = 1 << 14, // Make the initial sort direction Ascending when first sorting on this column (default). ImGuiTableColumnFlags_PreferSortAscending = 1 << 14, // Make the initial sort direction Ascending when first sorting on this column (default).
ImGuiTableColumnFlags_PreferSortDescending = 1 << 15, // Make the initial sort direction Descending when first sorting on this column. ImGuiTableColumnFlags_PreferSortDescending = 1 << 15, // Make the initial sort direction Descending when first sorting on this column.
ImGuiTableColumnFlags_IndentEnable = 1 << 16, // Use current Indent value when entering cell (default for column 0). ImGuiTableColumnFlags_IndentEnable = 1 << 16, // Use current Indent value when entering cell (default for column 0).
ImGuiTableColumnFlags_IndentDisable = 1 << 17, // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored. ImGuiTableColumnFlags_IndentDisable = 1 << 17, // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored.
ImGuiTableColumnFlags_AngledHeader = 1 << 18, // TableHeadersRow() will submit an angled header row for this column. Note this will add an extra row.
// Output status flags, read-only via TableGetColumnFlags() // Output status flags, read-only via TableGetColumnFlags()
ImGuiTableColumnFlags_IsEnabled = 1 << 24, // Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags. ImGuiTableColumnFlags_IsEnabled = 1 << 24, // Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags.
@ -1926,6 +1928,7 @@ struct ImGuiStyle
float TabBorderSize; // Thickness of border around tabs. float TabBorderSize; // Thickness of border around tabs.
float TabMinWidthForCloseButton; // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. float TabMinWidthForCloseButton; // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected.
float TabBarBorderSize; // Thickness of tab-bar separator, which takes on the tab active color to denote focus. float TabBarBorderSize; // Thickness of tab-bar separator, which takes on the tab active color to denote focus.
float TableAngledHeadersAngle; // Angle of angled headers (supported values range from -50.0f degrees to +50.0f degrees).
ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered). ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered).
ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.

View File

@ -3949,6 +3949,7 @@ static void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags)
ImGui::CheckboxFlags("_PreferSortDescending", p_flags, ImGuiTableColumnFlags_PreferSortDescending); ImGui::CheckboxFlags("_PreferSortDescending", p_flags, ImGuiTableColumnFlags_PreferSortDescending);
ImGui::CheckboxFlags("_IndentEnable", p_flags, ImGuiTableColumnFlags_IndentEnable); ImGui::SameLine(); HelpMarker("Default for column 0"); ImGui::CheckboxFlags("_IndentEnable", p_flags, ImGuiTableColumnFlags_IndentEnable); ImGui::SameLine(); HelpMarker("Default for column 0");
ImGui::CheckboxFlags("_IndentDisable", p_flags, ImGuiTableColumnFlags_IndentDisable); ImGui::SameLine(); HelpMarker("Default for column >0"); ImGui::CheckboxFlags("_IndentDisable", p_flags, ImGuiTableColumnFlags_IndentDisable); ImGui::SameLine(); HelpMarker("Default for column >0");
ImGui::CheckboxFlags("_AngledHeader", p_flags, ImGuiTableColumnFlags_AngledHeader);
} }
static void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags) static void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags)
@ -4695,8 +4696,14 @@ static void ShowDemoWindowTables()
ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 9); ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 9);
if (ImGui::BeginTable("table_columns_flags", column_count, flags, outer_size)) if (ImGui::BeginTable("table_columns_flags", column_count, flags, outer_size))
{ {
bool has_angled_header = false;
for (int column = 0; column < column_count; column++) for (int column = 0; column < column_count; column++)
{
has_angled_header |= (column_flags[column] & ImGuiTableColumnFlags_AngledHeader) != 0;
ImGui::TableSetupColumn(column_names[column], column_flags[column]); ImGui::TableSetupColumn(column_names[column], column_flags[column]);
}
if (has_angled_header)
ImGui::TableAngledHeadersRow();
ImGui::TableHeadersRow(); ImGui::TableHeadersRow();
for (int column = 0; column < column_count; column++) for (int column = 0; column < column_count; column++)
column_flags_out[column] = ImGui::TableGetColumnFlags(column); column_flags_out[column] = ImGui::TableGetColumnFlags(column);
@ -5182,6 +5189,56 @@ static void ShowDemoWindowTables()
ImGui::TreePop(); ImGui::TreePop();
} }
// Demonstrate using ImGuiTableColumnFlags_AngledHeader flag to create angled headers
if (open_action != -1)
ImGui::SetNextItemOpen(open_action != 0);
IMGUI_DEMO_MARKER("Tables/Angled headers");
if (ImGui::TreeNode("Angled headers"))
{
const char* column_names[] = { "Track", "cabasa", "ride", "smash", "tom-hi", "tom-mid", "tom-low", "hihat-o", "hihat-c", "snare-s", "snare-c", "clap", "rim", "kick" };
const int columns_count = IM_ARRAYSIZE(column_names);
const int rows_count = 12;
static ImGuiTableFlags table_flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_Hideable | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_HighlightHoveredColumn;
static bool bools[columns_count * rows_count] = {}; // Dummy storage selection storage
static int frozen_rows = 2;
ImGui::CheckboxFlags("_ScrollX", &table_flags, ImGuiTableFlags_ScrollX);
ImGui::CheckboxFlags("_ScrollY", &table_flags, ImGuiTableFlags_ScrollY);
ImGui::CheckboxFlags("_NoBordersInBody", &table_flags, ImGuiTableFlags_NoBordersInBody);
ImGui::CheckboxFlags("_HighlightHoveredColumn", &table_flags, ImGuiTableFlags_HighlightHoveredColumn);
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
ImGui::SliderInt("Frozen rows", &frozen_rows, 0, 2);
if (ImGui::BeginTable("table_angled_headers", columns_count, table_flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 12)))
{
ImGui::TableSetupColumn(column_names[0], ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_NoReorder);
for (int n = 1; n < columns_count; n++)
ImGui::TableSetupColumn(column_names[n], ImGuiTableColumnFlags_AngledHeader | ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupScrollFreeze(0, frozen_rows);
ImGui::TableAngledHeadersRow(); // Draw angled headers for all columns with the ImGuiTableColumnFlags_AngledHeader flag.
ImGui::TableHeadersRow(); // Draw remaining headers and allow access to context-menu and other functions.
for (int row = 0; row < rows_count; row++)
{
ImGui::PushID(row);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::AlignTextToFramePadding();
ImGui::Text("Track %d", row);
for (int column = 1; column < columns_count; column++)
if (ImGui::TableSetColumnIndex(column))
{
ImGui::PushID(column);
ImGui::Checkbox("", &bools[row * columns_count + column]);
ImGui::PopID();
}
ImGui::PopID();
}
ImGui::EndTable();
}
ImGui::TreePop();
}
// Demonstrate creating custom context menus inside columns, while playing it nice with context menus provided by TableHeadersRow()/TableHeader() // Demonstrate creating custom context menus inside columns, while playing it nice with context menus provided by TableHeadersRow()/TableHeader()
if (open_action != -1) if (open_action != -1)
ImGui::SetNextItemOpen(open_action != 0); ImGui::SetNextItemOpen(open_action != 0);
@ -5428,6 +5485,7 @@ static void ShowDemoWindowTables()
| ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBody
| ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY
| ImGuiTableFlags_SizingFixedFit; | ImGuiTableFlags_SizingFixedFit;
static ImGuiTableColumnFlags columns_base_flags = ImGuiTableColumnFlags_None;
enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable, CT_SelectableSpanRow }; enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable, CT_SelectableSpanRow };
static int contents_type = CT_SelectableSpanRow; static int contents_type = CT_SelectableSpanRow;
@ -5525,6 +5583,8 @@ static void ShowDemoWindowTables()
{ {
ImGui::Checkbox("show_headers", &show_headers); ImGui::Checkbox("show_headers", &show_headers);
ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn); ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn);
ImGui::CheckboxFlags("ImGuiTableColumnFlags_AngledHeader", &columns_base_flags, ImGuiTableColumnFlags_AngledHeader);
ImGui::SameLine(); HelpMarker("Enable AngledHeader on all columns. Best enabled on selected narrow columns (see \"Angled headers\" section of the demo).");
ImGui::TreePop(); ImGui::TreePop();
} }
@ -5590,12 +5650,12 @@ static void ShowDemoWindowTables()
// Declare columns // Declare columns
// We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications. // We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications.
// This is so our sort function can identify a column given our own identifier. We could also identify them based on their index! // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index!
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide, 0.0f, MyItemColumnID_ID); ImGui::TableSetupColumn("ID", columns_base_flags | ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide, 0.0f, MyItemColumnID_ID);
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name); ImGui::TableSetupColumn("Name", columns_base_flags | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name);
ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action); ImGui::TableSetupColumn("Action", columns_base_flags | ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action);
ImGui::TableSetupColumn("Quantity", ImGuiTableColumnFlags_PreferSortDescending, 0.0f, MyItemColumnID_Quantity); ImGui::TableSetupColumn("Quantity", columns_base_flags | ImGuiTableColumnFlags_PreferSortDescending, 0.0f, MyItemColumnID_Quantity);
ImGui::TableSetupColumn("Description", (flags & ImGuiTableFlags_NoHostExtendX) ? 0 : ImGuiTableColumnFlags_WidthStretch, 0.0f, MyItemColumnID_Description); ImGui::TableSetupColumn("Description", columns_base_flags | ((flags & ImGuiTableFlags_NoHostExtendX) ? 0 : ImGuiTableColumnFlags_WidthStretch), 0.0f, MyItemColumnID_Description);
ImGui::TableSetupColumn("Hidden", ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort); ImGui::TableSetupColumn("Hidden", columns_base_flags | ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort);
ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows); ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows);
// Sort our data if sort specs have been changed! // Sort our data if sort specs have been changed!
@ -5614,6 +5674,8 @@ static void ShowDemoWindowTables()
const bool sorts_specs_using_quantity = (ImGui::TableGetColumnFlags(3) & ImGuiTableColumnFlags_IsSorted) != 0; const bool sorts_specs_using_quantity = (ImGui::TableGetColumnFlags(3) & ImGuiTableColumnFlags_IsSorted) != 0;
// Show headers // Show headers
if (show_headers && (columns_base_flags & ImGuiTableColumnFlags_AngledHeader) != 0)
ImGui::TableAngledHeadersRow();
if (show_headers) if (show_headers)
ImGui::TableHeadersRow(); ImGui::TableHeadersRow();
@ -6400,7 +6462,6 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
ImGui::SeparatorText("Main"); ImGui::SeparatorText("Main");
ImGui::SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2("CellPadding", (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f"); ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f");
@ -6425,6 +6486,10 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f"); ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f"); ImGui::SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f");
ImGui::SeparatorText("Tables");
ImGui::SliderFloat2("CellPadding", (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f");
ImGui::SliderAngle("TableAngledHeadersAngle", &style.TableAngledHeadersAngle, -50.0f, +50.0f);
ImGui::SeparatorText("Widgets"); ImGui::SeparatorText("Widgets");
ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f");
int window_menu_button_position = style.WindowMenuButtonPosition + 1; int window_menu_button_position = style.WindowMenuButtonPosition + 1;

View File

@ -1989,6 +1989,14 @@ void ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int ve
} }
} }
void ImGui::ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out)
{
ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx;
ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx;
for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex)
vertex->pos = ImRotate(vertex->pos- pivot_in, cos_a, sin_a) + pivot_out;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] ImFontConfig // [SECTION] ImFontConfig
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -2731,6 +2731,8 @@ struct IMGUI_API ImGuiTable
float ResizedColumnNextWidth; float ResizedColumnNextWidth;
float ResizeLockMinContentsX2; // Lock minimum contents width while resizing down in order to not create feedback loops. But we allow growing the table. float ResizeLockMinContentsX2; // Lock minimum contents width while resizing down in order to not create feedback loops. But we allow growing the table.
float RefScale; // Reference scale to be able to rescale columns on font/dpi changes. float RefScale; // Reference scale to be able to rescale columns on font/dpi changes.
float AngledHeadersHeight; // Set by TableAngledHeadersRow(), used in TableUpdateLayout()
float AngledHeadersSlope; // Set by TableAngledHeadersRow(), used in TableUpdateLayout()
ImRect OuterRect; // Note: for non-scrolling table, OuterRect.Max.y is often FLT_MAX until EndTable(), unless a height has been specified in BeginTable(). ImRect OuterRect; // Note: for non-scrolling table, OuterRect.Max.y is often FLT_MAX until EndTable(), unless a height has been specified in BeginTable().
ImRect InnerRect; // InnerRect but without decoration. As with OuterRect, for non-scrolling tables, InnerRect.Max.y is ImRect InnerRect; // InnerRect but without decoration. As with OuterRect, for non-scrolling tables, InnerRect.Max.y is
ImRect WorkRect; ImRect WorkRect;
@ -2753,6 +2755,7 @@ struct IMGUI_API ImGuiTable
ImGuiTableColumnIdx ColumnsEnabledCount; // Number of enabled columns (<= ColumnsCount) ImGuiTableColumnIdx ColumnsEnabledCount; // Number of enabled columns (<= ColumnsCount)
ImGuiTableColumnIdx ColumnsEnabledFixedCount; // Number of enabled columns (<= ColumnsCount) ImGuiTableColumnIdx ColumnsEnabledFixedCount; // Number of enabled columns (<= ColumnsCount)
ImGuiTableColumnIdx DeclColumnsCount; // Count calls to TableSetupColumn() ImGuiTableColumnIdx DeclColumnsCount; // Count calls to TableSetupColumn()
ImGuiTableColumnIdx AngledHeadersCount; // Count columns with angled headers
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 HighlightColumnHeader; // Index of column which should be highlighted.
@ -2802,11 +2805,12 @@ struct IMGUI_API ImGuiTable
// Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table). // Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table).
// - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure. // - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure.
// - We also leave out of this structure data that tend to be particularly useful for debugging/metrics. // - We also leave out of this structure data that tend to be particularly useful for debugging/metrics.
// sizeof() ~ 112 bytes. // sizeof() ~ 120 bytes.
struct IMGUI_API ImGuiTableTempData struct IMGUI_API ImGuiTableTempData
{ {
int TableIndex; // Index in g.Tables.Buf[] pool int TableIndex; // Index in g.Tables.Buf[] pool
float LastTimeActive; // Last timestamp this structure was used float LastTimeActive; // Last timestamp this structure was used
float AngledheadersExtraWidth; // Used in EndTable()
ImVec2 UserOuterSize; // outer_size.x passed to BeginTable() ImVec2 UserOuterSize; // outer_size.x passed to BeginTable()
ImDrawListSplitter DrawSplitter; ImDrawListSplitter DrawSplitter;
@ -3177,8 +3181,10 @@ namespace ImGui
IMGUI_API int TableGetHoveredColumn(); // May use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead. 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 int TableGetHoveredColumn(); // May use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead. 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 int TableGetHoveredRow(); // Retrieve *PREVIOUS FRAME* hovered row. This difference with TableGetHoveredColumn() is the reason why this is not public yet. IMGUI_API int TableGetHoveredRow(); // Retrieve *PREVIOUS FRAME* hovered row. This difference with TableGetHoveredColumn() is the reason why this is not public yet.
IMGUI_API float TableGetHeaderRowHeight(); IMGUI_API float TableGetHeaderRowHeight();
IMGUI_API float TableGetHeaderAngledMaxLabelWidth();
IMGUI_API void TablePushBackgroundChannel(); IMGUI_API void TablePushBackgroundChannel();
IMGUI_API void TablePopBackgroundChannel(); IMGUI_API void TablePopBackgroundChannel();
IMGUI_API void TableAngledHeadersRowEx(float angle, float label_width = 0.0f);
// Tables: Internals // Tables: Internals
inline ImGuiTable* GetCurrentTable() { ImGuiContext& g = *GImGui; return g.CurrentTable; } inline ImGuiTable* GetCurrentTable() { ImGuiContext& g = *GImGui; return g.CurrentTable; }
@ -3336,6 +3342,7 @@ namespace ImGui
// Shade functions (write over already created vertices) // Shade functions (write over already created vertices)
IMGUI_API void ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1); IMGUI_API void ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1);
IMGUI_API void ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp); IMGUI_API void ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp);
IMGUI_API void ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out);
// Garbage collection // Garbage collection
IMGUI_API void GcCompactTransientMiscBuffers(); IMGUI_API void GcCompactTransientMiscBuffers();

View File

@ -478,9 +478,10 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
table->FreezeRowsRequest = table->FreezeRowsCount = 0; // This will be setup by TableSetupScrollFreeze(), if any table->FreezeRowsRequest = table->FreezeRowsCount = 0; // This will be setup by TableSetupScrollFreeze(), if any
table->FreezeColumnsRequest = table->FreezeColumnsCount = 0; table->FreezeColumnsRequest = table->FreezeColumnsCount = 0;
table->IsUnfrozenRows = true; table->IsUnfrozenRows = true;
table->DeclColumnsCount = 0; table->DeclColumnsCount = table->AngledHeadersCount = 0;
if (previous_frame_active + 1 < g.FrameCount) if (previous_frame_active + 1 < g.FrameCount)
table->IsActiveIdInTable = false; table->IsActiveIdInTable = false;
temp_data->AngledheadersExtraWidth = 0.0f;
// 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);
@ -984,6 +985,12 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0, ImGuiItemFlags_None); const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0, ImGuiItemFlags_None);
g.ActiveId = backup_active_id; g.ActiveId = backup_active_id;
// Determine skewed MousePos.x to support angled headers.
float mouse_skewed_x = g.IO.MousePos.x;
if (table->AngledHeadersHeight > 0.0f)
if (g.IO.MousePos.y >= table->OuterRect.Min.y && g.IO.MousePos.y <= table->OuterRect.Min.y + table->AngledHeadersHeight)
mouse_skewed_x += ImTrunc((table->OuterRect.Min.y + table->AngledHeadersHeight - g.IO.MousePos.y) * table->AngledHeadersSlope);
// [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column // [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column
// Process columns in their visible orders as we are comparing the visible order and adjusting host_clip_rect while looping. // Process columns in their visible orders as we are comparing the visible order and adjusting host_clip_rect while looping.
int visible_n = 0; int visible_n = 0;
@ -1025,7 +1032,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
} }
// Detect hovered column // Detect hovered column
if (is_hovering_table && g.IO.MousePos.x >= column->ClipRect.Min.x && g.IO.MousePos.x < column->ClipRect.Max.x) if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x)
table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n; table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;
// Lock start position // Lock start position
@ -1124,7 +1131,7 @@ 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 (mouse_skewed_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;
@ -1211,7 +1218,7 @@ void ImGui::TableUpdateBorders(ImGuiTable* table)
// Actual columns highlight/render will be performed in EndTable() and not be affected. // Actual columns highlight/render will be performed in EndTable() and not be affected.
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
const float hit_half_width = TABLE_RESIZE_SEPARATOR_HALF_THICKNESS; const float hit_half_width = TABLE_RESIZE_SEPARATOR_HALF_THICKNESS;
const float hit_y1 = table->OuterRect.Min.y; const float hit_y1 = (table->FreezeRowsCount >= 1 ? table->OuterRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight;
const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight); const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight);
const float hit_y2_head = hit_y1 + table_instance->LastTopHeadersRowHeight; const float hit_y2_head = hit_y1 + table_instance->LastTopHeadersRowHeight;
@ -1316,7 +1323,7 @@ void ImGui::EndTable()
max_pos_x = ImMax(max_pos_x, table->Columns[table->RightMostEnabledColumn].WorkMaxX + table->CellPaddingX + table->OuterPaddingX - outer_padding_for_border); max_pos_x = ImMax(max_pos_x, table->Columns[table->RightMostEnabledColumn].WorkMaxX + table->CellPaddingX + table->OuterPaddingX - outer_padding_for_border);
if (table->ResizedColumn != -1) if (table->ResizedColumn != -1)
max_pos_x = ImMax(max_pos_x, table->ResizeLockMinContentsX2); max_pos_x = ImMax(max_pos_x, table->ResizeLockMinContentsX2);
table->InnerWindow->DC.CursorMaxPos.x = max_pos_x; table->InnerWindow->DC.CursorMaxPos.x = max_pos_x + table->TempData->AngledheadersExtraWidth;
} }
// Pop clipping rect // Pop clipping rect
@ -1434,7 +1441,7 @@ void ImGui::EndTable()
} }
else if (temp_data->UserOuterSize.x <= 0.0f) else if (temp_data->UserOuterSize.x <= 0.0f)
{ {
const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.x : 0.0f; const float decoration_size = table->TempData->AngledheadersExtraWidth + ((table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.x : 0.0f);
outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - temp_data->UserOuterSize.x); outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - temp_data->UserOuterSize.x);
outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth)); outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth));
} }
@ -1501,6 +1508,11 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0 && init_width_or_weight > 0.0f) if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0 && init_width_or_weight > 0.0f)
if ((table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedFit || (table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame) if ((table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedFit || (table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame)
flags |= ImGuiTableColumnFlags_WidthFixed; flags |= ImGuiTableColumnFlags_WidthFixed;
if (flags & ImGuiTableColumnFlags_AngledHeader)
{
flags |= ImGuiTableColumnFlags_NoHeaderLabel;
table->AngledHeadersCount++;
}
TableSetupColumnFlags(table, column, flags); TableSetupColumnFlags(table, column, flags);
column->UserID = user_id; column->UserID = user_id;
@ -2623,7 +2635,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
// Draw inner border and resizing feedback // Draw inner border and resizing feedback
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
const float border_size = TABLE_BORDER_SIZE; const float border_size = TABLE_BORDER_SIZE;
const float draw_y1 = table->InnerRect.Min.y + ((table->Flags & ImGuiTableFlags_BordersOuterH) ? 1.0f : 0.0f); const float draw_y1 = ImMax(table->InnerRect.Min.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight) + ((table->Flags & ImGuiTableFlags_BordersOuterH) ? 1.0f : 0.0f);
const float draw_y2_body = table->InnerRect.Max.y; const float draw_y2_body = table->InnerRect.Max.y;
const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastTopHeadersRowHeight) : draw_y1; const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastTopHeadersRowHeight) : draw_y1;
if (table->Flags & ImGuiTableFlags_BordersInnerV) if (table->Flags & ImGuiTableFlags_BordersInnerV)
@ -2896,6 +2908,8 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table)
// - TableGetHeaderRowHeight() [Internal] // - TableGetHeaderRowHeight() [Internal]
// - TableHeadersRow() // - TableHeadersRow()
// - TableHeader() // - TableHeader()
// - TableAngledHeadersRow()
// - TableAngledHeadersRowEx() [Internal]
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
float ImGui::TableGetHeaderRowHeight() float ImGui::TableGetHeaderRowHeight()
@ -2914,6 +2928,18 @@ float ImGui::TableGetHeaderRowHeight()
return row_height + g.Style.CellPadding.y * 2.0f; return row_height + g.Style.CellPadding.y * 2.0f;
} }
float ImGui::TableGetHeaderAngledMaxLabelWidth()
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
float width = 0.0f;
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))
if (table->Columns[column_n].Flags & ImGuiTableColumnFlags_AngledHeader)
width = ImMax(width, CalcTextSize(TableGetColumnName(table, column_n), NULL, true).x);
return width + g.Style.CellPadding.x * 2.0f;
}
// [Public] This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn(). // [Public] 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 // 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. // and can create their own! For example: TableHeader() may be preceeded by Checkbox() or other custom widgets.
@ -3098,6 +3124,115 @@ void ImGui::TableHeader(const char* label)
TableOpenContextMenu(column_n); TableOpenContextMenu(column_n);
} }
// Unlike TableHeadersRow() it is not expected that you can reimplement or customize this with custom widgets.
// FIXME: highlight without ImGuiTableFlags_HighlightHoveredColumn
// FIXME: No hit-testing/button on the angled header.
void ImGui::TableAngledHeadersRow()
{
ImGuiContext& g = *GImGui;
TableAngledHeadersRowEx(g.Style.TableAngledHeadersAngle, 0.0f);
}
void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
ImGuiWindow* window = g.CurrentWindow;
ImDrawList* draw_list = window->DrawList;
IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!");
IM_ASSERT(table->CurrentRow == -1 && "Must be first row");
if (max_label_width == 0.0f)
max_label_width = TableGetHeaderAngledMaxLabelWidth();
// Angle argument expressed in (-IM_PI/2 .. +IM_PI/2) as it is easier to think about for user.
const bool flip_label = (angle < 0.0f);
angle -= IM_PI * 0.5f;
const float cos_a = ImCos(angle);
const float sin_a = ImSin(angle);
const float label_cos_a = flip_label ? ImCos(angle + IM_PI) : cos_a;
const float label_sin_a = flip_label ? ImSin(angle + IM_PI) : sin_a;
const ImVec2 unit_right = ImVec2(cos_a, sin_a);
// Calculate our base metrics and set angled headers data _before_ the first call to TableNextRow()
// FIXME-STYLE: Would it be better for user to submit 'max_label_width' or 'row_height' ? One can be derived from the other.
const float header_height = table->RowCellPaddingY * 2.0f + g.FontSize;
const float row_height = ImFabs(ImRotate(ImVec2(max_label_width, flip_label ? +header_height : -header_height), cos_a, sin_a).y);
const ImVec2 header_angled_vector = unit_right * (row_height / -sin_a);
table->AngledHeadersHeight = row_height;
table->AngledHeadersSlope = (sin_a != 0.0f) ? (cos_a / sin_a) : 0.0f;
// Declare row, override and draw our own background
TableNextRow(ImGuiTableRowFlags_Headers, row_height);
TableNextColumn();
table->DrawSplitter->SetCurrentChannel(draw_list, TABLE_DRAW_CHANNEL_BG0);
PushClipRect(table->BgClipRect.Min, table->BgClipRect.Max, false); // Span all columns
TableSetBgColor(ImGuiTableBgTarget_RowBg0, 0); // Cancel
draw_list->AddRectFilled(table->BgClipRect.Min, table->BgClipRect.Max, GetColorU32(ImGuiCol_TableHeaderBg, 0.25f)); // FIXME-STYLE: Change row background with an arbitrary color.
const ImRect row_r(table->WorkRect.Min.x, table->BgClipRect.Min.y, table->WorkRect.Max.x, window->DC.CursorPos.y + row_height);
const ImGuiID row_id = GetID("##AngledHeaders");
ButtonBehavior(row_r, row_id, NULL, NULL);
KeepAliveID(row_id);
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
int highlight_column_n = table->HighlightColumnHeader;
if (highlight_column_n == -1 && table->HoveredColumnBody != -1)
if (table_instance->HoveredRowLast == 0 && table->HoveredColumnBorder == -1 && (g.ActiveId == 0 || g.ActiveId == row_id || (table->IsActiveIdInTable || g.DragDropActive)))
highlight_column_n = table->HoveredColumnBody;
float max_x = 0.0f;
for (int pass = 0; pass < 2; pass++)
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
{
if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))
continue;
const int column_n = table->DisplayOrderToIndex[order_n];
ImGuiTableColumn* column = &table->Columns[column_n];
if ((column->Flags & ImGuiTableColumnFlags_AngledHeader) == 0) // Note: can't rely on ImGuiTableColumnFlags_IsVisible test here.
continue;
ImVec2 bg_shape[4];
bg_shape[0] = ImVec2(column->MaxX, row_r.Max.y);
bg_shape[1] = ImVec2(column->MinX, row_r.Max.y);
bg_shape[2] = bg_shape[1] + header_angled_vector;
bg_shape[3] = bg_shape[0] + header_angled_vector;
if (pass == 0)
{
// Draw shape
draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_TableHeaderBg));
if (column_n == highlight_column_n)
draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_Header)); // Highlight on hover
//draw_list->AddQuad(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_TableBorderLight), 1.0f);
max_x = ImMax(max_x, bg_shape[3].x);
// Draw label (first draw at an offset where RenderTextXXX() function won't meddle with applying current ClipRect, then transform to final offset)
// FIXME: May be worth tidying up all those operations to make them easier to understand.
const char* label_name = TableGetColumnName(table, column_n);
const float clip_width = max_label_width - (sin_a * table->RowCellPaddingY);
ImRect label_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width + (flip_label ? 0.0f : table->CellPaddingX), header_height + table->RowCellPaddingY));
ImVec2 label_size = CalcTextSize(label_name, NULL, true);
ImVec2 label_off = ImVec2(flip_label ? ImMax(0.0f, max_label_width - label_size.x - table->CellPaddingX) : table->CellPaddingX, table->RowCellPaddingY);
int vtx_idx_begin = draw_list->_VtxCurrentIdx;
RenderTextEllipsis(draw_list, label_r.Min + label_off, label_r.Max, label_r.Max.x, label_r.Max.x, label_name, NULL, &label_size);
//if (g.IO.KeyShift) { draw_list->AddRect(label_r.Min, label_r.Max, IM_COL32(0, 255, 0, 255), 0.0f, 0, 2.0f); }
int vtx_idx_end = draw_list->_VtxCurrentIdx;
// Rotate and offset label
ImVec2 pivot_in = label_r.GetBL();
ImVec2 pivot_out = ImVec2(column->WorkMinX, row_r.Max.y) + (flip_label ? (unit_right * clip_width) : ImVec2(header_height, 0.0f));
ShadeVertsTransformPos(draw_list, vtx_idx_begin, vtx_idx_end, pivot_in, label_cos_a, label_sin_a, pivot_out); // Rotate and offset
}
if (pass == 1)
{
// Draw border
draw_list->AddLine(bg_shape[0], bg_shape[3], TableGetColumnBorderCol(table, order_n, column_n));
}
}
PopClipRect();
table->TempData->AngledheadersExtraWidth = ImMax(0.0f, max_x - table->Columns[table->RightMostEnabledColumn].MaxX);
}
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// [SECTION] Tables: Context Menu // [SECTION] Tables: Context Menu
//------------------------------------------------------------------------- //-------------------------------------------------------------------------