From f2df804fcc71a042364780f5450de17896e83274 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 13 Dec 2020 19:19:23 +0100 Subject: [PATCH 01/18] Tables: four small fixes. Fixed last item flags leaking to disabled column, affecting IsItemHovered(). (#3651). Validate and fix invalid DisplayOrder data from ini file. Allow TableHeaderRows() to function will missing TableSetupColumn() calls. Made TableHeader() use AllowItemOverlap mode to allow submit subsequent item in same cell, since it covers the whole cell area. --- imgui.cpp | 1 + imgui_tables.cpp | 24 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9812f340..a6b314e8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4749,6 +4749,7 @@ bool ImGui::IsItemEdited() } // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority. +// FIXME: Although this is exposed, its interaction and ideal idiom with using ImGuiButtonFlags_AllowItemOverlap flag are extremely confusing, need rework. void ImGui::SetItemAllowOverlap() { ImGuiContext& g = *GImGui; diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 7a488b9a..10736be3 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1369,7 +1369,8 @@ const char* ImGui::TableGetColumnName(int column_n) const char* ImGui::TableGetColumnName(const ImGuiTable* table, int column_n) { - IM_ASSERT(table->IsLayoutLocked == true || column_n <= table->DeclColumnsCount); // NameOffset is invalid otherwise + if (table->IsLayoutLocked == false && column_n >= table->DeclColumnsCount) + return ""; // NameOffset is invalid at this point const ImGuiTableColumn* column = &table->Columns[column_n]; if (column->NameOffset == -1) return ""; @@ -1762,7 +1763,6 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n) window->DC.CursorMaxPos.x = window->DC.CursorPos.x; window->DC.ColumnsOffset.x = start_x - window->Pos.x - window->DC.Indent.x; // FIXME-WORKRECT window->DC.CurrLineTextBaseOffset = table->RowTextBaseline; - window->DC.LastItemId = 0; window->DC.NavLayerCurrent = (ImGuiNavLayer)column->NavLayerCurrent; window->WorkRect.Min.y = window->DC.CursorPos.y; @@ -1775,6 +1775,12 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n) window->DC.CursorPos.y = ImMax(window->DC.CursorPos.y, table->RowPosY2); window->SkipItems = column->IsSkipItems; + if (column->IsSkipItems) + { + window->DC.LastItemId = 0; + window->DC.LastItemStatusFlags = 0; + } + if (table->Flags & ImGuiTableFlags_NoClip) { // FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed. @@ -2638,8 +2644,10 @@ void ImGui::TableHeader(const char* label) //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] + // Using AllowItemOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items. bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None); + bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowItemOverlap); + SetItemAllowOverlap(); if (hovered || selected) { const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); @@ -3025,6 +3033,7 @@ void ImGui::TableLoadSettings(ImGuiTable* table) // Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings(); + ImU64 display_order_mask = 0; for (int data_n = 0; data_n < settings->ColumnsCount; data_n++, column_settings++) { int column_n = column_settings->Index; @@ -3044,12 +3053,19 @@ void ImGui::TableLoadSettings(ImGuiTable* table) column->DisplayOrder = column_settings->DisplayOrder; else column->DisplayOrder = (ImGuiTableColumnIdx)column_n; + display_order_mask |= (ImU64)1 << column->DisplayOrder; column->IsEnabled = column->IsEnabledNextFrame = column_settings->IsEnabled; column->SortOrder = column_settings->SortOrder; column->SortDirection = column_settings->SortDirection; } - // FIXME-TABLE: Need to validate .ini data + // Validate and fix invalid display order data + const ImU64 expected_display_order_mask = (settings->ColumnsCount == 64) ? ~0 : ((ImU64)1 << settings->ColumnsCount) - 1; + if (display_order_mask != expected_display_order_mask) + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + table->Columns[column_n].DisplayOrder = (ImGuiTableColumnIdx)column_n; + + // Rebuild index for (int column_n = 0; column_n < table->ColumnsCount; column_n++) table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)column_n; } From bd899efbd0bbc2c414ea39624e56154668063fc9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 14 Dec 2020 11:59:01 +0100 Subject: [PATCH 02/18] Tables: fixed "resize to default" of multiple stretch column (added 3b3503e, broken 7a61f340). Fixed a warning. Storing RightMostStretchedColumn column for resizing code. Avoid clearing RightMostEnabledColumn in BeginTable() so resizing code can potentially use it. (Added regression tests for resize all to default imgui_dev) --- imgui_demo.cpp | 2 +- imgui_internal.h | 3 ++- imgui_tables.cpp | 31 ++++++++++++++++++------------- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index bd232b78..bac21592 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4458,7 +4458,7 @@ static void ShowDemoWindowTables() for (int column = 0; column < COLUMNS_COUNT; column++) { ImGui::TableSetColumnIndex(column); - ImGui::Text("Cell %d,%d", 0, row); + ImGui::Text("Cell %d,%d", column, row); } } ImGui::EndTable(); diff --git a/imgui_internal.h b/imgui_internal.h index ab0578a7..295226c1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2047,8 +2047,9 @@ struct ImGuiTable ImGuiTableColumnIdx HeldHeaderColumn; // Index of column header being held. ImGuiTableColumnIdx ReorderColumn; // Index of column being reordered. (not cleared) ImGuiTableColumnIdx ReorderColumnDir; // -1 or +1 + ImGuiTableColumnIdx LeftMostStretchedColumn; // Index of left-most stretched column. + ImGuiTableColumnIdx RightMostStretchedColumn; // Index of right-most stretched column. ImGuiTableColumnIdx RightMostEnabledColumn; // Index of right-most non-hidden column. - ImGuiTableColumnIdx LeftMostStretchedColumnDisplayOrder; // Display order of left-most stretched column. ImGuiTableColumnIdx ContextPopupColumn; // Column right-clicked on, of -1 if opening context menu from a neutral/empty spot ImGuiTableColumnIdx FreezeRowsRequest; // Requested frozen rows count ImGuiTableColumnIdx FreezeRowsCount; // Actual frozen row count (== FreezeRowsRequest, or == 0 when no scrolling offset) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 10736be3..6d9530a4 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -369,7 +369,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->FreezeColumnsRequest = table->FreezeColumnsCount = 0; table->IsUnfrozen = true; table->DeclColumnsCount = 0; - table->RightMostEnabledColumn = -1; // Using opaque colors facilitate overlapping elements of the grid table->BorderColorStrong = GetColorU32(ImGuiCol_TableBorderStrong); @@ -709,7 +708,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) float sum_weights_stretched = 0.0f; // Sum of all weights for weighted columns. float sum_width_fixed_requests = 0.0f; // Sum of all width for fixed and auto-resize columns, excluding width contributed by Stretch columns. float max_width_auto = 0.0f; // Largest auto-width (used for SameWidths feature) - table->LeftMostStretchedColumnDisplayOrder = -1; + table->LeftMostStretchedColumn = table->RightMostStretchedColumn = -1; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { if (!(table->EnabledMaskByIndex & ((ImU64)1 << column_n))) @@ -760,11 +759,16 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) else { IM_ASSERT(column->Flags & ImGuiTableColumnFlags_WidthStretch); - if (column->StretchWeight < 0.0f) - column->StretchWeight = 1.0f; + + // Revert or initialize weight (when column->StretchWeight < 0.0f normally it means there has been no init value so it'll always default to 1.0f) + if (column->AutoFitQueue != 0x00 || column->StretchWeight < 0.0f) + column->StretchWeight = (column->InitStretchWeightOrWidth > 0.0f) ? column->InitStretchWeightOrWidth : 1.0f; + sum_weights_stretched += column->StretchWeight; - if (table->LeftMostStretchedColumnDisplayOrder == -1 || table->LeftMostStretchedColumnDisplayOrder > column->DisplayOrder) - table->LeftMostStretchedColumnDisplayOrder = column->DisplayOrder; + if (table->LeftMostStretchedColumn == -1 || table->Columns[table->LeftMostStretchedColumn].DisplayOrder > column->DisplayOrder) + table->LeftMostStretchedColumn = (ImGuiTableColumnIdx)column_n; + if (table->RightMostStretchedColumn == -1 || table->Columns[table->RightMostStretchedColumn].DisplayOrder < column->DisplayOrder) + table->RightMostStretchedColumn = (ImGuiTableColumnIdx)column_n; } max_width_auto = ImMax(max_width_auto, column->WidthAuto); sum_width_fixed_requests += table->CellPaddingX * 2.0f; @@ -831,8 +835,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) } // [Resize Rule 1] The right-most Visible column is not resizable if there is at least one Stretch column - // (see comments in TableResizeColumn()) - if (column->NextEnabledColumn == -1 && table->LeftMostStretchedColumnDisplayOrder != -1) + // See additional comments in TableSetColumnWidth(). + if (column->NextEnabledColumn == -1 && table->LeftMostStretchedColumn != -1) column->Flags |= ImGuiTableColumnFlags_NoDirectResize_; // Assign final width, record width in case we will need to shrink @@ -1885,7 +1889,7 @@ void ImGui::TableSetColumnWidth(int column_n, float width) // [Resize Rule 3] If we are are followed by a fixed column and we have a Stretch column before, we need to ensure // that our left border won't move, which we can do by making sure column_a/column_b resizes cancels each others. if (column_1 && (column_1->Flags & ImGuiTableColumnFlags_WidthFixed)) - if (table->LeftMostStretchedColumnDisplayOrder != -1 && table->LeftMostStretchedColumnDisplayOrder < column_0->DisplayOrder) + if (table->LeftMostStretchedColumn != -1 && table->Columns[table->LeftMostStretchedColumn].DisplayOrder < column_0->DisplayOrder) { // (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b) float column_1_width = ImMax(column_1->WidthRequest - (column_0_width - column_0->WidthRequest), min_width); @@ -1933,9 +1937,10 @@ void ImGui::TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n) if (!column->IsEnabled) return; column->CannotSkipItemsQueue = (1 << 0); - column->AutoFitQueue = (1 << 1); if (column->Flags & ImGuiTableColumnFlags_WidthStretch) table->AutoFitSingleStretchColumn = (ImGuiTableColumnIdx)column_n; + else + column->AutoFitQueue = (1 << 1); } void ImGui::TableSetColumnWidthAutoAll(ImGuiTable* table) @@ -1952,7 +1957,7 @@ void ImGui::TableSetColumnWidthAutoAll(ImGuiTable* table) void ImGui::TableUpdateColumnsWeightFromWidth(ImGuiTable* table) { - IM_ASSERT(table->LeftMostStretchedColumnDisplayOrder != -1); + IM_ASSERT(table->LeftMostStretchedColumn != -1 && table->RightMostStretchedColumn != -1); // Measure existing quantity float visible_weight = 0.0f; @@ -2506,7 +2511,7 @@ void ImGui::TableSortSpecsSanitize(ImGuiTable* table) { sort_order_count = 1; column->SortOrder = 0; - column->SortDirection = TableGetColumnAvailSortDirection(column, 0); + column->SortDirection = (ImU8)TableGetColumnAvailSortDirection(column, 0); break; } } @@ -2792,7 +2797,7 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table) if (column != NULL) { const bool can_resize = !(column->Flags & ImGuiTableColumnFlags_NoResize) && column->IsEnabled; - if (MenuItem("Size column to fit", NULL, false, can_resize)) + if (MenuItem("Size column to fit###SizeOne", NULL, false, can_resize)) TableSetColumnWidthAutoSingle(table, column_n); } From 892b48e2d61f1ee216ea852ad01e83e239385873 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 14 Dec 2020 19:24:48 +0100 Subject: [PATCH 03/18] Tables: Lock contents width while resizing down an horizontal scrolling table. Headers declare ideal width regardless of clipping. Misc comments. --- imgui.h | 6 ++--- imgui_internal.h | 1 + imgui_tables.cpp | 63 +++++++++++++++++++++++++++--------------------- 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/imgui.h b/imgui.h index 57b87ad6..bf836a75 100644 --- a/imgui.h +++ b/imgui.h @@ -60,7 +60,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.80 WIP" -#define IMGUI_VERSION_NUM 17906 +#define IMGUI_VERSION_NUM 17907 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -1037,8 +1037,8 @@ enum ImGuiTabItemFlags_ // When ScrollX is off: // - Table defaults to ImGuiTableFlags_ColumnsWidthStretch -> all Columns defaults to ImGuiTableColumnFlags_WidthStretch. // - Columns sizing policy allowed: Stretch (default) or Fixed/Auto. -// - Stretch Columns will share the width available in table. -// - Fixed Columns will generally obtain their requested width unless the Table cannot fit them all. +// - Fixed Columns will generally obtain their requested width (unless the Table cannot fit them all). +// - Stretch Columns will share the remaining width. // When ScrollX is on: // - Table defaults to ImGuiTableFlags_ColumnsWidthFixed -> all Columns defaults to ImGuiTableColumnFlags_WidthFixed. // - Columns sizing policy allowed: Fixed/Auto mostly! diff --git a/imgui_internal.h b/imgui_internal.h index 295226c1..cd10cfeb 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2012,6 +2012,7 @@ struct ImGuiTable float ColumnsTotalWidth; // Sum of current column width float ColumnsAutoFitWidth; // Sum of ideal column width in order nothing to be clipped, used for auto-fitting and content width submission in outer window 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 RefScale; // Reference scale to be able to rescale columns on font/dpi changes. ImRect OuterRect; // Note: OuterRect.Max.y is often FLT_MAX until EndTable(), unless a height has been specified in BeginTable(). ImRect WorkRect; diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 6d9530a4..261ebf0a 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -492,8 +492,7 @@ void ImGui::TableBeginApplyRequests(ImGuiTable* table) { // Handle resizing request // (We process this at the first TableBegin of the frame) - // FIXME-TABLE: Preserve contents width _while resizing down_ until releasing. - // FIXME-TABLE: Contains columns if our work area doesn't allow for scrolling. + // FIXME-TABLE: Contains columns if our work area doesn't allow for scrolling? if (table->InstanceCurrent == 0) { if (table->ResizedColumn != -1 && table->ResizedColumnNextWidth != FLT_MAX) @@ -691,6 +690,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) if ((table->Flags & ImGuiTableFlags_Sortable) && table->SortSpecsCount == 0 && !(table->Flags & ImGuiTableFlags_SortTristate)) table->IsSortSpecsDirty = true; table->RightMostEnabledColumn = (ImGuiTableColumnIdx)last_visible_column_idx; + IM_ASSERT(table->RightMostEnabledColumn >= 0); // [Part 2] Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible // to avoid the column fitting to wait until the first visible frame of the child container (may or not be a good thing). @@ -1014,9 +1014,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // because of using _WidthAutoResize/_WidthStretch). This will hide the resizing option from the context menu. if (is_hovering_table && table->HoveredColumnBody == -1) { - float unused_x1 = table->WorkRect.Min.x; - if (table->RightMostEnabledColumn != -1) - unused_x1 = ImMax(unused_x1, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x); + float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x); if (g.IO.MousePos.x >= unused_x1) table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount; } @@ -1109,6 +1107,8 @@ void ImGui::TableUpdateBorders(ImGuiTable* table) } if (held) { + if (table->LastResizedColumn == -1) + table->ResizeLockMinContentsX2 = table->RightMostEnabledColumn != -1 ? table->Columns[table->RightMostEnabledColumn].MaxX : -FLT_MAX; table->ResizedColumn = (ImGuiTableColumnIdx)column_n; table->InstanceInteracted = table->InstanceCurrent; } @@ -1182,6 +1182,8 @@ void ImGui::EndTable() float max_pos_x = backup_inner_max_pos_x; if (table->RightMostEnabledColumn != -1) max_pos_x = ImMax(max_pos_x, table->Columns[table->RightMostEnabledColumn].MaxX); + if (table->ResizedColumn != -1) + max_pos_x = ImMax(max_pos_x, table->ResizeLockMinContentsX2); #if 0 // Strip out dummy channel draw calls @@ -1861,7 +1863,12 @@ void ImGui::TableSetColumnWidth(int column_n, float width) ImGuiTableColumn* column_1 = (column_0->NextEnabledColumn != -1) ? &table->Columns[column_0->NextEnabledColumn] : NULL; - // In this surprisingly not simple because of how we support mixing Fixed and Stretch columns. + // In this surprisingly not simple because of how we support mixing Fixed and multiple Stretch columns. + // - All fixed: easy. + // - All stretch: easy. + // - One or more fixed + one stretch: easy. + // - One or more fixed + more than one stretch: A MESS + // When forwarding resize from Wn| to Fn+1| we need to be considerate of the _NoResize flag on Fn+1. // FIXME-TABLE: Find a way to rewrite all of this so interactions feel more consistent for the user. // Scenarios: @@ -2638,6 +2645,26 @@ void ImGui::TableHeader(const char* label) ImRect cell_r = TableGetCellBgRect(table, column_n); float label_height = ImMax(label_size.y, table->RowMinHeight - table->CellPaddingY * 2.0f); + // Calculate ideal size for sort order arrow + float w_arrow = 0.0f; + float w_sort_text = 0.0f; + char sort_order_suf[4] = ""; + const float ARROW_SCALE = 0.65f; + if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort)) + { + w_arrow = ImFloor(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x);// table->CellPadding.x); + 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; + } + } + + // 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); + // 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); @@ -2692,28 +2719,13 @@ void ImGui::TableHeader(const char* label) } // Sort order arrow - float w_arrow = 0.0f; - float w_sort_text = 0.0f; - float ellipsis_max = cell_r.Max.x; + const float ellipsis_max = cell_r.Max.x - w_arrow - w_sort_text; 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) { - char sort_order_suf[8]; - w_sort_text = 0.0f; - 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)); @@ -2721,7 +2733,7 @@ void ImGui::TableHeader(const char* label) PopStyleColor(); x += w_sort_text; } - RenderArrow(window->DrawList, ImVec2(x, y), col, column->SortDirection == ImGuiSortDirection_Ascending ? ImGuiDir_Up : ImGuiDir_Down, ARROW_SCALE); + RenderArrow(window->DrawList, ImVec2(x, y), GetColorU32(ImGuiCol_Text), column->SortDirection == ImGuiSortDirection_Ascending ? ImGuiDir_Up : ImGuiDir_Down, ARROW_SCALE); } // Handle clicking on column header to adjust Sort Order @@ -2741,11 +2753,6 @@ void ImGui::TableHeader(const char* label) 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); From 1fb26d18c42cf90d4fb3f99da453912cccb6c3d3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 16 Dec 2020 15:27:17 +0100 Subject: [PATCH 04/18] Tables: fixed seemingly unnecessarily copy of ImGuiTableColumnFlags_NoDirectResize_ which broken resizing from W3| in a F1 W3 F2 setup. Header only allow overlap on hover, not when active (amend f2df804f) Otherwise TableUpdateBorders() tends to override mouse cursor. --- imgui_tables.cpp | 30 +++++++++++------------------- imgui_widgets.cpp | 2 +- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 261ebf0a..7a9668f2 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -815,23 +815,12 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) continue; ImGuiTableColumn* column = &table->Columns[column_n]; - // Allocate width for stretched/weighted columns - if (column->Flags & ImGuiTableColumnFlags_WidthStretch) + // Allocate width for stretched/weighted columns (StretchWeight gets converted into WidthRequest) + if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) && !mixed_same_widths) { - // StretchWeight gets converted into WidthRequest - if (!mixed_same_widths) - { - float weight_ratio = column->StretchWeight / sum_weights_stretched; - column->WidthRequest = IM_FLOOR(ImMax(width_avail_for_stretched_columns * weight_ratio, min_column_width) + 0.01f); - width_remaining_for_stretched_columns -= column->WidthRequest; - } - - // [Resize Rule 2] Resizing from right-side of a stretch column preceding a fixed column - // needs to forward resizing to left-side of fixed column. We also need to copy the NoResize flag.. - if (column->NextEnabledColumn != -1) - if (ImGuiTableColumn* next_column = &table->Columns[column->NextEnabledColumn]) - if (next_column->Flags & ImGuiTableColumnFlags_WidthFixed) - column->Flags |= (next_column->Flags & ImGuiTableColumnFlags_NoDirectResize_); + float weight_ratio = column->StretchWeight / sum_weights_stretched; + column->WidthRequest = IM_FLOOR(ImMax(width_avail_for_stretched_columns * weight_ratio, min_column_width) + 0.01f); + width_remaining_for_stretched_columns -= column->WidthRequest; } // [Resize Rule 1] The right-most Visible column is not resizable if there is at least one Stretch column @@ -1876,15 +1865,17 @@ void ImGui::TableSetColumnWidth(int column_n, float width) // - F1 F2 F3 resize from F3| --> ok: alter ->WidthRequested of Fixed column. If active, ScrollX extent can be altered. // - F1 F2 W3 resize from F1| or F2| --> ok: alter ->WidthRequested of Fixed column. If active, ScrollX extent can be altered, but it doesn't make much sense as the Stretch column will always be minimal size. // - F1 F2 W3 resize from W3| --> ok: no-op (disabled by Resize Rule 1) - // - W1 W2 W3 resize from W1| or W2| --> FIXME + // - W1 W2 W3 resize from W1| or W2| --> ok // - W1 W2 W3 resize from W3| --> ok: no-op (disabled by Resize Rule 1) // - W1 F2 F3 resize from F3| --> ok: no-op (disabled by Resize Rule 1) // - W1 F2 resize from F2| --> ok: no-op (disabled by Resize Rule 1) // - W1 W2 F3 resize from W1| or W2| --> ok // - W1 F2 W3 resize from W1| or F2| --> FIXME // - F1 W2 F3 resize from W2| --> ok + // - F1 W3 F2 resize from W3| --> ok // - W1 F2 F3 resize from W1| --> ok: equivalent to resizing |F2. F3 will not move. (forwarded by Resize Rule 2) - // - W1 F2 F3 resize from F2| --> FIXME should resize F2, F3 and not have effect on W1 (Stretch columns are _before_ the Fixed column). + // - W1 F2 F3 resize from F2| --> ok + // All resizes from a Wx columns are locking other columns. // Rules: // - [Resize Rule 1] Can't resize from right of right-most visible column if there is any Stretch column. Implemented in TableUpdateLayout(). @@ -2679,7 +2670,8 @@ void ImGui::TableHeader(const char* label) // Using AllowItemOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items. bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowItemOverlap); - SetItemAllowOverlap(); + if (g.ActiveId != id) + SetItemAllowOverlap(); if (hovered || selected) { const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 8b67f0ef..da08b6ee 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7664,7 +7664,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, hovered |= (g.HoveredId == id); // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs to be hovered) - if (!held) + if (g.ActiveId != id) SetItemAllowOverlap(); // Drag and drop: re-order tabs From c4dbab8f5e16b5440fffea890f7a55c2fa2e3b29 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 16 Dec 2020 16:06:07 +0100 Subject: [PATCH 05/18] Tables: reset all width apply to hidden stretch columns. Comments. --- imgui_tables.cpp | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 7a9668f2..9c4a66c0 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1281,7 +1281,7 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo table->DeclColumnsCount++; // When passing a width automatically enforce WidthFixed policy - // (vs TableFixColumnFlags would default to WidthAutoResize) + // (whereas TableSetupColumnFlags would default to WidthAutoResize) if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0) if ((table->Flags & ImGuiTableFlags_ColumnsWidthFixed) && (init_width_or_weight > 0.0f)) flags |= ImGuiTableColumnFlags_WidthFixed; @@ -1856,7 +1856,8 @@ void ImGui::TableSetColumnWidth(int column_n, float width) // - All fixed: easy. // - All stretch: easy. // - One or more fixed + one stretch: easy. - // - One or more fixed + more than one stretch: A MESS + // - One or more fixed + more than one stretch: tricky. + // Qt when manual resize is enabled only support a single _trailing_ stretch column. // When forwarding resize from Wn| to Fn+1| we need to be considerate of the _NoResize flag on Fn+1. // FIXME-TABLE: Find a way to rewrite all of this so interactions feel more consistent for the user. @@ -1877,6 +1878,11 @@ void ImGui::TableSetColumnWidth(int column_n, float width) // - W1 F2 F3 resize from F2| --> ok // All resizes from a Wx columns are locking other columns. + // Possible improvements: + // - W1 W2 W3 resize W1| --> to not be stuck, both W2 and W3 would stretch down. Seems possible to fix. Would be most beneficial to simplify resize of all-weighted columns. + // - W1 F2 W3 resize W1| or F2| --> symmetrical resize is weird and glitchy. Seems possible to fix. + // - W3 F1 F2 resize W3| --> to not be stuck past F1|, both F1 and F2 would need to stretch down, which would be lossy or ambiguous. Seems hard to fix. + // Rules: // - [Resize Rule 1] Can't resize from right of right-most visible column if there is any Stretch column. Implemented in TableUpdateLayout(). // - [Resize Rule 2] Resizing from right-side of a Stretch column before a fixed column forward sizing to left-side of fixed column. @@ -1901,7 +1907,7 @@ void ImGui::TableSetColumnWidth(int column_n, float width) } else if (column_0->Flags & ImGuiTableColumnFlags_WidthStretch) { - // We can also use previous column if there's no next one + // We can also use previous column if there's no next one (this is used when doing an auto-fit on the right-most stretch column) if (column_1 == NULL) column_1 = (column_0->PrevEnabledColumn != -1) ? &table->Columns[column_0->PrevEnabledColumn] : NULL; if (column_1 == NULL) @@ -1916,6 +1922,7 @@ void ImGui::TableSetColumnWidth(int column_n, float width) } else { + // At this point column_1 is the next OR previous column and we know it is a stretch column. // (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b) float column_1_width = ImMax(column_1->WidthRequest - (column_0_width - column_0->WidthRequest), min_width); column_0_width = column_0->WidthRequest + column_1->WidthRequest - column_1_width; @@ -1946,7 +1953,7 @@ void ImGui::TableSetColumnWidthAutoAll(ImGuiTable* table) for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { ImGuiTableColumn* column = &table->Columns[column_n]; - if (!column->IsEnabled) + if (!column->IsEnabled && !(column->Flags & ImGuiTableColumnFlags_WidthStretch)) // Can reset weight of hidden stretch column continue; column->CannotSkipItemsQueue = (1 << 0); column->AutoFitQueue = (1 << 1); @@ -1977,7 +1984,8 @@ void ImGui::TableUpdateColumnsWeightFromWidth(ImGuiTable* table) ImGuiTableColumn* column = &table->Columns[column_n]; if (!column->IsEnabled || !(column->Flags & ImGuiTableColumnFlags_WidthStretch)) continue; - column->StretchWeight = ((column->WidthRequest + 0.0f) / visible_width) * visible_weight; + column->StretchWeight = (column->WidthRequest / visible_width) * visible_weight; + IM_ASSERT(column->StretchWeight > 0.0f); } } @@ -3276,6 +3284,10 @@ void ImGui::DebugNodeTable(ImGuiTable* table) BulletText("HoveredColumnBody: %d, HoveredColumnBorder: %d", table->HoveredColumnBody, table->HoveredColumnBorder); BulletText("ResizedColumn: %d, ReorderColumn: %d, HeldHeaderColumn: %d", table->ResizedColumn, table->ReorderColumn, table->HeldHeaderColumn); //BulletText("BgDrawChannels: %d/%d", 0, table->BgDrawChannelUnfrozen); + float sum_weights = 0.0f; + for (int n = 0; n < table->ColumnsCount; n++) + if (table->Columns[n].Flags & ImGuiTableColumnFlags_WidthStretch) + sum_weights += table->Columns[n].StretchWeight; for (int n = 0; n < table->ColumnsCount; n++) { ImGuiTableColumn* column = &table->Columns[n]; @@ -3283,13 +3295,13 @@ void ImGui::DebugNodeTable(ImGuiTable* table) ImFormatString(buf, IM_ARRAYSIZE(buf), "Column %d order %d name '%s': offset %+.2f to %+.2f\n" "Enabled: %d, VisibleX/Y: %d/%d, RequestOutput: %d, SkipItems: %d, DrawChannels: %d,%d\n" - "WidthGiven: %.1f, Request/Auto: %.1f/%.1f, StretchWeight: %.3f\n" + "WidthGiven: %.1f, Request/Auto: %.1f/%.1f, StretchWeight: %.3f (%.1f%%)\n" "MinX: %.1f, MaxX: %.1f (%+.1f), ClipRect: %.1f to %.1f (+%.1f)\n" "ContentWidth: %.1f,%.1f, HeadersUsed/Ideal %.1f/%.1f\n" "Sort: %d%s, UserID: 0x%08X, Flags: 0x%04X: %s%s%s%s..", n, column->DisplayOrder, name, column->MinX - table->WorkRect.Min.x, column->MaxX - table->WorkRect.Min.x, column->IsEnabled, column->IsVisibleX, column->IsVisibleY, column->IsRequestOutput, column->IsSkipItems, column->DrawChannelFrozen, column->DrawChannelUnfrozen, - column->WidthGiven, column->WidthRequest, column->WidthAuto, column->StretchWeight, + column->WidthGiven, column->WidthRequest, column->WidthAuto, column->StretchWeight, (column->StretchWeight / sum_weights) * 100.0f, column->MinX, column->MaxX, column->MaxX - column->MinX, column->ClipRect.Min.x, column->ClipRect.Max.x, column->ClipRect.Max.x - column->ClipRect.Min.x, column->ContentMaxXFrozen - column->WorkMinX, column->ContentMaxXUnfrozen - column->WorkMinX, column->ContentMaxXHeadersUsed - column->WorkMinX, column->ContentMaxXHeadersIdeal - column->WorkMinX, column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? " (Asc)" : (column->SortDirection == ImGuiSortDirection_Descending) ? " (Des)" : "", column->UserID, column->Flags, From ad83976b35c89ad3cbe9c6d8b7bf0a501457819d Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 17 Dec 2020 22:29:11 +0100 Subject: [PATCH 06/18] Tables: Added ImGuiTableFlags_NoHostExtendX (#3605) marked as WIP, will probably rename. Moved some code from BeginTable() to TableUpdateLayout() to late latch some of the required data. --- imgui.h | 23 ++++++++++++----------- imgui_demo.cpp | 26 ++++++++++++++++++++++++++ imgui_internal.h | 1 + imgui_tables.cpp | 48 ++++++++++++++++++++++++++++++------------------ 4 files changed, 69 insertions(+), 29 deletions(-) diff --git a/imgui.h b/imgui.h index bf836a75..c0d9dcf3 100644 --- a/imgui.h +++ b/imgui.h @@ -1075,20 +1075,21 @@ enum ImGuiTableFlags_ ImGuiTableFlags_ColumnsWidthFixed = 1 << 14, // Default if ScrollX is on. Columns will default to use _WidthFixed or _WidthAutoResize policy (if Resizable is off). Read description above for more details. ImGuiTableFlags_SameWidths = 1 << 15, // Make all columns the same widths which is useful with Fixed columns policy (but granted by default with Stretch policy + no resize). Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible and disable ImGuiTableFlags_Resizable. ImGuiTableFlags_NoHeadersWidth = 1 << 16, // Disable headers' contribution to automatic width calculation. - ImGuiTableFlags_NoHostExtendY = 1 << 17, // Disable extending past the limit set by outer_size.y, only meaningful when neither of ScrollX|ScrollY are set (data below the limit will be clipped and not visible) - ImGuiTableFlags_NoKeepColumnsVisible = 1 << 18, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. - ImGuiTableFlags_PreciseWidths = 1 << 19, // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth. - ImGuiTableFlags_NoClip = 1 << 20, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze(). + ImGuiTableFlags_NoHostExtendX = 1 << 17, // Disable extending table past the limit required by the right-most columns. Only meaningful with Fixed columns and when neither ScrollX nor ScrollY are set. + ImGuiTableFlags_NoHostExtendY = 1 << 18, // Disable extending table past the limit set by outer_size.y. Only meaningful when neither ScrollX nor ScrollY are set (data below the limit will be clipped and not visible) + ImGuiTableFlags_NoKeepColumnsVisible = 1 << 19, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. + ImGuiTableFlags_PreciseWidths = 1 << 20, // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth. + ImGuiTableFlags_NoClip = 1 << 21, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze(). // Padding - ImGuiTableFlags_PadOuterX = 1 << 21, // Default if BordersOuterV is on. Enable outer-most padding. - ImGuiTableFlags_NoPadOuterX = 1 << 22, // Default if BordersOuterV is off. Disable outer-most padding. - ImGuiTableFlags_NoPadInnerX = 1 << 23, // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off). + ImGuiTableFlags_PadOuterX = 1 << 22, // Default if BordersOuterV is on. Enable outer-most padding. + ImGuiTableFlags_NoPadOuterX = 1 << 23, // Default if BordersOuterV is off. Disable outer-most padding. + ImGuiTableFlags_NoPadInnerX = 1 << 24, // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off). // Scrolling - ImGuiTableFlags_ScrollX = 1 << 24, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this create a child window, ScrollY is currently generally recommended when using ScrollX. - ImGuiTableFlags_ScrollY = 1 << 25, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. + ImGuiTableFlags_ScrollX = 1 << 25, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this create a child window, ScrollY is currently generally recommended when using ScrollX. + ImGuiTableFlags_ScrollY = 1 << 26, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. // Sorting - ImGuiTableFlags_SortMulti = 1 << 26, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). - ImGuiTableFlags_SortTristate = 1 << 27 // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). + ImGuiTableFlags_SortMulti = 1 << 27, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). + ImGuiTableFlags_SortTristate = 1 << 28 // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). }; // Flags for ImGui::TableSetupColumn() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index bac21592..3d1773a3 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4221,6 +4221,7 @@ static void ShowDemoWindowTables() ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Outer size")) { + ImGui::Text("Using manual/explicit size:"); if (ImGui::BeginTable("##table1", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f))) { for (int row = 0; row < 5; row++) @@ -4248,6 +4249,30 @@ static void ShowDemoWindowTables() } ImGui::EndTable(); } + ImGui::Spacing(); + + // Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY + // FIXME-TABLE: WIP API: Don't use yet, will probably rework/rename those flags. + ImGui::Text("Using NoHostExtend flags:"); + static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_ColumnsWidthFixed; + ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX); + ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY); + if (ImGui::BeginTable("##table3", 3, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f))) + { + for (int row = 0; row < 10; row++) + { + ImGui::TableNextRow(); + for (int column = 0; column < 3; column++) + { + ImGui::TableNextColumn(); + ImGui::Text("Cell %d,%d", column, row); + } + } + ImGui::EndTable(); + } + ImGui::SameLine(); + ImGui::Text("Hello!"); + ImGui::TreePop(); } @@ -4715,6 +4740,7 @@ static void ShowDemoWindowTables() flags &= ~ImGuiTableFlags_ColumnsWidthStretch; // Can't specify both sizing polices so we clear the other ImGui::SameLine(); HelpMarker("[Default if ScrollX is on]\nEnlarge as needed: enable scrollbar if ScrollX is enabled, otherwise extend parent window's contents rectangle. Only Fixed columns allowed. Stretched columns will calculate their width assuming no scrolling."); ImGui::CheckboxFlags("ImGuiTableFlags_NoHeadersWidth", &flags, ImGuiTableFlags_NoHeadersWidth); + ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX); ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY); ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags, ImGuiTableFlags_NoKeepColumnsVisible); ImGui::SameLine(); HelpMarker("Only available if ScrollX is disabled."); diff --git a/imgui_internal.h b/imgui_internal.h index cd10cfeb..4ccfbf17 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2072,6 +2072,7 @@ struct ImGuiTable bool IsResetAllRequest; bool IsResetDisplayOrderRequest; bool IsUnfrozen; // Set when we got past the frozen row. + bool IsOuterRectFitX; // Set when outer_size value passed to BeginTable() is (>= -1.0f && <= 0.0f) bool MemoryCompacted; bool HostSkipItems; // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 9c4a66c0..0052c563 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -38,7 +38,7 @@ Index of this file: // - TableSetupColumn() user submit columns details (optional) // - TableSetupScrollFreeze() user submit scroll freeze information (optional) //----------------------------------------------------------------------------- -// - TableUpdateLayout() [Internal] setup everything, lock all widths, columns positions, clipping rectangles. Automatically called by the FIRST call to TableNextRow() or TableHeadersRow(). +// - TableUpdateLayout() [Internal] followup to BeginTable(): setup everything: widths, columns positions, clipping rectangles. Automatically called by the FIRST call to TableNextRow() or TableHeadersRow(). // | TableSetupDrawChannels() - setup ImDrawList channels // | TableUpdateBorders() - detect hovering columns for resize, ahead of contents submission // | TableDrawContextMenu() - draw right-click context menu @@ -280,6 +280,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->InnerWidth = inner_width; table->OuterRect = outer_rect; table->WorkRect = outer_rect; + table->IsOuterRectFitX = (outer_size.x >= -1.0f && outer_size.x <= 0.0f); // Bit ambiguous // When not using a child window, WorkRect.Max will grow as we append contents. if (use_child_window) @@ -325,7 +326,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos; table->HostBackupItemWidth = outer_window->DC.ItemWidth; table->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; - inner_window->ParentWorkRect = table->WorkRect; inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); // Padding and Spacing @@ -356,13 +356,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->InnerClipRect.ClipWithFull(table->HostClipRect); table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : inner_window->ClipRect.Max.y; - // Initial draw cmd starts with a BgClipRect that matches the one of its host, to facilitate merge draw commands by default. - // This is because all our cell highlight are manually clipped with BgClipRect - // Larger at first, if/after unfreezing will become same as tight - table->BgClipRect = table->InnerClipRect; - table->BgClipRectForDrawCmd = table->HostClipRect; - IM_ASSERT(table->BgClipRect.Min.y <= table->BgClipRect.Max.y); - table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow() table->FreezeRowsRequest = table->FreezeRowsCount = 0; // This will be setup by TableSetupScrollFreeze(), if any @@ -373,8 +366,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // Using opaque colors facilitate overlapping elements of the grid table->BorderColorStrong = GetColorU32(ImGuiCol_TableBorderStrong); table->BorderColorLight = GetColorU32(ImGuiCol_TableBorderLight); - table->BorderX1 = table->InnerClipRect.Min.x;// +((table->Flags & ImGuiTableFlags_BordersOuter) ? 0.0f : -1.0f); - table->BorderX2 = table->InnerClipRect.Max.x;// +((table->Flags & ImGuiTableFlags_BordersOuter) ? 0.0f : +1.0f); // Make table current const int table_idx = g.Tables.GetIndex(table); @@ -1001,26 +992,39 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // [Part 8] Detect/store when we are hovering the unused space after the right-most column (so e.g. context menus can react on it) // Clear Resizable flag if none of our column are actually resizable (either via an explicit _NoResize flag, either // because of using _WidthAutoResize/_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); if (is_hovering_table && table->HoveredColumnBody == -1) { - float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x); if (g.IO.MousePos.x >= unused_x1) table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount; } if (count_resizable == 0 && (table->Flags & ImGuiTableFlags_Resizable)) table->Flags &= ~ImGuiTableFlags_Resizable; - // [Part 9] Allocate draw channels + // [Part 9] Lock actual OuterRect/WorkRect right-most position. + // This is done late to handle the case of fixed-columns tables not claiming more widths that they need. + // Because of this we are careful with uses of WorkRect and InnerClipRect before this point. + if ((table->Flags & ImGuiTableFlags_NoHostExtendX) && table->InnerWindow == table->OuterWindow && table->RightMostStretchedColumn == -1) + { + table->OuterRect.Max.x = table->WorkRect.Max.x = unused_x1; + table->InnerClipRect.Max.x = ImMin(table->InnerClipRect.Max.x, unused_x1); + table->IsOuterRectFitX = false; + } + table->InnerWindow->ParentWorkRect = table->WorkRect; + table->BorderX1 = table->InnerClipRect.Min.x;// +((table->Flags & ImGuiTableFlags_BordersOuter) ? 0.0f : -1.0f); + table->BorderX2 = table->InnerClipRect.Max.x;// +((table->Flags & ImGuiTableFlags_BordersOuter) ? 0.0f : +1.0f); + + // [Part 10] Allocate draw channels and setup background cliprect TableSetupDrawChannels(table); - // [Part 10] Hit testing on borders + // [Part 11] Hit testing on borders if (table->Flags & ImGuiTableFlags_Resizable) TableUpdateBorders(table); table->LastFirstRowHeight = 0.0f; table->IsLayoutLocked = true; table->IsUsingHeaders = false; - // [Part 11] Context menu + // [Part 12] Context menu if (table->IsContextPopupOpen && table->InstanceCurrent == table->InstanceInteracted) { const ImGuiID context_menu_id = ImHashStr("##ContextMenu", 0, table->ID); @@ -1227,14 +1231,14 @@ void ImGui::EndTable() outer_window->DC.ItemWidth = table->HostBackupItemWidth; outer_window->DC.ItemWidthStack.Size = table->HostBackupItemWidthStackSize; outer_window->DC.ColumnsOffset = table->HostBackupColumnsOffset; + const float outer_width = table->IsOuterRectFitX ? table->ColumnsAutoFitWidth : table->WorkRect.GetWidth(); if (inner_window != outer_window) { EndChild(); } else { - ImVec2 item_size = table->OuterRect.GetSize(); - item_size.x = table->ColumnsTotalWidth; + ImVec2 item_size(outer_width, table->OuterRect.GetHeight()); ItemSize(item_size); } @@ -1247,7 +1251,8 @@ void ImGui::EndTable() } else { - outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos_x, table->WorkRect.Min.x + table->ColumnsAutoFitWidth); // For auto-fit + outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos_x, table->WorkRect.Min.x + outer_width); // For auto-fit + outer_window->DC.CursorPosPrevLine.x = table->WorkRect.Max.x; // For consistent reaction to SameLine() // FIXME: Should be a feature of layout/ItemAdd } // Save settings @@ -2068,6 +2073,13 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table) } column->DrawChannelCurrent = column->DrawChannelFrozen; } + + // Initial draw cmd starts with a BgClipRect that matches the one of its host, to facilitate merge draw commands by default. + // All our cell highlight are manually clipped with BgClipRect. When unfreezing it will be made smaller to fit scrolling rect. + // (This technically isn't part of setting up draw channels, but is reasonably related to be done here) + table->BgClipRect = table->InnerClipRect; + table->BgClipRectForDrawCmd = table->HostClipRect; + IM_ASSERT(table->BgClipRect.Min.y <= table->BgClipRect.Max.y); } // This function reorder draw channels based on matching clip rectangle, to facilitate merging them. Called by EndTable(). From 30468829c2a01d5f8610c517ef1defc152e72d6d Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 18 Dec 2020 12:18:34 +0100 Subject: [PATCH 07/18] Tables: Internal: Maintain InnerRect to further clarify some code. Renamed Bg1 fields to Bg2 (used by Selectable) as the other handles Bg0+Bg1. --- imgui.cpp | 5 +++-- imgui_demo.cpp | 4 ++-- imgui_internal.h | 9 ++++---- imgui_tables.cpp | 56 +++++++++++++++++++++++++++--------------------- 4 files changed, 42 insertions(+), 32 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a6b314e8..322a5162 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10512,8 +10512,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Debugging enums enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentRegionRect" }; - enum { TRT_OuterRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentFrozen, TRT_ColumnsContentUnfrozen, TRT_Count }; // Tables Rect Type - const char* trt_rects_names[TRT_Count] = { "OuterRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" }; + enum { TRT_OuterRect, TRT_InnerRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentFrozen, TRT_ColumnsContentUnfrozen, TRT_Count }; // Tables Rect Type + const char* trt_rects_names[TRT_Count] = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" }; if (cfg->ShowWindowsRectsType < 0) cfg->ShowWindowsRectsType = WRT_WorkRect; if (cfg->ShowTablesRectsType < 0) @@ -10524,6 +10524,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n) { if (rect_type == TRT_OuterRect) { return table->OuterRect; } + else if (rect_type == TRT_InnerRect) { return table->InnerRect; } else if (rect_type == TRT_WorkRect) { return table->WorkRect; } else if (rect_type == TRT_HostClipRect) { return table->HostClipRect; } else if (rect_type == TRT_InnerClipRect) { return table->InnerClipRect; } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3d1773a3..b4fd22d3 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4687,11 +4687,11 @@ static void ShowDemoWindowTables() | ImGuiTableFlags_ColumnsWidthFixed; enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable, CT_SelectableSpanRow }; - static int contents_type = CT_Button; + static int contents_type = CT_SelectableSpanRow; const char* contents_type_names[] = { "Text", "Button", "SmallButton", "FillButton", "Selectable", "Selectable (span row)" }; static int freeze_cols = 1; static int freeze_rows = 1; - static int items_count = IM_ARRAYSIZE(template_items_names); + static int items_count = IM_ARRAYSIZE(template_items_names) * 2; static ImVec2 outer_size_value = ImVec2(0, TEXT_BASE_HEIGHT * 12); static float row_min_height = 0.0f; // Auto static float inner_width_with_scroll = 0.0f; // Auto-extend diff --git a/imgui_internal.h b/imgui_internal.h index 4ccfbf17..6aece7cc 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2014,7 +2014,8 @@ struct ImGuiTable 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 RefScale; // Reference scale to be able to rescale columns on font/dpi changes. - ImRect OuterRect; // Note: 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 WorkRect; ImRect InnerClipRect; ImRect BgClipRect; // We use this to cpu-clip cell background color fill @@ -2022,7 +2023,7 @@ struct ImGuiTable ImRect HostClipRect; // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window. ImRect HostBackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable() ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable() - ImRect HostBackupClipRect; // Backup of InnerWindow->ClipRect during PushTableBackground()/PopTableBackground() + ImRect HostBackupInnerClipRect; // Backup of InnerWindow->ClipRect during PushTableBackground()/PopTableBackground() ImVec2 HostBackupPrevLineSize; // Backup of InnerWindow->DC.PrevLineSize at the end of BeginTable() ImVec2 HostBackupCurrLineSize; // Backup of InnerWindow->DC.CurrLineSize at the end of BeginTable() ImVec2 HostBackupCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable() @@ -2058,8 +2059,8 @@ struct ImGuiTable ImGuiTableColumnIdx FreezeColumnsCount; // Actual frozen columns count (== FreezeColumnsRequest, or == 0 when no scrolling offset) ImGuiTableColumnIdx RowCellDataCurrent; // Index of current RowCellData[] entry in current row ImGuiTableDrawChannelIdx DummyDrawChannel; // Redirect non-visible columns here. - ImGuiTableDrawChannelIdx Bg1DrawChannelCurrent; // For Selectable() and other widgets drawing accross columns after the freezing line. Index within DrawSplitter.Channels[] - ImGuiTableDrawChannelIdx Bg1DrawChannelUnfrozen; + ImGuiTableDrawChannelIdx Bg2DrawChannelCurrent; // For Selectable() and other widgets drawing accross columns after the freezing line. Index within DrawSplitter.Channels[] + ImGuiTableDrawChannelIdx Bg2DrawChannelUnfrozen; bool IsLayoutLocked; // Set by TableUpdateLayout() which is called when beginning the first row. bool IsInsideRow; // Set when inside TableBeginRow()/TableEndRow(). bool IsInitializing; diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 0052c563..f35a3aa5 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -177,8 +177,8 @@ Index of this file: // Configuration static const int TABLE_DRAW_CHANNEL_BG0 = 0; -static const int TABLE_DRAW_CHANNEL_BG1_FROZEN = 1; -static const int TABLE_DRAW_CHANNEL_NOCLIP = 2; // When using ImGuiTableFlags_NoClip +static const int TABLE_DRAW_CHANNEL_BG2_FROZEN = 1; +static const int TABLE_DRAW_CHANNEL_NOCLIP = 2; // When using ImGuiTableFlags_NoClip (this becomes the last visible channel) static const float TABLE_BORDER_SIZE = 1.0f; // FIXME-TABLE: Currently hard-coded because of clipping assumptions with outer borders rendering. static const float TABLE_RESIZE_SEPARATOR_HALF_THICKNESS = 4.0f; // Extend outside inner borders. static const float TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER = 0.06f; // Delay/timer before making the hover feedback (color+cursor) visible because tables/columns tends to be more cramped. @@ -278,8 +278,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->ColumnsCount = columns_count; table->IsLayoutLocked = false; table->InnerWidth = inner_width; - table->OuterRect = outer_rect; - table->WorkRect = outer_rect; table->IsOuterRectFitX = (outer_size.x >= -1.0f && outer_size.x <= 0.0f); // Bit ambiguous // When not using a child window, WorkRect.Max will grow as we append contents. @@ -303,12 +301,19 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // Create scrolling region (without border and zero window padding) ImGuiWindowFlags child_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None; - BeginChildEx(name, instance_id, table->OuterRect.GetSize(), false, child_flags); + BeginChildEx(name, instance_id, outer_rect.GetSize(), false, child_flags); table->InnerWindow = g.CurrentWindow; table->WorkRect = table->InnerWindow->WorkRect; table->OuterRect = table->InnerWindow->Rect(); + table->InnerRect = table->InnerWindow->InnerRect; IM_ASSERT(table->InnerWindow->WindowPadding.x == 0.0f && table->InnerWindow->WindowPadding.y == 0.0f && table->InnerWindow->WindowBorderSize == 0.0f); } + else + { + // For non-scrolling tables, WorkRect == OuterRect == InnerRect. + // But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable(). + table->WorkRect = table->OuterRect = table->InnerRect = outer_rect; + } // Push a standardized ID for both child-using and not-child-using tables PushOverrideID(instance_id); @@ -1148,12 +1153,13 @@ void ImGui::EndTable() inner_window->DC.CursorMaxPos = table->HostBackupCursorMaxPos; if (inner_window != outer_window) { - table->OuterRect.Max.y = ImMax(table->OuterRect.Max.y, inner_window->Pos.y + inner_window->Size.y); + // Both OuterRect/InnerRect are valid from BeginTable inner_window->DC.CursorMaxPos.y = table->RowPosY2; } else if (!(flags & ImGuiTableFlags_NoHostExtendY)) { - table->OuterRect.Max.y = ImMax(table->OuterRect.Max.y, inner_window->DC.CursorPos.y); + // Patch OuterRect/InnerRect height + table->OuterRect.Max.y = table->InnerRect.Max.y = ImMax(table->OuterRect.Max.y, inner_window->DC.CursorPos.y); inner_window->DC.CursorMaxPos.y = table->RowPosY2; } table->WorkRect.Max.y = ImMax(table->WorkRect.Max.y, table->OuterRect.Max.y); @@ -1166,7 +1172,6 @@ void ImGui::EndTable() // Draw borders if ((flags & ImGuiTableFlags_Borders) != 0) TableDrawBorders(table); - table->DrawSplitter.SetCurrentChannel(inner_window->DrawList, 0); // Store content width reference for each column (before attempting to merge draw calls) const float backup_outer_cursor_pos_x = outer_window->DC.CursorPos.x; @@ -1192,6 +1197,7 @@ void ImGui::EndTable() #endif // Flatten channels and merge draw calls + table->DrawSplitter.SetCurrentChannel(inner_window->DrawList, 0); if ((table->Flags & ImGuiTableFlags_NoClip) == 0) TableMergeDrawChannels(table); table->DrawSplitter.Merge(inner_window->DrawList); @@ -1563,7 +1569,6 @@ void ImGui::TableEndRow(ImGuiTable* table) // Row background fill const float bg_y1 = table->RowPosY1; const float bg_y2 = table->RowPosY2; - const bool unfreeze_rows_actual = (table->CurrentRow + 1 == table->FreezeRowsCount); const bool unfreeze_rows_request = (table->CurrentRow + 1 == table->FreezeRowsRequest); if (table->CurrentRow == 0) @@ -1654,7 +1659,7 @@ void ImGui::TableEndRow(ImGuiTable* table) float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y); table->BgClipRect.Min.y = table->BgClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y); table->BgClipRect.Max.y = table->BgClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y; - table->Bg1DrawChannelCurrent = table->Bg1DrawChannelUnfrozen; + table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen; IM_ASSERT(table->BgClipRectForDrawCmd.Min.y <= table->BgClipRectForDrawCmd.Max.y); float row_height = table->RowPosY2 - table->RowPosY1; @@ -2004,6 +2009,8 @@ void ImGui::TableUpdateColumnsWeightFromWidth(ImGuiTable* table) // - TableDrawBorders() [Internal] //------------------------------------------------------------------------- +// Bg2 is used by Selectable (and possibly other widgets) to render to the background. +// Unlike our Bg0/1 channel which we uses for RowBg/CellBg/Borders and where we guarantee all shapes to be CPU-clipped, the Bg2 channel being widgets-facing will rely on regular ClipRect. void ImGui::TablePushBackgroundChannel() { ImGuiContext& g = *GImGui; @@ -2011,9 +2018,9 @@ void ImGui::TablePushBackgroundChannel() ImGuiTable* table = g.CurrentTable; // Optimization: avoid SetCurrentChannel() + PushClipRect() - table->HostBackupClipRect = window->ClipRect; + table->HostBackupInnerClipRect = window->ClipRect; SetWindowClipRectBeforeSetChannel(window, table->BgClipRectForDrawCmd); - table->DrawSplitter.SetCurrentChannel(window->DrawList, table->Bg1DrawChannelCurrent); + table->DrawSplitter.SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent); } void ImGui::TablePopBackgroundChannel() @@ -2024,7 +2031,7 @@ void ImGui::TablePopBackgroundChannel() ImGuiTableColumn* column = &table->Columns[table->CurrentColumn]; // Optimization: avoid PopClipRect() + SetCurrentChannel() - SetWindowClipRectBeforeSetChannel(window, table->HostBackupClipRect); + SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect); table->DrawSplitter.SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); } @@ -2036,10 +2043,10 @@ void ImGui::TablePopBackgroundChannel() // - After crossing FreezeRowsCount, all columns see their current draw channel changed to a second set of channels. // - We only use the dummy draw channel so we can push a null clipping rectangle into it without affecting other // channels, while simplifying per-row/per-cell overhead. It will be empty and discarded when merged. -// - We allocate 1 or 2 background draw channels. This is because we know PushTableBackground() is only used for +// - We allocate 1 or 2 background draw channels. This is because we know TablePushBackgroundChannel() is only used for // horizontal spanning. If we allowed vertical spanning we'd need one background draw channel per merge group (1-4). // Draw channel allocation (before merging): -// - NoClip --> 2+D+1 channels: bg0 + bg1 + foreground (same clip rect == 1 draw call) (FIXME-TABLE: could merge bg1 and foreground?) +// - NoClip --> 2+D+1 channels: bg0/1 + bg2 + foreground (same clip rect == 1 draw call) (FIXME-TABLE: could merge bg2 and foreground?) // - Clip --> 2+D+N channels // - FreezeRows --> 2+D+N*2 (unless scrolling value is zero) // - FreezeRows || FreezeColunns --> 3+D+N*2 (unless scrolling value is zero) @@ -2053,8 +2060,8 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table) const int channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy; table->DrawSplitter.Split(table->InnerWindow->DrawList, channels_total); table->DummyDrawChannel = (ImGuiTableDrawChannelIdx)((channels_for_dummy > 0) ? channels_total - 1 : -1); - table->Bg1DrawChannelCurrent = TABLE_DRAW_CHANNEL_BG1_FROZEN; - table->Bg1DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)((table->FreezeRowsCount > 0) ? 2 + channels_for_row : TABLE_DRAW_CHANNEL_BG1_FROZEN); + table->Bg2DrawChannelCurrent = TABLE_DRAW_CHANNEL_BG2_FROZEN; + table->Bg2DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)((table->FreezeRowsCount > 0) ? 2 + channels_for_row : TABLE_DRAW_CHANNEL_BG2_FROZEN); int draw_channel_current = 2; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) @@ -2117,6 +2124,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) ImDrawListSplitter* splitter = &table->DrawSplitter; const bool has_freeze_v = (table->FreezeRowsCount > 0); const bool has_freeze_h = (table->FreezeColumnsCount > 0); + IM_ASSERT(splitter->_Current == 0); // Track which groups we are going to attempt to merge, and which channels goes into each group. struct MergeGroup @@ -2200,15 +2208,15 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) // 2. Rewrite channel list in our preferred order if (merge_group_mask != 0) { - // We skip channel 0 (Bg0) and 1 (Bg1 frozen) from the shuffling since they won't move - see channels allocation in TableSetupDrawChannels(). + // We skip channel 0 (Bg0/Bg1) and 1 (Bg2 frozen) from the shuffling since they won't move - see channels allocation in TableSetupDrawChannels(). const int LEADING_DRAW_CHANNELS = 2; g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data; ImBitArray remaining_mask; // We need 132-bit of storage remaining_mask.ClearBits(); remaining_mask.SetBitRange(LEADING_DRAW_CHANNELS, splitter->_Count - 1); - remaining_mask.ClearBit(table->Bg1DrawChannelUnfrozen); - IM_ASSERT(has_freeze_v == false || table->Bg1DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG1_FROZEN); + remaining_mask.ClearBit(table->Bg2DrawChannelUnfrozen); + IM_ASSERT(has_freeze_v == false || table->Bg2DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG2_FROZEN); int remaining_count = splitter->_Count - (has_freeze_v ? LEADING_DRAW_CHANNELS + 1 : LEADING_DRAW_CHANNELS); //ImRect host_rect = (table->InnerWindow == table->OuterWindow) ? table->InnerClipRect : table->HostClipRect; ImRect host_rect = table->HostClipRect; @@ -2257,9 +2265,9 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) } } - // Make sure Bg1DrawChannelUnfrozen appears in the middle of our groups (whereas Bg0 and Bg1 frozen are fixed to 0 and 1) + // Make sure Bg2DrawChannelUnfrozen appears in the middle of our groups (whereas Bg0/Bg1 and Bg2 frozen are fixed to 0 and 1) if (merge_group_n == 1 && has_freeze_v) - memcpy(dst_tmp++, &splitter->_Channels[table->Bg1DrawChannelUnfrozen], sizeof(ImDrawChannel)); + memcpy(dst_tmp++, &splitter->_Channels[table->Bg2DrawChannelUnfrozen], sizeof(ImDrawChannel)); } // Append unmergeable channels that we didn't reorder at the end of the list @@ -2289,8 +2297,8 @@ void ImGui::TableDrawBorders(ImGuiTable* table) // Draw inner border and resizing feedback const float border_size = TABLE_BORDER_SIZE; - const float draw_y1 = table->OuterRect.Min.y; - const float draw_y2_body = table->OuterRect.Max.y; + const float draw_y1 = table->InnerRect.Min.y; + const float draw_y2_body = table->InnerRect.Max.y; const float draw_y2_head = table->IsUsingHeaders ? ((table->FreezeRowsCount >= 1 ? table->OuterRect.Min.y : table->WorkRect.Min.y) + table->LastFirstRowHeight) : draw_y1; if (table->Flags & ImGuiTableFlags_BordersInnerV) From 22ace4438c86b3137567c69346a3836d26ebf95c Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 18 Dec 2020 14:14:15 +0100 Subject: [PATCH 08/18] Tables: clarify assumption that rendering of bg/borders in bg0/bg1 are cpu-clipped allowing frozen/unfrozen to share drawcmd + remove offset on outer borders of scrolling tables. --- imgui_internal.h | 3 ++- imgui_tables.cpp | 42 ++++++++++++++++++++---------------------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 6aece7cc..eb753fe5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2019,7 +2019,8 @@ struct ImGuiTable ImRect WorkRect; ImRect InnerClipRect; ImRect BgClipRect; // We use this to cpu-clip cell background color fill - ImRect BgClipRectForDrawCmd; + ImRect Bg0ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG0/1 channel. This tends to be == OuterWindow->ClipRect at BeginTable() because output in BG0/BG1 is cpu-clipped + ImRect Bg2ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG2 channel. This tends to be a correct, tight-fit, because output to BG2 are done by widgets relying on regular ClipRect. ImRect HostClipRect; // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window. ImRect HostBackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable() ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable() diff --git a/imgui_tables.cpp b/imgui_tables.cpp index f35a3aa5..1c347b68 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1601,7 +1601,7 @@ void ImGui::TableEndRow(ImGuiTable* table) // In theory we could call SetWindowClipRectBeforeSetChannel() but since we know TableEndRow() is // always followed by a change of clipping rectangle we perform the smallest overwrite possible here. if ((table->Flags & ImGuiTableFlags_NoClip) == 0) - window->DrawList->_CmdHeader.ClipRect = table->BgClipRectForDrawCmd.ToVec4(); + window->DrawList->_CmdHeader.ClipRect = table->Bg0ClipRectForDrawCmd.ToVec4(); table->DrawSplitter.SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0); } @@ -1657,10 +1657,10 @@ void ImGui::TableEndRow(ImGuiTable* table) // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y); - table->BgClipRect.Min.y = table->BgClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y); - table->BgClipRect.Max.y = table->BgClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y; + table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y); + table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y; table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen; - IM_ASSERT(table->BgClipRectForDrawCmd.Min.y <= table->BgClipRectForDrawCmd.Max.y); + IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y); float row_height = table->RowPosY2 - table->RowPosY1; table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y; @@ -1669,7 +1669,7 @@ void ImGui::TableEndRow(ImGuiTable* table) { ImGuiTableColumn* column = &table->Columns[column_n]; column->DrawChannelCurrent = column->DrawChannelUnfrozen; - column->ClipRect.Min.y = table->BgClipRectForDrawCmd.Min.y; + column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y; } // Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y @@ -2019,7 +2019,7 @@ void ImGui::TablePushBackgroundChannel() // Optimization: avoid SetCurrentChannel() + PushClipRect() table->HostBackupInnerClipRect = window->ClipRect; - SetWindowClipRectBeforeSetChannel(window, table->BgClipRectForDrawCmd); + SetWindowClipRectBeforeSetChannel(window, table->Bg2ClipRectForDrawCmd); table->DrawSplitter.SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent); } @@ -2046,7 +2046,7 @@ void ImGui::TablePopBackgroundChannel() // - We allocate 1 or 2 background draw channels. This is because we know TablePushBackgroundChannel() is only used for // horizontal spanning. If we allowed vertical spanning we'd need one background draw channel per merge group (1-4). // Draw channel allocation (before merging): -// - NoClip --> 2+D+1 channels: bg0/1 + bg2 + foreground (same clip rect == 1 draw call) (FIXME-TABLE: could merge bg2 and foreground?) +// - NoClip --> 2+D+1 channels: bg0/1 + bg2 + foreground (same clip rect == always 1 draw call) // - Clip --> 2+D+N channels // - FreezeRows --> 2+D+N*2 (unless scrolling value is zero) // - FreezeRows || FreezeColunns --> 3+D+N*2 (unless scrolling value is zero) @@ -2085,7 +2085,8 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table) // All our cell highlight are manually clipped with BgClipRect. When unfreezing it will be made smaller to fit scrolling rect. // (This technically isn't part of setting up draw channels, but is reasonably related to be done here) table->BgClipRect = table->InnerClipRect; - table->BgClipRectForDrawCmd = table->HostClipRect; + table->Bg0ClipRectForDrawCmd = table->OuterWindow->ClipRect; + table->Bg2ClipRectForDrawCmd = table->HostClipRect; IM_ASSERT(table->BgClipRect.Min.y <= table->BgClipRect.Max.y); } @@ -2288,19 +2289,17 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) void ImGui::TableDrawBorders(ImGuiTable* table) { ImGuiWindow* inner_window = table->InnerWindow; - ImGuiWindow* outer_window = table->OuterWindow; table->DrawSplitter.SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_BG0); if (inner_window->Hidden || !table->HostClipRect.Overlaps(table->InnerClipRect)) return; ImDrawList* inner_drawlist = inner_window->DrawList; - ImDrawList* outer_drawlist = outer_window->DrawList; + inner_drawlist->PushClipRect(table->Bg0ClipRectForDrawCmd.Min, table->Bg0ClipRectForDrawCmd.Max, false); // Draw inner border and resizing feedback const float border_size = TABLE_BORDER_SIZE; const float draw_y1 = table->InnerRect.Min.y; const float draw_y2_body = table->InnerRect.Max.y; const float draw_y2_head = table->IsUsingHeaders ? ((table->FreezeRowsCount >= 1 ? table->OuterRect.Min.y : table->WorkRect.Min.y) + table->LastFirstRowHeight) : draw_y1; - if (table->Flags & ImGuiTableFlags_BordersInnerV) { for (int order_n = 0; order_n < table->ColumnsCount; order_n++) @@ -2351,23 +2350,21 @@ void ImGui::TableDrawBorders(ImGuiTable* table) // parent. In inner_window, it won't reach out over scrollbars. Another weird solution would be to display part // of it in inner window, and the part that's over scrollbars in the outer window..) // Either solution currently won't allow us to use a larger border size: the border would clipped. - ImRect outer_border = table->OuterRect; + const ImRect outer_border = table->OuterRect; const ImU32 outer_col = table->BorderColorStrong; - if (inner_window != outer_window) // FIXME-TABLE - outer_border.Expand(1.0f); if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter) { - outer_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, ~0, border_size); + inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, ~0, border_size); } else if (table->Flags & ImGuiTableFlags_BordersOuterV) { - outer_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Min.x, outer_border.Max.y), outer_col, border_size); - outer_drawlist->AddLine(ImVec2(outer_border.Max.x, outer_border.Min.y), outer_border.Max, outer_col, border_size); + inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Min.x, outer_border.Max.y), outer_col, border_size); + inner_drawlist->AddLine(ImVec2(outer_border.Max.x, outer_border.Min.y), outer_border.Max, outer_col, border_size); } else if (table->Flags & ImGuiTableFlags_BordersOuterH) { - outer_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Max.x, outer_border.Min.y), outer_col, border_size); - outer_drawlist->AddLine(ImVec2(outer_border.Min.x, outer_border.Max.y), outer_border.Max, outer_col, border_size); + inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Max.x, outer_border.Min.y), outer_col, border_size); + inner_drawlist->AddLine(ImVec2(outer_border.Min.x, outer_border.Max.y), outer_border.Max, outer_col, border_size); } } if ((table->Flags & ImGuiTableFlags_BordersInnerH) && table->RowPosY2 < table->OuterRect.Max.y) @@ -2377,6 +2374,8 @@ void ImGui::TableDrawBorders(ImGuiTable* table) if (border_y >= table->BgClipRect.Min.y && border_y < table->BgClipRect.Max.y) inner_drawlist->AddLine(ImVec2(table->BorderX1, border_y), ImVec2(table->BorderX2, border_y), table->BorderColorLight, border_size); } + + inner_drawlist->PopClipRect(); } //------------------------------------------------------------------------- @@ -2638,7 +2637,6 @@ void ImGui::TableHeadersRow() // 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; @@ -2660,7 +2658,7 @@ void ImGui::TableHeader(const char* label) 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 + // 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); @@ -2671,7 +2669,7 @@ void ImGui::TableHeader(const char* label) const float ARROW_SCALE = 0.65f; if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort)) { - w_arrow = ImFloor(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x);// table->CellPadding.x); + w_arrow = ImFloor(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x); if (column->SortOrder > 0) { ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), "%d", column->SortOrder + 1); From 532aa5a69bbec87560639702d78a2df97a4be2a0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 18 Dec 2020 17:27:41 +0100 Subject: [PATCH 09/18] Tables: (breaking) change outer_size.x default value to -FLT_MIN, make outer_size.x == 0.0f act as ImGuiTableFlags_NoHostExtendX (#3605, ad83976b) when no scrolling and no stretch column. Which is more consistent. Demo: moved "Compact table" to "Padding" section, makes more sense. Tweaked demo. --- imgui.h | 27 ++++--- imgui_demo.cpp | 190 ++++++++++++++++++++++++----------------------- imgui_internal.h | 2 +- imgui_tables.cpp | 46 +++++++----- 4 files changed, 138 insertions(+), 127 deletions(-) diff --git a/imgui.h b/imgui.h index c0d9dcf3..f2f2f535 100644 --- a/imgui.h +++ b/imgui.h @@ -462,7 +462,7 @@ namespace ImGui IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value); IMGUI_API bool RadioButton(const char* label, bool active); // use with e.g. if (RadioButton("one", my_value==1)) { my_value = 1; } IMGUI_API bool RadioButton(const char* label, int* v, int v_button); // shortcut to handle the above pattern when value is an integer - IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-1, 0), const char* overlay = NULL); + IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-FLT_MIN, 0), const char* overlay = NULL); IMGUI_API void Bullet(); // draw a small circle + keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses // Widgets: Combo Box @@ -679,7 +679,7 @@ namespace ImGui // TableNextRow() -> Text("Hello 0") // Not OK! Missing TableSetColumnIndex() or TableNextColumn()! Text will not appear! // ---------------------------------------------------------------------------------------------------------- // - 5. Call EndTable() - IMGUI_API bool BeginTable(const char* str_id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f); + IMGUI_API bool BeginTable(const char* str_id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(-FLT_MIN, 0), float inner_width = 0.0f); IMGUI_API void EndTable(); // only call EndTable() if BeginTable() returns true! IMGUI_API void TableNextRow(ImGuiTableRowFlags row_flags = 0, float min_row_height = 0.0f); // append into the first cell of a new row. IMGUI_API bool TableNextColumn(); // append into the next column (or first column of next row if currently in last column). Return true when column is visible. @@ -1075,21 +1075,20 @@ enum ImGuiTableFlags_ ImGuiTableFlags_ColumnsWidthFixed = 1 << 14, // Default if ScrollX is on. Columns will default to use _WidthFixed or _WidthAutoResize policy (if Resizable is off). Read description above for more details. ImGuiTableFlags_SameWidths = 1 << 15, // Make all columns the same widths which is useful with Fixed columns policy (but granted by default with Stretch policy + no resize). Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible and disable ImGuiTableFlags_Resizable. ImGuiTableFlags_NoHeadersWidth = 1 << 16, // Disable headers' contribution to automatic width calculation. - ImGuiTableFlags_NoHostExtendX = 1 << 17, // Disable extending table past the limit required by the right-most columns. Only meaningful with Fixed columns and when neither ScrollX nor ScrollY are set. - ImGuiTableFlags_NoHostExtendY = 1 << 18, // Disable extending table past the limit set by outer_size.y. Only meaningful when neither ScrollX nor ScrollY are set (data below the limit will be clipped and not visible) - ImGuiTableFlags_NoKeepColumnsVisible = 1 << 19, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. - ImGuiTableFlags_PreciseWidths = 1 << 20, // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth. - ImGuiTableFlags_NoClip = 1 << 21, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze(). + ImGuiTableFlags_NoHostExtendY = 1 << 17, // Disable extending table past the limit set by outer_size.y. Only meaningful when neither ScrollX nor ScrollY are set (data below the limit will be clipped and not visible) + ImGuiTableFlags_NoKeepColumnsVisible = 1 << 18, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. + ImGuiTableFlags_PreciseWidths = 1 << 19, // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth. + ImGuiTableFlags_NoClip = 1 << 20, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze(). // Padding - ImGuiTableFlags_PadOuterX = 1 << 22, // Default if BordersOuterV is on. Enable outer-most padding. - ImGuiTableFlags_NoPadOuterX = 1 << 23, // Default if BordersOuterV is off. Disable outer-most padding. - ImGuiTableFlags_NoPadInnerX = 1 << 24, // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off). + ImGuiTableFlags_PadOuterX = 1 << 21, // Default if BordersOuterV is on. Enable outer-most padding. + ImGuiTableFlags_NoPadOuterX = 1 << 22, // Default if BordersOuterV is off. Disable outer-most padding. + ImGuiTableFlags_NoPadInnerX = 1 << 23, // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off). // Scrolling - ImGuiTableFlags_ScrollX = 1 << 25, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this create a child window, ScrollY is currently generally recommended when using ScrollX. - ImGuiTableFlags_ScrollY = 1 << 26, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. + ImGuiTableFlags_ScrollX = 1 << 24, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this create a child window, ScrollY is currently generally recommended when using ScrollX. + ImGuiTableFlags_ScrollY = 1 << 25, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. // Sorting - ImGuiTableFlags_SortMulti = 1 << 27, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). - ImGuiTableFlags_SortTristate = 1 << 28 // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). + ImGuiTableFlags_SortMulti = 1 << 26, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). + ImGuiTableFlags_SortTristate = 1 << 27 // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). }; // Flags for ImGui::TableSetupColumn() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b4fd22d3..a4a2a4ed 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3692,7 +3692,8 @@ static void ShowDemoWindowTables() ImGui::EndTable(); } - if (ImGui::BeginTable("##table2", 3, flags | ImGuiTableFlags_ColumnsWidthFixed)) + // Use outer_size.x == 0.0f instead of default to make the table as tight as possible (only valid when no scrolling and no stretch column) + if (ImGui::BeginTable("##table2", 3, flags | ImGuiTableFlags_ColumnsWidthFixed, ImVec2(0.0f, 0.0f))) { ImGui::TableSetupColumn("One"); ImGui::TableSetupColumn("Two"); @@ -3716,8 +3717,8 @@ static void ShowDemoWindowTables() ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Padding")) { - static ImGuiTableFlags flags = ImGuiTableFlags_BordersV; - + // First example: showcase use of padding flags and effect of BorderOuterV/BorderInnerV on X padding. + // We don't expose BorderOuterH/BorderInnerH here because they have no effect on X padding. HelpMarker( "We often want outer padding activated when any using features which makes the edges of a column visible:\n" "e.g.:\n" @@ -3726,20 +3727,21 @@ static void ShowDemoWindowTables() "Because of this, activating BorderOuterV sets the default to PadOuterX. Using PadOuterX or NoPadOuterX you can override the default.\n\n" "Actual padding values are using style.CellPadding."); + static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV; PushStyleCompact(); - ImGui::CheckboxFlags("ImGuiTableFlags_PadOuterX", &flags, ImGuiTableFlags_PadOuterX); + ImGui::CheckboxFlags("ImGuiTableFlags_PadOuterX", &flags1, ImGuiTableFlags_PadOuterX); ImGui::SameLine(); HelpMarker("Enable outer-most padding (default if ImGuiTableFlags_BordersOuterV is set)"); - ImGui::CheckboxFlags("ImGuiTableFlags_NoPadOuterX", &flags, ImGuiTableFlags_NoPadOuterX); + ImGui::CheckboxFlags("ImGuiTableFlags_NoPadOuterX", &flags1, ImGuiTableFlags_NoPadOuterX); ImGui::SameLine(); HelpMarker("Disable outer-most padding (default if ImGuiTableFlags_BordersOuterV is not set)"); - ImGui::CheckboxFlags("ImGuiTableFlags_NoPadInnerX", &flags, ImGuiTableFlags_NoPadInnerX); + ImGui::CheckboxFlags("ImGuiTableFlags_NoPadInnerX", &flags1, ImGuiTableFlags_NoPadInnerX); ImGui::SameLine(); HelpMarker("Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off)"); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags, ImGuiTableFlags_BordersOuterV); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags, ImGuiTableFlags_BordersInnerV); + ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags1, ImGuiTableFlags_BordersOuterV); + ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags1, ImGuiTableFlags_BordersInnerV); static bool show_headers = false; ImGui::Checkbox("show_headers", &show_headers); PopStyleCompact(); - if (ImGui::BeginTable("##table1", 3, flags)) + if (ImGui::BeginTable("##table1", 3, flags1)) { if (show_headers) { @@ -3772,6 +3774,49 @@ static void ShowDemoWindowTables() ImGui::EndTable(); } + // Second example: set style.CellPadding to (0.0) or a custom value. + // FIXME-TABLE: Vertical border effectively not displayed the same way as horizontal one... + HelpMarker("Setting style.CellPadding to (0,0) or a custom value."); + static ImGuiTableFlags flags2 = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg; + static ImVec2 cell_padding(0.0f, 0.0f); + static bool show_widget_frame_bg = false; + + PushStyleCompact(); + ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags2, ImGuiTableFlags_Borders); + ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags2, ImGuiTableFlags_BordersH); + ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags2, ImGuiTableFlags_BordersV); + ImGui::CheckboxFlags("ImGuiTableFlags_BordersInner", &flags2, ImGuiTableFlags_BordersInner); + ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuter", &flags2, ImGuiTableFlags_BordersOuter); + ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags2, ImGuiTableFlags_RowBg); + ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags2, ImGuiTableFlags_Resizable); + ImGui::Checkbox("show_widget_frame_bg", &show_widget_frame_bg); + ImGui::SliderFloat2("CellPadding", &cell_padding.x, 0.0f, 10.0f, "%.0f"); + PopStyleCompact(); + + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cell_padding); + if (ImGui::BeginTable("##table2", 3, flags2)) + { + static char text_bufs[3 * 5][16]; // Mini text storage for 3x5 cells + static bool init = true; + if (!show_widget_frame_bg) + ImGui::PushStyleColor(ImGuiCol_FrameBg, 0); + for (int cell = 0; cell < 3 * 5; cell++) + { + ImGui::TableNextColumn(); + if (init) + strcpy(text_bufs[cell], "edit me"); + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::PushID(cell); + ImGui::InputText("##cell", text_bufs[cell], IM_ARRAYSIZE(text_bufs[cell])); + ImGui::PopID(); + } + if (!show_widget_frame_bg) + ImGui::PopStyleColor(); + init = false; + ImGui::EndTable(); + } + ImGui::PopStyleVar(); + ImGui::TreePop(); } @@ -3822,7 +3867,7 @@ static void ShowDemoWindowTables() // When using ScrollX or ScrollY we need to specify a size for our table container! // Otherwise by default the table will fit all available space, like a BeginChild() call. - ImVec2 size = ImVec2(0, TEXT_BASE_HEIGHT * 8); + ImVec2 size = ImVec2(-FLT_MIN, TEXT_BASE_HEIGHT * 8); if (ImGui::BeginTable("##table1", 3, flags, size)) { ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible @@ -3875,7 +3920,7 @@ static void ShowDemoWindowTables() // When using ScrollX or ScrollY we need to specify a size for our table container! // Otherwise by default the table will fit all available space, like a BeginChild() call. - ImVec2 outer_size = ImVec2(0, TEXT_BASE_HEIGHT * 8); + ImVec2 outer_size = ImVec2(-FLT_MIN, TEXT_BASE_HEIGHT * 8); if (ImGui::BeginTable("##table1", 7, flags, outer_size)) { ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows); @@ -3949,7 +3994,7 @@ static void ShowDemoWindowTables() = ImGuiTableFlags_ColumnsWidthFixed | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable; - ImVec2 size = ImVec2(0, TEXT_BASE_HEIGHT * 9); + ImVec2 size = ImVec2(-FLT_MIN, TEXT_BASE_HEIGHT * 9); if (ImGui::BeginTable("##table", column_count, flags, size)) { for (int column = 0; column < column_count; column++) @@ -4057,7 +4102,7 @@ static void ShowDemoWindowTables() ImGui::CheckboxFlags("ImGuiTableFlags_NoClip", &flags, ImGuiTableFlags_NoClip); PopStyleCompact(); - ImVec2 outer_size(0, TEXT_BASE_HEIGHT * 7); + ImVec2 outer_size(-FLT_MIN, TEXT_BASE_HEIGHT * 7); if (ImGui::BeginTable("##nways", column_count, flags, outer_size)) { for (int cell = 0; cell < 10 * column_count; cell++) @@ -4153,50 +4198,6 @@ static void ShowDemoWindowTables() ImGui::TreePop(); } - if (open_action != -1) - ImGui::SetNextItemOpen(open_action != 0); - if (ImGui::TreeNode("Compact table")) - { - // FIXME-TABLE: Vertical border not displayed the same way as horizontal one... - HelpMarker("Setting style.CellPadding to (0,0)."); - static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg; - static bool no_widget_frame = false; - - PushStyleCompact(); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuter", &flags, ImGuiTableFlags_BordersOuter); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH); - ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV); - ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg); - ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); - ImGui::Checkbox("no_widget_frame", &no_widget_frame); - PopStyleCompact(); - - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0, 0)); - if (ImGui::BeginTable("##3ways", 3, flags)) - { - for (int row = 0; row < 10; row++) - { - static char text_buf[32] = ""; - ImGui::TableNextRow(); - for (int column = 0; column < 3; column++) - { - ImGui::TableSetColumnIndex(column); - ImGui::SetNextItemWidth(-FLT_MIN); - ImGui::PushID(row * 3 + column); - if (no_widget_frame) - ImGui::PushStyleColor(ImGuiCol_FrameBg, 0); - ImGui::InputText("##cell", text_buf, IM_ARRAYSIZE(text_buf)); - if (no_widget_frame) - ImGui::PopStyleColor(); - ImGui::PopID(); - } - } - ImGui::EndTable(); - } - ImGui::PopStyleVar(); - ImGui::TreePop(); - } - if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Row height")) @@ -4216,12 +4217,37 @@ static void ShowDemoWindowTables() ImGui::TreePop(); } - if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); if (ImGui::TreeNode("Outer size")) { - ImGui::Text("Using manual/explicit size:"); + // Showcasing use of outer_size.x == 0.0f and ImGuiTableFlags_NoHostExtendY + // The default value of outer_size.x is -FLT_MIN which right-align tables. + // Using outer_size.x == 0.0f on a table with no scrolling and no stretch column we can make them tighter. + ImGui::Text("Using auto/all width, using NoHostExtendY:"); + static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg | ImGuiTableFlags_ColumnsWidthFixed; + static bool use_all_width = false; + ImGui::Checkbox("Use all width", &use_all_width); + ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY); + if (ImGui::BeginTable("##table3", 3, flags, ImVec2(use_all_width ? -FLT_MIN : 0.0f, TEXT_BASE_HEIGHT * 5.5f))) + { + for (int row = 0; row < 10; row++) + { + ImGui::TableNextRow(); + for (int column = 0; column < 3; column++) + { + ImGui::TableNextColumn(); + ImGui::Text("Cell %d,%d", column, row); + } + } + ImGui::EndTable(); + } + ImGui::SameLine(); + ImGui::Text("Hello!"); + + ImGui::Spacing(); + + ImGui::Text("Using explicit size:"); if (ImGui::BeginTable("##table1", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f))) { for (int row = 0; row < 5; row++) @@ -4249,29 +4275,6 @@ static void ShowDemoWindowTables() } ImGui::EndTable(); } - ImGui::Spacing(); - - // Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY - // FIXME-TABLE: WIP API: Don't use yet, will probably rework/rename those flags. - ImGui::Text("Using NoHostExtend flags:"); - static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_ColumnsWidthFixed; - ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX); - ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY); - if (ImGui::BeginTable("##table3", 3, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f))) - { - for (int row = 0; row < 10; row++) - { - ImGui::TableNextRow(); - for (int column = 0; column < 3; column++) - { - ImGui::TableNextColumn(); - ImGui::Text("Cell %d,%d", column, row); - } - } - ImGui::EndTable(); - } - ImGui::SameLine(); - ImGui::Text("Hello!"); ImGui::TreePop(); } @@ -4622,7 +4625,7 @@ static void ShowDemoWindowTables() ImGui::CheckboxFlags("ImGuiTableFlags_SortTristate", &flags, ImGuiTableFlags_SortTristate); ImGui::SameLine(); HelpMarker("When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0)."); - if (ImGui::BeginTable("##table", 4, flags, ImVec2(0, TEXT_BASE_HEIGHT * 15), 0.0f)) + if (ImGui::BeginTable("##table", 4, flags, ImVec2(-FLT_MIN, TEXT_BASE_HEIGHT * 15), 0.0f)) { // Declare columns // We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications. @@ -4692,7 +4695,7 @@ static void ShowDemoWindowTables() static int freeze_cols = 1; static int freeze_rows = 1; static int items_count = IM_ARRAYSIZE(template_items_names) * 2; - static ImVec2 outer_size_value = ImVec2(0, TEXT_BASE_HEIGHT * 12); + static ImVec2 outer_size_value = ImVec2(-FLT_MIN, TEXT_BASE_HEIGHT * 12); static float row_min_height = 0.0f; // Auto static float inner_width_with_scroll = 0.0f; // Auto-extend static bool outer_size_enabled = true; @@ -4740,7 +4743,6 @@ static void ShowDemoWindowTables() flags &= ~ImGuiTableFlags_ColumnsWidthStretch; // Can't specify both sizing polices so we clear the other ImGui::SameLine(); HelpMarker("[Default if ScrollX is on]\nEnlarge as needed: enable scrollbar if ScrollX is enabled, otherwise extend parent window's contents rectangle. Only Fixed columns allowed. Stretched columns will calculate their width assuming no scrolling."); ImGui::CheckboxFlags("ImGuiTableFlags_NoHeadersWidth", &flags, ImGuiTableFlags_NoHeadersWidth); - ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX); ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY); ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags, ImGuiTableFlags_NoKeepColumnsVisible); ImGui::SameLine(); HelpMarker("Only available if ScrollX is disabled."); @@ -4790,7 +4792,11 @@ static void ShowDemoWindowTables() ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); ImGui::Checkbox("outer_size", &outer_size_enabled); ImGui::SameLine(); - HelpMarker("If scrolling is disabled (ScrollX and ScrollY not set), the table is output directly in the parent window. OuterSize.y then becomes the minimum size for the table, which will extend vertically if there are more rows (unless NoHostExtendV is set)."); + HelpMarker("If scrolling is disabled (ScrollX and ScrollY not set):\n" + "- The table is output directly in the parent window.\n" + "- OuterSize.x < 0.0f will right-align the table.\n" + "- OuterSize.x = 0.0f will narrow fit the table unless there are any Stretch column.\n" + "- OuterSize.y then becomes the minimum size for the table, which will extend vertically if there are more rows (unless NoHostExtendY is set)."); // From a user point of view we will tend to use 'inner_width' differently depending on whether our table is embedding scrolling. // To facilitate experimentation we expose two values and will select the right one depending on active flags. @@ -4837,12 +4843,12 @@ static void ShowDemoWindowTables() // Declare columns // 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! - ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide, -1.0f, MyItemColumnID_ID); - ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, -1.0f, MyItemColumnID_Name); - ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, -1.0f, MyItemColumnID_Action); - ImGui::TableSetupColumn("Quantity (Long Label)", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthStretch, 1.0f, MyItemColumnID_Quantity);// , ImGuiTableColumnFlags_WidthAutoResize); - ImGui::TableSetupColumn("Description", ImGuiTableColumnFlags_WidthStretch, 1.0f, MyItemColumnID_Description); - ImGui::TableSetupColumn("Hidden", ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort); + ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide, -1.0f, MyItemColumnID_ID); + ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, -1.0f, MyItemColumnID_Name); + ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, -1.0f, MyItemColumnID_Action); + ImGui::TableSetupColumn("Quantity", ImGuiTableColumnFlags_PreferSortDescending, 1.0f, MyItemColumnID_Quantity); + ImGui::TableSetupColumn("Description", ImGuiTableColumnFlags_WidthStretch, 1.0f, MyItemColumnID_Description); + ImGui::TableSetupColumn("Hidden", ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort); ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows); // Sort our data if sort specs have been changed! diff --git a/imgui_internal.h b/imgui_internal.h index eb753fe5..0d5be716 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2074,7 +2074,7 @@ struct ImGuiTable bool IsResetAllRequest; bool IsResetDisplayOrderRequest; bool IsUnfrozen; // Set when we got past the frozen row. - bool IsOuterRectFitX; // Set when outer_size value passed to BeginTable() is (>= -1.0f && <= 0.0f) + bool IsOuterRectAutoFitX; // Set when outer_size.x == 0.0f in BeginTable(), scrolling is disabled, and there are stretch columns. bool MemoryCompacted; bool HostSkipItems; // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 1c347b68..be751218 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -65,26 +65,31 @@ Index of this file: // TABLE SIZING //----------------------------------------------------------------------------- // (Read carefully because this is subtle but it does make sense!) -// About 'outer_size', its meaning needs to differ slightly depending of if we are using ScrollX/ScrollY flags: -// X: -// - outer_size.x < 0.0f -> right align from window/work-rect maximum x edge. -// - outer_size.x = 0.0f -> auto enlarge, use all available space. -// - outer_size.x > 0.0f -> fixed width -// Y with ScrollX/ScrollY: using a child window for scrolling: -// - outer_size.y < 0.0f -> bottom align -// - outer_size.y = 0.0f -> bottom align, consistent with BeginChild(). not recommended unless table is last item in parent window. -// - outer_size.y > 0.0f -> fixed child height. recommended when using Scrolling on any axis. -// Y without scrolling, we output table directly in parent window: -// - outer_size.y < 0.0f -> bottom align (will auto extend, unless NoHostExtendV is set) -// - outer_size.y = 0.0f -> zero minimum height (will auto extend, unless NoHostExtendV is set) -// - outer_size.y > 0.0f -> minimum height (will auto extend, unless NoHostExtendV is set) +//----------------------------------------------------------------------------- +// About 'outer_size': +// Its meaning needs to differ slightly depending of if we are using ScrollX/ScrollY flags. +// Default value is ImVec2(-FLT_MIN, 0.0f). When binding this in a scripting language please follow this default value. +// X +// - outer_size.x < 0.0f -> Right-align from window/work-rect right-most edge. With -FLT_MIN will right-align exactly on right-most edge. With -1.0f will right-align one pixel away from right-most edge. +// - outer_size.x = 0.0f -> Auto width. Generally use all available width. When NOT using scrolling and NOT using any Stretch column, use only necessary width, otherwise same as -FLT_MIN. +// - outer_size.x > 0.0f -> Fixed width. +// Y with ScrollX/ScrollY disabled: we output table directly in current window +// - outer_size.y < 0.0f -> Bottom-align (but will auto extend, unless NoHostExtendV is set) +// - outer_size.y = 0.0f -> No minimum height (but will auto extend, unless NoHostExtendV is set) +// - outer_size.y > 0.0f -> Set Minimum height (but will auto extend, unless NoHostExtendV is set) +// Y with ScrollX/ScrollY enabled: using a child window for scrolling +// - outer_size.y < 0.0f -> Bottom-align +// - outer_size.y = 0.0f -> Bottom-align, consistent with BeginChild(). Not recommended unless table is last item in parent window. +// - outer_size.y > 0.0f -> Set Exact height. Recommended when using Scrolling on any axis. +//----------------------------------------------------------------------------- // About 'inner_width': -// With ScrollX: +// With ScrollX disabled: +// - inner_width -> *ignored* +// With ScrollX enable: // - inner_width < 0.0f -> *illegal* fit in known width (right align from outer_size.x) <-- weird // - inner_width = 0.0f -> fit in outer_width: Fixed size columns will take space they need (if avail, otherwise shrink down), Stretch columns becomes Fixed columns. // - inner_width > 0.0f -> override scrolling width, generally to be larger than outer_size.x. Fixed column take space they need (if avail, otherwise shrink down), Stretch columns share remaining space! -// Without ScrollX: -// - inner_width -> *ignored* +//----------------------------------------------------------------------------- // Details: // - If you want to use Stretch columns with ScrollX, you generally need to specify 'inner_width' otherwise the concept // of "available space" doesn't make sense. @@ -278,7 +283,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->ColumnsCount = columns_count; table->IsLayoutLocked = false; table->InnerWidth = inner_width; - table->IsOuterRectFitX = (outer_size.x >= -1.0f && outer_size.x <= 0.0f); // Bit ambiguous + table->IsOuterRectAutoFitX = (outer_size.x == 0.0f) && (use_child_window == false); // When not using a child window, WorkRect.Max will grow as we append contents. if (use_child_window) @@ -1009,11 +1014,12 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // [Part 9] Lock actual OuterRect/WorkRect right-most position. // This is done late to handle the case of fixed-columns tables not claiming more widths that they need. // Because of this we are careful with uses of WorkRect and InnerClipRect before this point. - if ((table->Flags & ImGuiTableFlags_NoHostExtendX) && table->InnerWindow == table->OuterWindow && table->RightMostStretchedColumn == -1) + if (table->RightMostStretchedColumn != -1) + table->IsOuterRectAutoFitX = false; + if (table->IsOuterRectAutoFitX) { table->OuterRect.Max.x = table->WorkRect.Max.x = unused_x1; table->InnerClipRect.Max.x = ImMin(table->InnerClipRect.Max.x, unused_x1); - table->IsOuterRectFitX = false; } table->InnerWindow->ParentWorkRect = table->WorkRect; table->BorderX1 = table->InnerClipRect.Min.x;// +((table->Flags & ImGuiTableFlags_BordersOuter) ? 0.0f : -1.0f); @@ -1237,7 +1243,7 @@ void ImGui::EndTable() outer_window->DC.ItemWidth = table->HostBackupItemWidth; outer_window->DC.ItemWidthStack.Size = table->HostBackupItemWidthStackSize; outer_window->DC.ColumnsOffset = table->HostBackupColumnsOffset; - const float outer_width = table->IsOuterRectFitX ? table->ColumnsAutoFitWidth : table->WorkRect.GetWidth(); + const float outer_width = table->IsOuterRectAutoFitX ? table->WorkRect.GetWidth() : table->ColumnsAutoFitWidth; if (inner_window != outer_window) { EndChild(); From ae63d56426caea1c724688d5de07ba9b61acc25b Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 18 Dec 2020 19:03:10 +0100 Subject: [PATCH 10/18] Tables: remove ImGuiTableFlags_NoHeadersWidth since it is so rarely used and can be specified on a per-column basis. --- imgui.h | 2 +- imgui_demo.cpp | 1 - imgui_tables.cpp | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/imgui.h b/imgui.h index f2f2f535..42aa8f6d 100644 --- a/imgui.h +++ b/imgui.h @@ -1074,10 +1074,10 @@ enum ImGuiTableFlags_ ImGuiTableFlags_ColumnsWidthStretch = 1 << 13, // Default if ScrollX is off. Columns will default to use _WidthStretch. Read description above for more details. ImGuiTableFlags_ColumnsWidthFixed = 1 << 14, // Default if ScrollX is on. Columns will default to use _WidthFixed or _WidthAutoResize policy (if Resizable is off). Read description above for more details. ImGuiTableFlags_SameWidths = 1 << 15, // Make all columns the same widths which is useful with Fixed columns policy (but granted by default with Stretch policy + no resize). Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible and disable ImGuiTableFlags_Resizable. - ImGuiTableFlags_NoHeadersWidth = 1 << 16, // Disable headers' contribution to automatic width calculation. ImGuiTableFlags_NoHostExtendY = 1 << 17, // Disable extending table past the limit set by outer_size.y. Only meaningful when neither ScrollX nor ScrollY are set (data below the limit will be clipped and not visible) ImGuiTableFlags_NoKeepColumnsVisible = 1 << 18, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. ImGuiTableFlags_PreciseWidths = 1 << 19, // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth. + // Clipping ImGuiTableFlags_NoClip = 1 << 20, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze(). // Padding ImGuiTableFlags_PadOuterX = 1 << 21, // Default if BordersOuterV is on. Enable outer-most padding. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index a4a2a4ed..4619997e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4742,7 +4742,6 @@ static void ShowDemoWindowTables() if (ImGui::CheckboxFlags("ImGuiTableFlags_ColumnsWidthFixed", &flags, ImGuiTableFlags_ColumnsWidthFixed)) flags &= ~ImGuiTableFlags_ColumnsWidthStretch; // Can't specify both sizing polices so we clear the other ImGui::SameLine(); HelpMarker("[Default if ScrollX is on]\nEnlarge as needed: enable scrollbar if ScrollX is enabled, otherwise extend parent window's contents rectangle. Only Fixed columns allowed. Stretched columns will calculate their width assuming no scrolling."); - ImGui::CheckboxFlags("ImGuiTableFlags_NoHeadersWidth", &flags, ImGuiTableFlags_NoHeadersWidth); ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY); ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags, ImGuiTableFlags_NoKeepColumnsVisible); ImGui::SameLine(); HelpMarker("Only available if ScrollX is disabled."); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index be751218..3f58ca8d 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -727,7 +727,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) const float content_width_body = ImMax(column->ContentMaxXFrozen, column->ContentMaxXUnfrozen) - column->WorkMinX; const float content_width_headers = column->ContentMaxXHeadersIdeal - column->WorkMinX; float width_auto = content_width_body; - if (!(table->Flags & ImGuiTableFlags_NoHeadersWidth) && !(column->Flags & ImGuiTableColumnFlags_NoHeaderWidth)) + if (!(column->Flags & ImGuiTableColumnFlags_NoHeaderWidth)) width_auto = ImMax(width_auto, content_width_headers); width_auto = ImMax(width_auto, min_column_width); From a640698123fe2716f8d1f910a3acf6f6b12de17e Mon Sep 17 00:00:00 2001 From: nyorain Date: Mon, 21 Dec 2020 15:52:02 +0100 Subject: [PATCH 11/18] Clipper: Fixed incorrect end-list positioning when using ImGuiListClipper with 1 item (bug in 1.79). (#3663) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 481d0f77..9116ef67 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -80,6 +80,7 @@ Other Changes: - Drag and Drop: Fix drag and drop to tie same-size drop targets by choosen the later one. Fixes dragging into a full-window-sized dockspace inside a zero-padded window. (#3519, #2717) [@Black-Cat] - Checkbox: Added CheckboxFlags() helper with int* type. +- Clipper: Fixed incorrect end-list positioning when using ImGuiListClipper with 1 item (bug in 1.79). (#3663) [@nyorain] - InputText: Fixed updating cursor/selection position when a callback altered the buffer in a way where the byte count is unchanged but the decoded character count changes. (#3587) [@gqw] - InputText: Fixed swiching from single to multi-line while preserving same ID. diff --git a/imgui.cpp b/imgui.cpp index 322a5162..f2d56845 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2266,8 +2266,8 @@ bool ImGuiListClipper::Step() if (table && table->IsInsideRow) ImGui::TableEndRow(table); - // Reached end of list - if (DisplayEnd >= ItemsCount || GetSkipItemForListClipping()) + // No items + if (ItemsCount == 0 || GetSkipItemForListClipping()) { End(); return false; @@ -2320,6 +2320,13 @@ bool ImGuiListClipper::Step() StepNo = 2; } + // Reached end of list + if (DisplayEnd >= ItemsCount) + { + End(); + return false; + } + // Step 2: calculate the actual range of elements to display, and position the cursor before the first element if (StepNo == 2) { From defce31c2e557cbf2964706cf89f8e3a68e85679 Mon Sep 17 00:00:00 2001 From: Aiekick Date: Sun, 20 Dec 2020 20:04:48 +0100 Subject: [PATCH 12/18] Add ImDrawList::AddQuadBezierCurve(), ImDrawList::PathQuadBezierCurveTo() (#3127, #3664, #3665) --- imgui.h | 10 ++++--- imgui_demo.cpp | 12 ++++++-- imgui_draw.cpp | 79 +++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 85 insertions(+), 16 deletions(-) diff --git a/imgui.h b/imgui.h index 42aa8f6d..1beaec99 100644 --- a/imgui.h +++ b/imgui.h @@ -2357,8 +2357,9 @@ struct ImDrawList IMGUI_API void AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL); IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, bool closed, float thickness); IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col); // Note: Anti-aliased filling requires points to be in clockwise order. - IMGUI_API void AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); - + IMGUI_API void AddQuadBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments = 0); // Quad Bezier (3 control points) + IMGUI_API void AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points) + // Image primitives // - Read FAQ to understand what ImTextureID is. // - "p_min" and "p_max" represent the upper-left and lower-right corners of the rectangle. @@ -2374,8 +2375,9 @@ struct ImDrawList inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; } // Note: Anti-aliased filling requires points to be in clockwise order. inline void PathStroke(ImU32 col, bool closed, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, closed, thickness); _Path.Size = 0; } IMGUI_API void PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 10); - IMGUI_API void PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle - IMGUI_API void PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0); + IMGUI_API void PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle + IMGUI_API void PathQuadBezierCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments = 0); // Quad Bezier (3 control points) + IMGUI_API void PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0); // Cubic Bezier (4 control points) IMGUI_API void PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, ImDrawCornerFlags rounding_corners = ImDrawCornerFlags_All); // Advanced diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 4619997e..e1339a10 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -6917,11 +6917,13 @@ static void ShowExampleAppCustomRendering(bool* p_open) // Draw a bunch of primitives ImGui::Text("All primitives"); - static float sz = 36.0f; + static float sz = 33.0f; static float thickness = 3.0f; static int ngon_sides = 6; static bool circle_segments_override = false; static int circle_segments_override_v = 12; + static bool curve_segments_override = false; + static int curve_segments_override_v = 8; static ImVec4 colf = ImVec4(1.0f, 1.0f, 0.4f, 1.0f); ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 72.0f, "%.0f"); ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f"); @@ -6930,6 +6932,10 @@ static void ShowExampleAppCustomRendering(bool* p_open) ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); if (ImGui::SliderInt("Circle segments", &circle_segments_override_v, 3, 40)) circle_segments_override = true; + ImGui::Checkbox("##curvessegmentoverride", &curve_segments_override); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + if (ImGui::SliderInt("Curves segments", &curve_segments_override_v, 3, 40)) + curve_segments_override = true; ImGui::ColorEdit4("Color", &colf.x); const ImVec2 p = ImGui::GetCursorScreenPos(); @@ -6939,6 +6945,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) const ImDrawCornerFlags corners_all = ImDrawCornerFlags_All; const ImDrawCornerFlags corners_tl_br = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotRight; const int circle_segments = circle_segments_override ? circle_segments_override_v : 0; + const int curve_segments = curve_segments_override ? curve_segments_override_v : 0; float x = p.x + 4.0f; float y = p.y + 4.0f; for (int n = 0; n < 2; n++) @@ -6955,7 +6962,8 @@ static void ShowExampleAppCustomRendering(bool* p_open) draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th); x += sz + spacing; // Diagonal line - draw_list->AddBezierCurve(ImVec2(x, y), ImVec2(x + sz*1.3f, y + sz*0.3f), ImVec2(x + sz - sz*1.3f, y + sz - sz*0.3f), ImVec2(x + sz, y + sz), col, th); + draw_list->AddQuadBezierCurve(ImVec2(x, y + sz * 0.6), ImVec2(x + sz * 0.5, y - sz * 0.4), ImVec2(x + sz, y + sz), col, th, curve_segments); x += sz + spacing; // Quad Bezier (3 control points) + draw_list->AddBezierCurve(ImVec2(x, y), ImVec2(x + sz * 1.3f, y + sz * 0.3f), ImVec2(x + sz - sz * 1.3f, y + sz - sz * 0.3f), ImVec2(x + sz, y + sz), col, th, curve_segments); x = p.x + 4; y += sz + spacing; } diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 451bc2ff..e2f414c6 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1050,6 +1050,7 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa } } +// Cubic bezier ImVec2 ImBezierCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t) { float u = 1.0f - t; @@ -1060,13 +1061,14 @@ ImVec2 ImBezierCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const return ImVec2(w1*p1.x + w2*p2.x + w3*p3.x + w4*p4.x, w1*p1.y + w2*p2.y + w3*p3.y + w4*p4.y); } +// Cubic bezier // Closely mimics BezierClosestPointCasteljauStep() in imgui.cpp static void PathBezierToCasteljau(ImVector* path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level) { float dx = x4 - x1; float dy = y4 - y1; - float d2 = ((x2 - x4) * dy - (y2 - y4) * dx); - float d3 = ((x3 - x4) * dy - (y3 - y4) * dx); + float d2 = (x2 - x4) * dy - (y2 - y4) * dx; + float d3 = (x3 - x4) * dy - (y3 - y4) * dx; d2 = (d2 >= 0) ? d2 : -d2; d3 = (d3 >= 0) ? d3 : -d3; if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy)) @@ -1075,17 +1077,18 @@ static void PathBezierToCasteljau(ImVector* path, float x1, float y1, fl } else if (level < 10) { - float x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f; - float x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f; - float x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f; - float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f; - float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f; - float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f; - PathBezierToCasteljau(path, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1); - PathBezierToCasteljau(path, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1); + float x12 = (x1 + x2) * 0.5f, y12 = (y1 + y2) * 0.5f; + float x23 = (x2 + x3) * 0.5f, y23 = (y2 + y3) * 0.5f; + float x34 = (x3 + x4) * 0.5f, y34 = (y3 + y4) * 0.5f; + float x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f; + float x234 = (x23 + x34) * 0.5f, y234 = (y23 + y34) * 0.5f; + float x1234 = (x123 + x234) * 0.5f, y1234 = (y123 + y234) * 0.5f; + PathBezierToCasteljau(path, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1); + PathBezierToCasteljau(path, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1); } } +// Cubic bezier void ImDrawList::PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments) { ImVec2 p1 = _Path.back(); @@ -1101,6 +1104,51 @@ void ImDrawList::PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImV } } +// Quadratic bezier +ImVec2 ImQuadBezierCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float t) +{ + float u = 1.0f - t; + float w1 = u * u; + float w2 = 2 * u * t; + float w3 = t * t; + return ImVec2(w1 * p1.x + w2 * p2.x + w3 * p3.x, w1 * p1.y + w2 * p2.y + w3 * p3.y); +} + +// Quadratic bezier +static void PathQuadBezierToCasteljau(ImVector* path, float x1, float y1, float x2, float y2, float x3, float y3, float tess_tol, int level) +{ + float dx = x3 - x1, dy = y3 - y1; + float det = (x2 - x3) * dy - (y2 - y3) * dx; + if (det * det * 4.0f < tess_tol * (dx * dx + dy * dy)) + { + path->push_back(ImVec2(x3, y3)); + } + else if (level < 10) + { + float x12 = (x1 + x2) * 0.5f, y12 = (y1 + y2) * 0.5f; + float x23 = (x2 + x3) * 0.5f, y23 = (y2 + y3) * 0.5f; + float x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f; + PathQuadBezierToCasteljau(path, x1, y1, x12, y12, x123, y123, tess_tol, level + 1); + PathQuadBezierToCasteljau(path, x123, y123, x23, y23, x3, y3, tess_tol, level + 1); + } +} + +// Quadratic bezier +void ImDrawList::PathQuadBezierCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments) +{ + ImVec2 p1 = _Path.back(); + if (num_segments == 0) + { + PathQuadBezierToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, _Data->CurveTessellationTol, 0); // Auto-tessellated + } + else + { + float t_step = 1.0f / (float)num_segments; + for (int i_step = 1; i_step <= num_segments; i_step++) + _Path.push_back(ImQuadBezierCalc(p1, p2, p3, t_step * i_step)); + } +} + void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDrawCornerFlags rounding_corners) { rounding = ImMin(rounding, ImFabs(b.x - a.x) * ( ((rounding_corners & ImDrawCornerFlags_Top) == ImDrawCornerFlags_Top) || ((rounding_corners & ImDrawCornerFlags_Bot) == ImDrawCornerFlags_Bot) ? 0.5f : 1.0f ) - 1.0f); @@ -1310,6 +1358,17 @@ void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, in PathFillConvex(col); } +// Quad Bezier takes 3 controls points +void ImDrawList::AddQuadBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + PathLineTo(p1); + PathQuadBezierCurveTo(p2, p3, num_segments); + PathStroke(col, false, thickness); +} + // Cubic Bezier takes 4 controls points void ImDrawList::AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments) { From 550bfcfc598aad9668f8dc91b07cac5d702e7472 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 21 Dec 2020 16:25:56 +0100 Subject: [PATCH 13/18] Amend defce31 Add ImDrawList::AddQuadBezierCurve(), ImDrawList::PathQuadBezierCurveTo() (#3127, #3664, #3665) --- docs/CHANGELOG.txt | 2 ++ imgui.h | 12 ++++++------ imgui_demo.cpp | 19 ++++++++++++------- imgui_draw.cpp | 22 +++++++++++----------- imgui_internal.h | 1 + 5 files changed, 32 insertions(+), 24 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9116ef67..390c8f88 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -89,6 +89,8 @@ Other Changes: feedback and control of keyboard/gamepad navigation highlight and mouse hover disable flag. (#787, #2048) - Metrics: Fixed mishandling of ImDrawCmd::VtxOffset in wireframe mesh renderer. - Metrics: Rebranded as "Dear ImGui Metrics/Debugger" to clarify its purpose. +- ImDrawList: Added ImDrawList::AddQuadBezierCurve(), ImDrawList::PathQuadBezierCurveTo() quadratic bezier + helpers. (#3127, #3664, #3665) [@aiekick] - Fonts: Updated GetGlyphRangesJapanese() to include a larger 2999 ideograms selection of Joyo/Jinmeiyo kanjis, from the previous 1946 ideograms selection. This will consume a some more memory but be generally much more fitting for Japanese display, until we switch to a more dynamic atlas. (#3627) [@vaiorabbit] diff --git a/imgui.h b/imgui.h index 1beaec99..b7cb914f 100644 --- a/imgui.h +++ b/imgui.h @@ -2357,9 +2357,9 @@ struct ImDrawList IMGUI_API void AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL); IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, bool closed, float thickness); IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col); // Note: Anti-aliased filling requires points to be in clockwise order. - IMGUI_API void AddQuadBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments = 0); // Quad Bezier (3 control points) - IMGUI_API void AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points) - + IMGUI_API void AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points) + IMGUI_API void AddQuadBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments = 0); // Quadratic Bezier (3 control points) + // Image primitives // - Read FAQ to understand what ImTextureID is. // - "p_min" and "p_max" represent the upper-left and lower-right corners of the rectangle. @@ -2375,9 +2375,9 @@ struct ImDrawList inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; } // Note: Anti-aliased filling requires points to be in clockwise order. inline void PathStroke(ImU32 col, bool closed, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, closed, thickness); _Path.Size = 0; } IMGUI_API void PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 10); - IMGUI_API void PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle - IMGUI_API void PathQuadBezierCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments = 0); // Quad Bezier (3 control points) - IMGUI_API void PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0); // Cubic Bezier (4 control points) + IMGUI_API void PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle + IMGUI_API void PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0); // Cubic Bezier (4 control points) + IMGUI_API void PathQuadBezierCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments = 0); // Quadratic Bezier (3 control points) IMGUI_API void PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, ImDrawCornerFlags rounding_corners = ImDrawCornerFlags_All); // Advanced diff --git a/imgui_demo.cpp b/imgui_demo.cpp index e1339a10..2ee7c86b 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -6917,7 +6917,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) // Draw a bunch of primitives ImGui::Text("All primitives"); - static float sz = 33.0f; + static float sz = 36.0f; static float thickness = 3.0f; static int ngon_sides = 6; static bool circle_segments_override = false; @@ -6930,12 +6930,10 @@ static void ShowExampleAppCustomRendering(bool* p_open) ImGui::SliderInt("N-gon sides", &ngon_sides, 3, 12); ImGui::Checkbox("##circlesegmentoverride", &circle_segments_override); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - if (ImGui::SliderInt("Circle segments", &circle_segments_override_v, 3, 40)) - circle_segments_override = true; + circle_segments_override |= ImGui::SliderInt("Circle segments override", &circle_segments_override_v, 3, 40); ImGui::Checkbox("##curvessegmentoverride", &curve_segments_override); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - if (ImGui::SliderInt("Curves segments", &curve_segments_override_v, 3, 40)) - curve_segments_override = true; + curve_segments_override |= ImGui::SliderInt("Curves segments override", &curve_segments_override_v, 3, 40); ImGui::ColorEdit4("Color", &colf.x); const ImVec2 p = ImGui::GetCursorScreenPos(); @@ -6962,8 +6960,15 @@ static void ShowExampleAppCustomRendering(bool* p_open) draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th); x += sz + spacing; // Diagonal line - draw_list->AddQuadBezierCurve(ImVec2(x, y + sz * 0.6), ImVec2(x + sz * 0.5, y - sz * 0.4), ImVec2(x + sz, y + sz), col, th, curve_segments); x += sz + spacing; // Quad Bezier (3 control points) - draw_list->AddBezierCurve(ImVec2(x, y), ImVec2(x + sz * 1.3f, y + sz * 0.3f), ImVec2(x + sz - sz * 1.3f, y + sz - sz * 0.3f), ImVec2(x + sz, y + sz), col, th, curve_segments); + + // Quadratic Bezier Curve (3 control points) + ImVec2 cp3[3] = { ImVec2(x, y + sz * 0.6f), ImVec2(x + sz * 0.5f, y - sz * 0.4f), ImVec2(x + sz, y + sz) }; + draw_list->AddQuadBezierCurve(cp3[0], cp3[1], cp3[2], col, th, curve_segments); x += sz + spacing; + + // Cubic Bezier Curve (4 control points) + ImVec2 cp4[4] = { ImVec2(x, y), ImVec2(x + sz * 1.3f, y + sz * 0.3f), ImVec2(x + sz - sz * 1.3f, y + sz - sz * 0.3f), ImVec2(x + sz, y + sz) }; + draw_list->AddBezierCurve(cp4[0], cp4[1], cp4[2], cp4[3], col, th, curve_segments); + x = p.x + 4; y += sz + spacing; } diff --git a/imgui_draw.cpp b/imgui_draw.cpp index e2f414c6..6799977b 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1358,17 +1358,6 @@ void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, in PathFillConvex(col); } -// Quad Bezier takes 3 controls points -void ImDrawList::AddQuadBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - PathLineTo(p1); - PathQuadBezierCurveTo(p2, p3, num_segments); - PathStroke(col, false, thickness); -} - // Cubic Bezier takes 4 controls points void ImDrawList::AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments) { @@ -1380,6 +1369,17 @@ void ImDrawList::AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2 PathStroke(col, false, thickness); } +// Quadratic Bezier takes 3 controls points +void ImDrawList::AddQuadBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + PathLineTo(p1); + PathQuadBezierCurveTo(p2, p3, num_segments); + PathStroke(col, false, thickness); +} + void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect) { if ((col & IM_COL32_A_MASK) == 0) diff --git a/imgui_internal.h b/imgui_internal.h index 0d5be716..f18a193f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -399,6 +399,7 @@ static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) IMGUI_API ImVec2 ImBezierCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t); // Cubic Bezier IMGUI_API ImVec2 ImBezierClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments); // For curves with explicit number of segments IMGUI_API ImVec2 ImBezierClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol);// For auto-tessellated curves you can use tess_tol = style.CurveTessellationTol +IMGUI_API ImVec2 ImQuadBezierCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float t); // Quadratic Bezier IMGUI_API ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p); IMGUI_API bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); IMGUI_API ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); From 4d8e839ddf3786f11390002b86218419311843fb Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 21 Dec 2020 17:11:33 +0100 Subject: [PATCH 14/18] Renamed ImDrawList::AddBezierCurve() to ImDrawList::AddBezierCubic(), ImDrawList::PathBezierCurveTo() to ImDrawList::PathBezierCubicCurveTo(). (#3127, #3664, #3665) Renamed corresponding internal functions as well. --- docs/CHANGELOG.txt | 2 + imgui.cpp | 15 ++++---- imgui.h | 13 +++++-- imgui_demo.cpp | 4 +- imgui_draw.cpp | 94 ++++++++++++++++++++++------------------------ imgui_internal.h | 8 ++-- 6 files changed, 69 insertions(+), 67 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 390c8f88..47cc42bd 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -39,6 +39,8 @@ Breaking Changes: - Added imgui_tables.cpp file! Manually constructed project files will need the new file added! - Backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/. (#3513) +- Renamed ImDrawList::AddBezierCurve() to ImDrawList::AddBezierCubic(). Kept inline redirection function (will obsolete). +- Renamed ImDrawList::PathBezierCurveTo() to ImDrawList::PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete). - Removed redirecting functions/enums names that were marked obsolete in 1.60 (April 2017): - io.RenderDrawListsFn pointer -> use ImGui::GetDrawData() value and call the render function of your backend - ImGui::IsAnyWindowFocused() -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow) diff --git a/imgui.cpp b/imgui.cpp index f2d56845..27ade591 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -371,6 +371,7 @@ CODE When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete). - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added! - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API. - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures @@ -1120,7 +1121,7 @@ void ImGuiIO::ClearInputCharacters() // [SECTION] MISC HELPERS/UTILITIES (Geometry functions) //----------------------------------------------------------------------------- -ImVec2 ImBezierClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments) +ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments) { IM_ASSERT(num_segments > 0); // Use ImBezierClosestPointCasteljau() ImVec2 p_last = p1; @@ -1129,7 +1130,7 @@ ImVec2 ImBezierClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3 float t_step = 1.0f / (float)num_segments; for (int i_step = 1; i_step <= num_segments; i_step++) { - ImVec2 p_current = ImBezierCalc(p1, p2, p3, p4, t_step * i_step); + ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step); ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p); float dist2 = ImLengthSqr(p - p_line); if (dist2 < p_closest_dist2) @@ -1143,7 +1144,7 @@ ImVec2 ImBezierClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3 } // Closely mimics PathBezierToCasteljau() in imgui_draw.cpp -static void BezierClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_closest, ImVec2& p_last, float& p_closest_dist2, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level) +static void ImBezierCubicClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_closest, ImVec2& p_last, float& p_closest_dist2, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level) { float dx = x4 - x1; float dy = y4 - y1; @@ -1171,20 +1172,20 @@ static void BezierClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_closest, float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f; float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f; float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f; - BezierClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1); - BezierClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1); + ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1); + ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1); } } // tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol // Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically. -ImVec2 ImBezierClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol) +ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol) { IM_ASSERT(tess_tol > 0.0f); ImVec2 p_last = p1; ImVec2 p_closest; float p_closest_dist2 = FLT_MAX; - BezierClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0); + ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0); return p_closest; } diff --git a/imgui.h b/imgui.h index b7cb914f..6b54ae06 100644 --- a/imgui.h +++ b/imgui.h @@ -2357,8 +2357,8 @@ struct ImDrawList IMGUI_API void AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL); IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, bool closed, float thickness); IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col); // Note: Anti-aliased filling requires points to be in clockwise order. - IMGUI_API void AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points) - IMGUI_API void AddQuadBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments = 0); // Quadratic Bezier (3 control points) + IMGUI_API void AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points) + IMGUI_API void AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments = 0); // Quadratic Bezier (3 control points) // Image primitives // - Read FAQ to understand what ImTextureID is. @@ -2376,8 +2376,8 @@ struct ImDrawList inline void PathStroke(ImU32 col, bool closed, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, closed, thickness); _Path.Size = 0; } IMGUI_API void PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 10); IMGUI_API void PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle - IMGUI_API void PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0); // Cubic Bezier (4 control points) - IMGUI_API void PathQuadBezierCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments = 0); // Quadratic Bezier (3 control points) + IMGUI_API void PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0); // Cubic Bezier (4 control points) + IMGUI_API void PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments = 0); // Quadratic Bezier (3 control points) IMGUI_API void PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, ImDrawCornerFlags rounding_corners = ImDrawCornerFlags_All); // Advanced @@ -2407,6 +2407,11 @@ struct ImDrawList inline void PrimWriteIdx(ImDrawIdx idx) { *_IdxWritePtr = idx; _IdxWritePtr++; } inline void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } // Write vertex with unique index +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + inline void AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0) { AddBezierCubic(p1, p2, p3, p4, col, thickness, num_segments); } + inline void PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0) { PathBezierCubicCurveTo(p2, p3, p4, num_segments); } +#endif + // [Internal helpers] IMGUI_API void _ResetForNewFrame(); IMGUI_API void _ClearFreeMemory(); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 2ee7c86b..f890a742 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -6963,11 +6963,11 @@ static void ShowExampleAppCustomRendering(bool* p_open) // Quadratic Bezier Curve (3 control points) ImVec2 cp3[3] = { ImVec2(x, y + sz * 0.6f), ImVec2(x + sz * 0.5f, y - sz * 0.4f), ImVec2(x + sz, y + sz) }; - draw_list->AddQuadBezierCurve(cp3[0], cp3[1], cp3[2], col, th, curve_segments); x += sz + spacing; + draw_list->AddBezierQuadratic(cp3[0], cp3[1], cp3[2], col, th, curve_segments); x += sz + spacing; // Cubic Bezier Curve (4 control points) ImVec2 cp4[4] = { ImVec2(x, y), ImVec2(x + sz * 1.3f, y + sz * 0.3f), ImVec2(x + sz - sz * 1.3f, y + sz - sz * 0.3f), ImVec2(x + sz, y + sz) }; - draw_list->AddBezierCurve(cp4[0], cp4[1], cp4[2], cp4[3], col, th, curve_segments); + draw_list->AddBezierCubic(cp4[0], cp4[1], cp4[2], cp4[3], col, th, curve_segments); x = p.x + 4; y += sz + spacing; diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 6799977b..fc6d4816 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1050,20 +1050,27 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa } } -// Cubic bezier -ImVec2 ImBezierCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t) +ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t) { float u = 1.0f - t; - float w1 = u*u*u; - float w2 = 3*u*u*t; - float w3 = 3*u*t*t; - float w4 = t*t*t; - return ImVec2(w1*p1.x + w2*p2.x + w3*p3.x + w4*p4.x, w1*p1.y + w2*p2.y + w3*p3.y + w4*p4.y); + float w1 = u * u * u; + float w2 = 3 * u * u * t; + float w3 = 3 * u * t * t; + float w4 = t * t * t; + return ImVec2(w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x, w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y); } -// Cubic bezier -// Closely mimics BezierClosestPointCasteljauStep() in imgui.cpp -static void PathBezierToCasteljau(ImVector* path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level) +ImVec2 ImBezierQuadraticCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float t) +{ + float u = 1.0f - t; + float w1 = u * u; + float w2 = 2 * u * t; + float w3 = t * t; + return ImVec2(w1 * p1.x + w2 * p2.x + w3 * p3.x, w1 * p1.y + w2 * p2.y + w3 * p3.y); +} + +// Closely mimics ImBezierCubicClosestPointCasteljau() in imgui.cpp +static void PathBezierCubicCurveToCasteljau(ImVector* path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level) { float dx = x4 - x1; float dy = y4 - y1; @@ -1083,39 +1090,12 @@ static void PathBezierToCasteljau(ImVector* path, float x1, float y1, fl float x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f; float x234 = (x23 + x34) * 0.5f, y234 = (y23 + y34) * 0.5f; float x1234 = (x123 + x234) * 0.5f, y1234 = (y123 + y234) * 0.5f; - PathBezierToCasteljau(path, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1); - PathBezierToCasteljau(path, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1); + PathBezierCubicCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1); + PathBezierCubicCurveToCasteljau(path, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1); } } -// Cubic bezier -void ImDrawList::PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments) -{ - ImVec2 p1 = _Path.back(); - if (num_segments == 0) - { - PathBezierToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, _Data->CurveTessellationTol, 0); // Auto-tessellated - } - else - { - float t_step = 1.0f / (float)num_segments; - for (int i_step = 1; i_step <= num_segments; i_step++) - _Path.push_back(ImBezierCalc(p1, p2, p3, p4, t_step * i_step)); - } -} - -// Quadratic bezier -ImVec2 ImQuadBezierCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float t) -{ - float u = 1.0f - t; - float w1 = u * u; - float w2 = 2 * u * t; - float w3 = t * t; - return ImVec2(w1 * p1.x + w2 * p2.x + w3 * p3.x, w1 * p1.y + w2 * p2.y + w3 * p3.y); -} - -// Quadratic bezier -static void PathQuadBezierToCasteljau(ImVector* path, float x1, float y1, float x2, float y2, float x3, float y3, float tess_tol, int level) +static void PathBezierQuadraticCurveToCasteljau(ImVector* path, float x1, float y1, float x2, float y2, float x3, float y3, float tess_tol, int level) { float dx = x3 - x1, dy = y3 - y1; float det = (x2 - x3) * dy - (y2 - y3) * dx; @@ -1128,24 +1108,38 @@ static void PathQuadBezierToCasteljau(ImVector* path, float x1, float y1 float x12 = (x1 + x2) * 0.5f, y12 = (y1 + y2) * 0.5f; float x23 = (x2 + x3) * 0.5f, y23 = (y2 + y3) * 0.5f; float x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f; - PathQuadBezierToCasteljau(path, x1, y1, x12, y12, x123, y123, tess_tol, level + 1); - PathQuadBezierToCasteljau(path, x123, y123, x23, y23, x3, y3, tess_tol, level + 1); + PathBezierQuadraticCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, tess_tol, level + 1); + PathBezierQuadraticCurveToCasteljau(path, x123, y123, x23, y23, x3, y3, tess_tol, level + 1); } } -// Quadratic bezier -void ImDrawList::PathQuadBezierCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments) +void ImDrawList::PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments) { ImVec2 p1 = _Path.back(); if (num_segments == 0) { - PathQuadBezierToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, _Data->CurveTessellationTol, 0); // Auto-tessellated + PathBezierCubicCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, _Data->CurveTessellationTol, 0); // Auto-tessellated } else { float t_step = 1.0f / (float)num_segments; for (int i_step = 1; i_step <= num_segments; i_step++) - _Path.push_back(ImQuadBezierCalc(p1, p2, p3, t_step * i_step)); + _Path.push_back(ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step)); + } +} + +void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments) +{ + ImVec2 p1 = _Path.back(); + if (num_segments == 0) + { + PathBezierQuadraticCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, _Data->CurveTessellationTol, 0);// Auto-tessellated + } + else + { + float t_step = 1.0f / (float)num_segments; + for (int i_step = 1; i_step <= num_segments; i_step++) + _Path.push_back(ImBezierQuadraticCalc(p1, p2, p3, t_step * i_step)); } } @@ -1359,24 +1353,24 @@ void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, in } // Cubic Bezier takes 4 controls points -void ImDrawList::AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments) +void ImDrawList::AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments) { if ((col & IM_COL32_A_MASK) == 0) return; PathLineTo(p1); - PathBezierCurveTo(p2, p3, p4, num_segments); + PathBezierCubicCurveTo(p2, p3, p4, num_segments); PathStroke(col, false, thickness); } // Quadratic Bezier takes 3 controls points -void ImDrawList::AddQuadBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments) +void ImDrawList::AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments) { if ((col & IM_COL32_A_MASK) == 0) return; PathLineTo(p1); - PathQuadBezierCurveTo(p2, p3, num_segments); + PathBezierQuadraticCurveTo(p2, p3, num_segments); PathStroke(col, false, thickness); } diff --git a/imgui_internal.h b/imgui_internal.h index f18a193f..5a0c9104 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -396,10 +396,10 @@ static inline float ImLinearSweep(float current, float target, float speed) static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } // Helpers: Geometry -IMGUI_API ImVec2 ImBezierCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t); // Cubic Bezier -IMGUI_API ImVec2 ImBezierClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments); // For curves with explicit number of segments -IMGUI_API ImVec2 ImBezierClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol);// For auto-tessellated curves you can use tess_tol = style.CurveTessellationTol -IMGUI_API ImVec2 ImQuadBezierCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float t); // Quadratic Bezier +IMGUI_API ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t); +IMGUI_API ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments); // For curves with explicit number of segments +IMGUI_API ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol);// For auto-tessellated curves you can use tess_tol = style.CurveTessellationTol +IMGUI_API ImVec2 ImBezierQuadraticCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float t); IMGUI_API ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p); IMGUI_API bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); IMGUI_API ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); From 2e48c2da8147afd04edbacb49efaeb9bcc0a0561 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 21 Dec 2020 19:09:11 +0100 Subject: [PATCH 15/18] Removed redirecting functions/enums names that were marked obsolete in 1.63 (August 2018) + tables tweaks. --- docs/CHANGELOG.txt | 7 ++++++- imgui.cpp | 5 +++++ imgui.h | 35 +++++++++++++---------------------- imgui_demo.cpp | 12 +++++++----- imgui_tables.cpp | 13 +++++++------ 5 files changed, 38 insertions(+), 34 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 47cc42bd..9797ddcd 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,7 +41,7 @@ Breaking Changes: - Backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/. (#3513) - Renamed ImDrawList::AddBezierCurve() to ImDrawList::AddBezierCubic(). Kept inline redirection function (will obsolete). - Renamed ImDrawList::PathBezierCurveTo() to ImDrawList::PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete). -- Removed redirecting functions/enums names that were marked obsolete in 1.60 (April 2017): +- Removed redirecting functions/enums names that were marked obsolete in 1.60 (April 2018): - io.RenderDrawListsFn pointer -> use ImGui::GetDrawData() value and call the render function of your backend - ImGui::IsAnyWindowFocused() -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow) - ImGui::IsAnyWindowHovered() -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow) @@ -50,6 +50,11 @@ Breaking Changes: - Removed redirecting functions/enums names that were marked obsolete in 1.61 (May 2018): - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X was value for decimal_precision. - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter. +- Removed redirecting functions/enums names that were marked obsolete in 1.63 (August 2018): + - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit(). + - ImGuiCol_ModalWindowDarkening -> use ImGuiCol_ModalWindowDimBg + - ImGuiInputTextCallback -> use ImGuiTextEditCallback + - ImGuiInputTextCallbackData -> use ImGuiTextEditCallbackData - If you were still using the old names, while you are cleaning up, considering enabling IMGUI_DISABLE_OBSOLETE_FUNCTIONS in imconfig.h even temporarily to have a pass at finding and removing up old API calls, if any remaining. diff --git a/imgui.cpp b/imgui.cpp index 27ade591..b05beab3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -371,6 +371,11 @@ CODE When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018): + - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit(). + - ImGuiCol_ModalWindowDarkening -> use ImGuiCol_ModalWindowDimBg + - ImGuiInputTextCallback -> use ImGuiTextEditCallback + - ImGuiInputTextCallbackData -> use ImGuiTextEditCallbackData - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete). - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added! - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API. diff --git a/imgui.h b/imgui.h index 6b54ae06..a094a66c 100644 --- a/imgui.h +++ b/imgui.h @@ -1074,21 +1074,21 @@ enum ImGuiTableFlags_ ImGuiTableFlags_ColumnsWidthStretch = 1 << 13, // Default if ScrollX is off. Columns will default to use _WidthStretch. Read description above for more details. ImGuiTableFlags_ColumnsWidthFixed = 1 << 14, // Default if ScrollX is on. Columns will default to use _WidthFixed or _WidthAutoResize policy (if Resizable is off). Read description above for more details. ImGuiTableFlags_SameWidths = 1 << 15, // Make all columns the same widths which is useful with Fixed columns policy (but granted by default with Stretch policy + no resize). Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible and disable ImGuiTableFlags_Resizable. - ImGuiTableFlags_NoHostExtendY = 1 << 17, // Disable extending table past the limit set by outer_size.y. Only meaningful when neither ScrollX nor ScrollY are set (data below the limit will be clipped and not visible) - ImGuiTableFlags_NoKeepColumnsVisible = 1 << 18, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. - ImGuiTableFlags_PreciseWidths = 1 << 19, // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth. + ImGuiTableFlags_NoHostExtendY = 1 << 16, // Disable extending table past the limit set by outer_size.y. Only meaningful when neither ScrollX nor ScrollY are set (data below the limit will be clipped and not visible) + ImGuiTableFlags_NoKeepColumnsVisible = 1 << 17, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. + ImGuiTableFlags_PreciseWidths = 1 << 18, // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth. // Clipping - ImGuiTableFlags_NoClip = 1 << 20, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze(). + ImGuiTableFlags_NoClip = 1 << 19, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze(). // Padding - ImGuiTableFlags_PadOuterX = 1 << 21, // Default if BordersOuterV is on. Enable outer-most padding. - ImGuiTableFlags_NoPadOuterX = 1 << 22, // Default if BordersOuterV is off. Disable outer-most padding. - ImGuiTableFlags_NoPadInnerX = 1 << 23, // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off). + ImGuiTableFlags_PadOuterX = 1 << 20, // Default if BordersOuterV is on. Enable outer-most padding. + ImGuiTableFlags_NoPadOuterX = 1 << 21, // Default if BordersOuterV is off. Disable outer-most padding. + ImGuiTableFlags_NoPadInnerX = 1 << 22, // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off). // Scrolling - ImGuiTableFlags_ScrollX = 1 << 24, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this create a child window, ScrollY is currently generally recommended when using ScrollX. - ImGuiTableFlags_ScrollY = 1 << 25, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. + ImGuiTableFlags_ScrollX = 1 << 23, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this create a child window, ScrollY is currently generally recommended when using ScrollX. + ImGuiTableFlags_ScrollY = 1 << 24, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. // Sorting - ImGuiTableFlags_SortMulti = 1 << 26, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). - ImGuiTableFlags_SortTristate = 1 << 27 // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). + ImGuiTableFlags_SortMulti = 1 << 25, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). + ImGuiTableFlags_SortTristate = 1 << 26 // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). }; // Flags for ImGui::TableSetupColumn() @@ -1392,11 +1392,6 @@ enum ImGuiCol_ ImGuiCol_NavWindowingDimBg, // Darken/colorize entire screen behind the CTRL+TAB window list, when active ImGuiCol_ModalWindowDimBg, // Darken/colorize entire screen behind a modal window, when one is active ImGuiCol_COUNT - - // Obsolete names (will be removed) -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiCol_ModalWindowDarkening = ImGuiCol_ModalWindowDimBg // [renamed in 1.63] -#endif }; // Enumeration for PushStyleVar() / PopStyleVar() to temporarily modify the ImGuiStyle structure. @@ -1726,8 +1721,8 @@ struct ImGuiIO // Miscellaneous options bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations. - bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl (was called io.OptMacOSXBehaviors prior to 1.63) - bool ConfigInputTextCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. (was called: io.OptCursorBlink prior to 1.63) + bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl. + bool ConfigInputTextCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag) bool ConfigWindowsMoveFromTitleBarOnly; // = false // [BETA] Set to true to only allow moving windows when clicked+dragged from the title bar. Windows without a title bar are not affected. float ConfigMemoryCompactTimer; // = 60.0f // [BETA] Free transient windows/tables memory buffers when unused for given amount of time. Set to -1.0f to disable. @@ -1960,11 +1955,7 @@ namespace ImGui static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } // OBSOLETED in 1.66 (from Sep 2018) static inline void SetScrollHere(float center_ratio=0.5f){ SetScrollHereY(center_ratio); } - // OBSOLETED in 1.63 (between Aug 2018 and Sept 2018) - static inline bool IsItemDeactivatedAfterChange() { return IsItemDeactivatedAfterEdit(); } } -typedef ImGuiInputTextCallback ImGuiTextEditCallback; // OBSOLETED in 1.63 (from Aug 2018): made the names consistent -typedef ImGuiInputTextCallbackData ImGuiTextEditCallbackData; #endif //----------------------------------------------------------------------------- diff --git a/imgui_demo.cpp b/imgui_demo.cpp index f890a742..9a7e68f2 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3594,8 +3594,10 @@ static void ShowDemoWindowTables() "Fixed-width columns generally makes more sense if you want to use horizontal scrolling.\n\n" "Double-click a column border to auto-fit the column to its contents."); static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_ColumnsWidthFixed | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody; - //ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX); // FIXME-TABLE: Explain or fix the effect of enable Scroll on outer_size - if (ImGui::BeginTable("##table1", 3, flags)) + static bool use_all_width = true; + ImGui::Checkbox("Use all width", &use_all_width); + + if (ImGui::BeginTable("##table1", 3, flags, ImVec2(use_all_width ? -FLT_MIN : 0.0f, 0.0f))) { for (int row = 0; row < 5; row++) { @@ -3663,7 +3665,7 @@ static void ShowDemoWindowTables() if (ImGui::TreeNode("Reorderable, hideable, with headers")) { HelpMarker("Click and drag column headers to reorder columns.\n\nYou can also right-click on a header to open a context menu."); - static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody; + static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV; PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); ImGui::CheckboxFlags("ImGuiTableFlags_Reorderable", &flags, ImGuiTableFlags_Reorderable); @@ -4026,7 +4028,7 @@ static void ShowDemoWindowTables() { HelpMarker("This demonstrate embedding a table into another table cell."); - if (ImGui::BeginTable("recurse1", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable)) + if (ImGui::BeginTable("recurse1", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) { ImGui::TableSetupColumn("A0"); ImGui::TableSetupColumn("A1"); @@ -4036,7 +4038,7 @@ static void ShowDemoWindowTables() ImGui::Text("A0 Cell 0"); { float rows_height = TEXT_BASE_HEIGHT * 2; - if (ImGui::BeginTable("recurse2", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable)) + if (ImGui::BeginTable("recurse2", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) { ImGui::TableSetupColumn("B0"); ImGui::TableSetupColumn("B1"); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 3f58ca8d..7c5d081f 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -2295,10 +2295,11 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) void ImGui::TableDrawBorders(ImGuiTable* table) { ImGuiWindow* inner_window = table->InnerWindow; - table->DrawSplitter.SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_BG0); if (inner_window->Hidden || !table->HostClipRect.Overlaps(table->InnerClipRect)) return; + ImDrawList* inner_drawlist = inner_window->DrawList; + table->DrawSplitter.SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0); inner_drawlist->PushClipRect(table->Bg0ClipRectForDrawCmd.Min, table->Bg0ClipRectForDrawCmd.Max, false); // Draw inner border and resizing feedback @@ -2318,12 +2319,12 @@ void ImGui::TableDrawBorders(ImGuiTable* table) const bool is_hovered = (table->HoveredColumnBorder == column_n); const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent); const bool is_resizable = (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_)) == 0; + const bool is_frozen_separator = (table->FreezeColumnsCount != -1 && table->FreezeColumnsCount == order_n + 1); if (column->MaxX > table->InnerClipRect.Max.x && !is_resized)// && is_hovered) continue; - if (column->NextEnabledColumn == -1 && !is_resizable) - if ((table->Flags & ImGuiTableFlags_SameWidths) == 0) - continue; + if (column->NextEnabledColumn == -1 && !is_resizable && (table->Flags & ImGuiTableFlags_SameWidths) == 0) + continue; if (column->MaxX <= column->ClipRect.Min.x) // FIXME-TABLE FIXME-STYLE: Assume BorderSize==1, this is problematic if we want to increase the border size.. continue; @@ -2331,7 +2332,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) // Always draw full height border when being resized/hovered, or on the delimitation of frozen column scrolling. ImU32 col; float draw_y2; - if (is_hovered || is_resized || (table->FreezeColumnsCount != -1 && table->FreezeColumnsCount == order_n + 1)) + if (is_hovered || is_resized || is_frozen_separator) { draw_y2 = draw_y2_body; col = is_resized ? GetColorU32(ImGuiCol_SeparatorActive) : is_hovered ? GetColorU32(ImGuiCol_SeparatorHovered) : table->BorderColorStrong; @@ -2348,7 +2349,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) } // Draw outer border - // FIXME-TABLE: could use AddRect or explicit VLine/HLine helper? + // FIXME: could use AddRect or explicit VLine/HLine helper? if (table->Flags & ImGuiTableFlags_BordersOuter) { // Display outer border offset by 1 which is a simple way to display it without adding an extra draw call From 1aa59f90d023cf6c21a9468c5d5401dd97e23f67 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 22 Dec 2020 14:57:43 +0100 Subject: [PATCH 16/18] Minor API comments and tweaks, standardize index used in imgui.h. Tables: tweaked TableSetupColumn() assert to use IM_ASSERT_USER_ERROR(). --- docs/CHANGELOG.txt | 4 ++ imgui.cpp | 12 ++-- imgui.h | 134 ++++++++++++++++++++++++--------------------- imgui_demo.cpp | 8 +-- imgui_tables.cpp | 6 +- 5 files changed, 92 insertions(+), 72 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9797ddcd..d0bf6534 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -132,6 +132,8 @@ Other Changes: VERSION 1.79 (Released 2020-10-08) ----------------------------------------------------------------------- +Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.79 + Breaking Changes: - Fonts: Removed ImFont::DisplayOffset in favor of ImFontConfig::GlyphOffset. DisplayOffset was applied @@ -224,6 +226,8 @@ Other Changes: VERSION 1.78 (Released 2020-08-18) ----------------------------------------------------------------------- +Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.78 + Breaking Changes: - Obsoleted use of the trailing 'float power=1.0f' parameter for those functions: [@Shironekoben, @ocornut] diff --git a/imgui.cpp b/imgui.cpp index b05beab3..c0809faa 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -124,7 +124,7 @@ CODE - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!) - Controls are automatically adjusted for OSX to match standard OSX text editing operations. - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard. - - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW + - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://dearimgui.org/controls_sheets PROGRAMMER GUIDE @@ -156,18 +156,20 @@ CODE HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI ---------------------------------------------- - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h) - - Or maintain your own branch where you have imconfig.h modified. + - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over master. + - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file. - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes. If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will likely be a comment about it. Please report any issue to the GitHub page! - - Try to keep your copy of dear imgui reasonably up to date. + - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file. + - Try to keep your copy of Dear ImGui reasonably up to date. GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE --------------------------------------------------------------- - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library. - - In the majority of cases you should be able to use unmodified backends files available in the examples/ folder. + - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder. - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system. It is recommended you build and statically link the .cpp files as part of your project and NOT as shared library (DLL). - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating Dear ImGui types with your own maths types. @@ -348,7 +350,7 @@ CODE 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone. Your code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.). - - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW. + - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://dearimgui.org/controls_sheets - If you need to share inputs between your game and the imgui parts, the easiest approach is to go all-or-nothing, with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved. - Mouse: diff --git a/imgui.h b/imgui.h index a094a66c..8f833d0b 100644 --- a/imgui.h +++ b/imgui.h @@ -19,25 +19,24 @@ /* Index of this file: -// Header mess -// Forward declarations and basic types -// ImGui API (Dear ImGui end-user API) -// Flags & Enumerations -// Memory allocations macros -// ImVector<> -// ImGuiStyle -// ImGuiIO -// Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs) -// Obsolete functions -// Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, ImColor) -// Draw List API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData) -// Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) +// [SECTION] Header mess +// [SECTION] Forward declarations and basic types +// [SECTION] Dear ImGui end-user API functions +// [SECTION] Flags & Enumerations +// [SECTION] Helpers: Memory allocations macros, ImVector<> +// [SECTION] ImGuiStyle +// [SECTION] ImGuiIO +// [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs) +// [SECTION] Obsolete functions +// [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, ImColor) +// [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData) +// [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) */ #pragma once -// Configuration file with compile-time options (edit imconfig.h or #define IMGUI_USER_CONFIG to your own filename) +// Configuration file with compile-time options (edit imconfig.h or '#define IMGUI_USER_CONFIG "myfilename.h" from your build system') #ifdef IMGUI_USER_CONFIG #include IMGUI_USER_CONFIG #endif @@ -48,7 +47,7 @@ Index of this file: #ifndef IMGUI_DISABLE //----------------------------------------------------------------------------- -// Header mess +// [SECTION] Header mess //----------------------------------------------------------------------------- // Includes @@ -86,11 +85,13 @@ Index of this file: #else #define IM_OFFSETOF(_TYPE,_MEMBER) ((size_t)&(((_TYPE*)0)->_MEMBER)) // Offset of _MEMBER within _TYPE. Old style macro. #endif + +// Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions. #if !defined(IMGUI_USE_STB_SPRINTF) && defined(__clang__) -#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) // Apply printf-style warnings to our formatting functions. +#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) #define IM_FMTLIST(FMT) __attribute__((format(printf, FMT, 0))) #elif !defined(IMGUI_USE_STB_SPRINTF) && defined(__GNUC__) && defined(__MINGW32__) -#define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1))) // Apply printf-style warnings to our formatting functions. +#define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1))) #define IM_FMTLIST(FMT) __attribute__((format(gnu_printf, FMT, 0))) #else #define IM_FMTARGS(FMT) @@ -106,12 +107,12 @@ Index of this file: #endif #elif defined(__GNUC__) #pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind -#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead +#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind +#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #endif //----------------------------------------------------------------------------- -// Forward declarations and basic types +// [SECTION] Forward declarations and basic types //----------------------------------------------------------------------------- // Forward declarations @@ -186,10 +187,10 @@ typedef int ImGuiWindowFlags; // -> enum ImGuiWindowFlags_ // Flags: f typedef void* ImTextureID; // User data for rendering backend to identify a texture. This is whatever to you want it to be! read the FAQ about ImTextureID for details. #endif typedef unsigned int ImGuiID; // A unique ID used by widgets, typically hashed from a stack of string. -typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data); -typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); +typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data); // Callback function for ImGui::InputText() +typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); // Callback function for ImGui::SetNextWindowSizeConstraints() -// Decoded character types +// Character types // (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display) typedef unsigned short ImWchar16; // A single decoded U16 character/code point. We encode them as multi bytes UTF-8 when used in strings. typedef unsigned int ImWchar32; // A single decoded U32 character/code point. We encode them as multi bytes UTF-8 when used in strings. @@ -234,8 +235,8 @@ struct ImVec2 // 4D vector (often used to store floating-point colors) struct ImVec4 { - float x, y, z, w; - ImVec4() { x = y = z = w = 0.0f; } + float x, y, z, w; + ImVec4() { x = y = z = w = 0.0f; } ImVec4(float _x, float _y, float _z, float _w) { x = _x; y = _y; z = _z; w = _w; } #ifdef IM_VEC4_CLASS_EXTRA IM_VEC4_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec4. @@ -243,8 +244,8 @@ struct ImVec4 }; //----------------------------------------------------------------------------- -// ImGui: Dear ImGui end-user API -// (This is a namespace. You can add extra ImGui:: functions in your own separate file. Please don't modify imgui source files!) +// [SECTION] Dear ImGui end-user API functions +// (Note that ImGui:: being a namespace, you can add extra ImGui:: functions in your own separate file. Please don't modify imgui source files!) //----------------------------------------------------------------------------- namespace ImGui @@ -266,19 +267,19 @@ namespace ImGui IMGUI_API ImDrawData* GetDrawData(); // valid after Render() and until the next call to NewFrame(). this is what you have to render. // Demo, Debug, Information - IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create Demo window (previously called ShowTestWindow). demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! - IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create About window. display Dear ImGui version, credits and build/system information. + IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create Demo window. demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create Metrics/Debugger window. display Dear ImGui internals: windows, draw commands, various internal state, etc. + IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create About window. display Dear ImGui version, credits and build/system information. IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style) IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles. IMGUI_API void ShowFontSelector(const char* label); // add font selector block (not a window), essentially a combo listing the loaded fonts. IMGUI_API void ShowUserGuide(); // add basic help/info block (not a window): how to manipulate ImGui as a end-user (mouse/keyboard controls). - IMGUI_API const char* GetVersion(); // get the compiled version string e.g. "1.23" (essentially the compiled value for IMGUI_VERSION) + IMGUI_API const char* GetVersion(); // get the compiled version string e.g. "1.80 WIP" (essentially the value for IMGUI_VERSION from the compiled version of imgui.cpp) // Styles IMGUI_API void StyleColorsDark(ImGuiStyle* dst = NULL); // new, recommended style (default) - IMGUI_API void StyleColorsClassic(ImGuiStyle* dst = NULL); // classic imgui style IMGUI_API void StyleColorsLight(ImGuiStyle* dst = NULL); // best used with borders and a custom, thicker font + IMGUI_API void StyleColorsClassic(ImGuiStyle* dst = NULL); // classic imgui style // Windows // - Begin() = push window to the stack and start appending to it. End() = pop window from the stack. @@ -338,20 +339,21 @@ namespace ImGui IMGUI_API void SetWindowFocus(const char* name); // set named window to be focused / top-most. use NULL to remove focus. // Content region - // - Those functions are bound to be redesigned soon (they are confusing, incomplete and return values in local window coordinates which increases confusion) - IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates + // - Retrieve available space from a given point. GetContentRegionAvail() is frequently useful. + // - Those functions are bound to be redesigned (they are confusing, incomplete and the Min/Max return values are in local window coordinates which increases confusion) IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() + IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min (roughly (0,0)-Scroll), in window coordinates IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates IMGUI_API float GetWindowContentRegionWidth(); // // Windows Scrolling - IMGUI_API float GetScrollX(); // get scrolling amount [0..GetScrollMaxX()] - IMGUI_API float GetScrollY(); // get scrolling amount [0..GetScrollMaxY()] - IMGUI_API float GetScrollMaxX(); // get maximum scrolling amount ~~ ContentSize.x - WindowSize.x - IMGUI_API float GetScrollMaxY(); // get maximum scrolling amount ~~ ContentSize.y - WindowSize.y - IMGUI_API void SetScrollX(float scroll_x); // set scrolling amount [0..GetScrollMaxX()] - IMGUI_API void SetScrollY(float scroll_y); // set scrolling amount [0..GetScrollMaxY()] + IMGUI_API float GetScrollX(); // get scrolling amount [0 .. GetScrollMaxX()] + IMGUI_API float GetScrollY(); // get scrolling amount [0 .. GetScrollMaxY()] + IMGUI_API void SetScrollX(float scroll_x); // set scrolling amount [0 .. GetScrollMaxX()] + IMGUI_API void SetScrollY(float scroll_y); // set scrolling amount [0 .. GetScrollMaxY()] + IMGUI_API float GetScrollMaxX(); // get maximum scrolling amount ~~ ContentSize.x - WindowSize.x - DecorationsSize.x + IMGUI_API float GetScrollMaxY(); // get maximum scrolling amount ~~ ContentSize.y - WindowSize.y - DecorationsSize.y IMGUI_API void SetScrollHereX(float center_x_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_x_ratio=0.0: left, 0.5: center, 1.0: right. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead. IMGUI_API void SetScrollHereY(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead. IMGUI_API void SetScrollFromPosX(float local_x, float center_x_ratio = 0.5f); // adjust scrolling amount to make given position visible. Generally GetCursorStartPos() + offset to compute a valid position. @@ -360,23 +362,16 @@ namespace ImGui // Parameters stacks (shared) IMGUI_API void PushFont(ImFont* font); // use NULL as a shortcut to push default font IMGUI_API void PopFont(); - IMGUI_API void PushStyleColor(ImGuiCol idx, ImU32 col); + IMGUI_API void PushStyleColor(ImGuiCol idx, ImU32 col); // modify a style color. always use this if you modify the style after NewFrame(). IMGUI_API void PushStyleColor(ImGuiCol idx, const ImVec4& col); IMGUI_API void PopStyleColor(int count = 1); - IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); - IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); + IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); // modify a style float variable. always use this if you modify the style after NewFrame(). + IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); // modify a style ImVec2 variable. always use this if you modify the style after NewFrame(). IMGUI_API void PopStyleVar(int count = 1); IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets IMGUI_API void PopAllowKeyboardFocus(); IMGUI_API void PushButtonRepeat(bool repeat); // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (using io.KeyRepeatDelay/io.KeyRepeatRate setting). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame. IMGUI_API void PopButtonRepeat(); - IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx); // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwise use GetColorU32() to get style color with style alpha baked in. - IMGUI_API ImFont* GetFont(); // get current font - IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied - IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API - IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f); // retrieve given style color with style alpha applied and optional extra alpha multiplier - IMGUI_API ImU32 GetColorU32(const ImVec4& col); // retrieve given color with style alpha applied - IMGUI_API ImU32 GetColorU32(ImU32 col); // retrieve given color with style alpha applied // Parameters stacks (current window) IMGUI_API void PushItemWidth(float item_width); // push width of items for common large "item+label" widgets. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side). 0.0f = default to ~2/3 of windows width, @@ -386,6 +381,15 @@ namespace ImGui IMGUI_API void PushTextWrapPos(float wrap_local_pos_x = 0.0f); // push word-wrapping position for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space IMGUI_API void PopTextWrapPos(); + // Style read access + IMGUI_API ImFont* GetFont(); // get current font + IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied + IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API + IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f); // retrieve given style color with style alpha applied and optional extra alpha multiplier, packed as a 32-bit value suitable for ImDrawList + IMGUI_API ImU32 GetColorU32(const ImVec4& col); // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList + IMGUI_API ImU32 GetColorU32(ImU32 col); // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList + IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx); // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwise use GetColorU32() to get style color with style alpha baked in. + // Cursor / Layout // - By "cursor" we mean the current output position. // - The typical widget behavior is to output themselves at the current cursor position, then move the cursor one line down. @@ -608,7 +612,7 @@ namespace ImGui IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true); // return true when activated + toggle (*p_selected) if p_selected != NULL // Tooltips - // - Tooltip are windows following the mouse which do not take focus away. + // - Tooltip are windows following the mouse. They do not take focus away. IMGUI_API void BeginTooltip(); // begin/append a tooltip window. to create full-featured tooltip (with any kind of items). IMGUI_API void EndTooltip(); IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip, typically use with ImGui::IsItemHovered(). override any previous call to SetTooltip(). @@ -738,7 +742,6 @@ namespace ImGui IMGUI_API void LogText(const char* fmt, ...) IM_FMTARGS(1); // pass text data straight to log (without being displayed) // Drag and Drop - // - [BETA API] API may evolve! // - If you stop calling BeginDragDropSource() the payload is preserved however it won't have a preview tooltip (we currently display a fallback "..." tooltip as replacement) IMGUI_API bool BeginDragDropSource(ImGuiDragDropFlags flags = 0); // call when the current item is active. If this return true, you can call SetDragDropPayload() + EndDragDropSource() IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t sz, ImGuiCond cond = 0); // type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. @@ -749,6 +752,7 @@ namespace ImGui IMGUI_API const ImGuiPayload* GetDragDropPayload(); // peek directly into the current payload from anywhere. may return NULL. use ImGuiPayload::IsDataType() to test for the payload type. // Clipping + // - Mouse hovering is affected by ImGui::PushClipRect() calls, unlike direct calls to ImDrawList::PushClipRect() which are render only. IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect); IMGUI_API void PopClipRect(); @@ -858,7 +862,7 @@ namespace ImGui } // namespace ImGui //----------------------------------------------------------------------------- -// Flags & Enumerations +// [SECTION] Flags & Enumerations //----------------------------------------------------------------------------- // Flags for ImGui::Begin() @@ -1277,7 +1281,7 @@ enum ImGuiKeyModFlags_ // Gamepad/Keyboard navigation // Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays. // Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Backend: set ImGuiBackendFlags_HasGamepad and fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). -// Read instructions in imgui.cpp for more details. Download PNG/PSD at http://goo.gl/9LgVZW. +// Read instructions in imgui.cpp for more details. Download PNG/PSD at http://dearimgui.org/controls_sheets. enum ImGuiNavInput_ { // Gamepad Mapping @@ -1547,7 +1551,10 @@ enum ImGuiCond_ }; //----------------------------------------------------------------------------- -// Helpers: Memory allocations macros +// [SECTION] Helpers: Memory allocations macros, ImVector<> +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- // IM_MALLOC(), IM_FREE(), IM_NEW(), IM_PLACEMENT_NEW(), IM_DELETE() // We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax. // Defining a custom placement new() with a custom parameter allows us to bypass including which on some platforms complains when user has disabled exceptions. @@ -1563,7 +1570,7 @@ inline void operator delete(void*, ImNewWrapper, void*) {} // This is only re template void IM_DELETE(T* p) { if (p) { p->~T(); ImGui::MemFree(p); } } //----------------------------------------------------------------------------- -// Helper: ImVector<> +// ImVector<> // Lightweight std::vector<>-like class to avoid dragging dependencies (also, some implementations of STL with debug enabled are absurdly slow, we bypass it so our code runs fast in debug). //----------------------------------------------------------------------------- // - You generally do NOT need to care or use this ever. But we need to make it available in imgui.h because some of our public structures are relying on it. @@ -1633,7 +1640,8 @@ struct ImVector }; //----------------------------------------------------------------------------- -// ImGuiStyle +// [SECTION] ImGuiStyle +//----------------------------------------------------------------------------- // You may modify the ImGui::GetStyle() main instance during initialization and before NewFrame(). // During the frame, use ImGui::PushStyleVar(ImGuiStyleVar_XXXX)/PopStyleVar() to alter the main style values, // and ImGui::PushStyleColor(ImGuiCol_XXX)/PopStyleColor() for colors. @@ -1687,7 +1695,8 @@ struct ImGuiStyle }; //----------------------------------------------------------------------------- -// ImGuiIO +// [SECTION] ImGuiIO +//----------------------------------------------------------------------------- // Communicate most settings and inputs/outputs to Dear ImGui using this structure. // Access via ImGui::GetIO(). Read 'Programmer guide' section in .cpp file for general usage. //----------------------------------------------------------------------------- @@ -1821,7 +1830,7 @@ struct ImGuiIO }; //----------------------------------------------------------------------------- -// Misc data structures +// [SECTION] Misc data structures //----------------------------------------------------------------------------- // Shared state of InputText(), passed as an argument to your callback when a ImGuiInputTextFlags_Callback* flag is used. @@ -1919,7 +1928,8 @@ struct ImGuiTableSortSpecs }; //----------------------------------------------------------------------------- -// Obsolete functions (Will be removed! Read 'API BREAKING CHANGES' section in imgui.cpp for details) +// [SECTION] Obsolete functions +// (Will be removed! Read 'API BREAKING CHANGES' section in imgui.cpp for details) // Please keep your copy of dear imgui up to date! Occasionally set '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in imconfig.h to stay ahead. //----------------------------------------------------------------------------- @@ -1959,7 +1969,7 @@ namespace ImGui #endif //----------------------------------------------------------------------------- -// Helpers +// [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, ImColor) //----------------------------------------------------------------------------- // Helper: Unicode defines @@ -2161,7 +2171,7 @@ struct ImColor }; //----------------------------------------------------------------------------- -// Draw List API (ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData) +// [SECTION] Drawing API (ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData) // Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList. //----------------------------------------------------------------------------- @@ -2435,7 +2445,7 @@ struct ImDrawData }; //----------------------------------------------------------------------------- -// Font API (ImFontConfig, ImFontGlyph, ImFontAtlasFlags, ImFontAtlas, ImFontGlyphRangesBuilder, ImFont) +// [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontAtlasFlags, ImFontAtlas, ImFontGlyphRangesBuilder, ImFont) //----------------------------------------------------------------------------- struct ImFontConfig diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9a7e68f2..6ef33591 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -5516,13 +5516,13 @@ void ImGui::ShowAboutWindow(bool* p_open) bool ImGui::ShowStyleSelector(const char* label) { static int style_idx = -1; - if (ImGui::Combo(label, &style_idx, "Classic\0Dark\0Light\0")) + if (ImGui::Combo(label, &style_idx, "Dark\0Light\0Classic\0")) { switch (style_idx) { - case 0: ImGui::StyleColorsClassic(); break; - case 1: ImGui::StyleColorsDark(); break; - case 2: ImGui::StyleColorsLight(); break; + case 0: ImGui::StyleColorsDark(); break; + case 1: ImGui::StyleColorsLight(); break; + case 2: ImGui::StyleColorsClassic(); break; } return true; } diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 7c5d081f..609ccb61 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1291,8 +1291,12 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo ImGuiTable* table = g.CurrentTable; IM_ASSERT(table != NULL && "Need to call TableSetupColumn() after BeginTable()!"); IM_ASSERT(table->IsLayoutLocked == false && "Need to call call TableSetupColumn() before first row!"); - IM_ASSERT(table->DeclColumnsCount >= 0 && table->DeclColumnsCount < table->ColumnsCount && "Called TableSetupColumn() too many times!"); IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()"); + if (table->DeclColumnsCount >= table->ColumnsCount) + { + IM_ASSERT_USER_ERROR(table->DeclColumnsCount < table->ColumnsCount, "Called TableSetupColumn() too many times!"); + return; + } ImGuiTableColumn* column = &table->Columns[table->DeclColumnsCount]; table->DeclColumnsCount++; From 972ca8166faade6c1cb0cd0a3cdcd50bb51c9efa Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 22 Dec 2020 16:40:52 +0100 Subject: [PATCH 17/18] Tables: extracted code for TableGetMaxColumnWidth(), fixing "stuck" resize being lossy as it used an older calculation for it and didn't honor e.g. _NoKeepColumnsVisible --- imgui.h | 2 +- imgui_internal.h | 1 + imgui_tables.cpp | 80 ++++++++++++++++++++++++++---------------------- 3 files changed, 45 insertions(+), 38 deletions(-) diff --git a/imgui.h b/imgui.h index 8f833d0b..e01bdc5d 100644 --- a/imgui.h +++ b/imgui.h @@ -1079,7 +1079,7 @@ enum ImGuiTableFlags_ ImGuiTableFlags_ColumnsWidthFixed = 1 << 14, // Default if ScrollX is on. Columns will default to use _WidthFixed or _WidthAutoResize policy (if Resizable is off). Read description above for more details. ImGuiTableFlags_SameWidths = 1 << 15, // Make all columns the same widths which is useful with Fixed columns policy (but granted by default with Stretch policy + no resize). Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible and disable ImGuiTableFlags_Resizable. ImGuiTableFlags_NoHostExtendY = 1 << 16, // Disable extending table past the limit set by outer_size.y. Only meaningful when neither ScrollX nor ScrollY are set (data below the limit will be clipped and not visible) - ImGuiTableFlags_NoKeepColumnsVisible = 1 << 17, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. + ImGuiTableFlags_NoKeepColumnsVisible = 1 << 17, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. Not recommended if columns are resizable. ImGuiTableFlags_PreciseWidths = 1 << 18, // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth. // Clipping ImGuiTableFlags_NoClip = 1 << 19, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze(). diff --git a/imgui_internal.h b/imgui_internal.h index 5a0c9104..51556f29 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2325,6 +2325,7 @@ namespace ImGui IMGUI_API const char* TableGetColumnName(const ImGuiTable* table, int column_n); IMGUI_API ImGuiID TableGetColumnResizeID(const ImGuiTable* table, int column_n, int instance_no = 0); IMGUI_API float TableGetMinColumnWidth(); + IMGUI_API float TableGetMaxColumnWidth(const ImGuiTable* table, int column_n); IMGUI_API void TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n); IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table); IMGUI_API void TableRemove(ImGuiTable* table); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 609ccb61..0c20f052 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -703,7 +703,6 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // [Part 3] Fix column flags. Calculate ideal width for columns. Count how many fixed/stretch columns we have and sum of weights. const float min_column_width = TableGetMinColumnWidth(); - const float min_column_distance = min_column_width + table->CellPaddingX * 2.0f + table->CellSpacingX1 + table->CellSpacingX2; int count_fixed = 0; // Number of columns that have fixed sizing policy (not stretched sizing policy) (this is NOT the opposite of count_resizable!) int count_resizable = 0; // Number of columns the user can resize (this is NOT the opposite of count_fixed!) float sum_weights_stretched = 0.0f; // Sum of all weights for weighted columns. @@ -895,42 +894,20 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) if (is_hovering_table && g.IO.MousePos.x >= column->ClipRect.Min.x && g.IO.MousePos.x < column->ClipRect.Max.x) table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n; - // Maximum width - float max_width = FLT_MAX; - if (table->Flags & ImGuiTableFlags_ScrollX) - { - // Frozen columns can't reach beyond visible width else scrolling will naturally break. - if (order_n < table->FreezeColumnsRequest) - { - max_width = (table->InnerClipRect.Max.x - (table->FreezeColumnsRequest - order_n) * min_column_distance) - offset_x; - max_width = max_width - table->OuterPaddingX - table->CellPaddingX - table->CellSpacingX2; - } - } - else if ((table->Flags & ImGuiTableFlags_NoKeepColumnsVisible) == 0) - { - // If horizontal scrolling if disabled, we apply a final lossless shrinking of columns in order to make - // sure they are all visible. Because of this we also know that all of the columns will always fit in - // table->WorkRect and therefore in table->InnerRect (because ScrollX is off) - // FIXME-TABLE: This is solved incorrectly but also quite a difficult problem to fix as we also want ClipRect width to match. - // See "table_width_distrib" and "table_width_keep_visible" tests - max_width = table->WorkRect.Max.x - (table->ColumnsEnabledCount - column->IndexWithinEnabledSet - 1) * min_column_distance - offset_x; - //max_width -= table->CellSpacingX1; - max_width -= table->CellSpacingX2; - max_width -= table->CellPaddingX * 2.0f; - max_width -= table->OuterPaddingX; - } + // Lock start position + column->MinX = offset_x; + + // Lock width based on start position and minimum/maximum width for this position + float max_width = TableGetMaxColumnWidth(table, column_n); column->WidthGiven = ImMin(column->WidthGiven, max_width); - - // Minimum width column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, min_column_width)); + column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f; - // Lock all our positions + // Lock other positions // - ClipRect.Min.x: Because merging draw commands doesn't compare min boundaries, we make ClipRect.Min.x match left bounds to be consistent regardless of merging. // - ClipRect.Max.x: using WorkMaxX instead of MaxX (aka including padding) makes things more consistent when resizing down, tho slightly detrimental to visibility in very-small column. // - ClipRect.Max.x: using MaxX makes it easier for header to receive hover highlight with no discontinuity and display sorting arrow. // - FIXME-TABLE: We want equal width columns to have equal (ClipRect.Max.x - WorkMinX) width, which means ClipRect.max.x cannot stray off host_clip_rect.Max.x else right-most column may appear shorter. - column->MinX = offset_x; - column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f; column->WorkMinX = column->MinX + table->CellPaddingX + table->CellSpacingX1; column->WorkMaxX = column->MaxX - table->CellPaddingX - table->CellSpacingX2; // Expected max column->ItemWidth = ImFloor(column->WidthGiven * 0.65f); @@ -1835,6 +1812,7 @@ void ImGui::TableEndCell(ImGuiTable* table) // [SECTION] Tables: Columns width management //------------------------------------------------------------------------- // - TableGetMinColumnWidth() [Internal] +// - TableGetMaxColumnWidth() [Internal] // - TableSetColumnWidth() // - TableSetColumnWidthAutoSingle() [Internal] // - TableSetColumnWidthAutoAll() [Internal] @@ -1849,6 +1827,37 @@ float ImGui::TableGetMinColumnWidth() return g.Style.FramePadding.x * 1.0f; } +// Maximum column content width given current layout. Use column->MinX so this value on a per-column basis. +float ImGui::TableGetMaxColumnWidth(const ImGuiTable* table, int column_n) +{ + const ImGuiTableColumn* column = &table->Columns[column_n]; + float max_width = FLT_MAX; + const float min_column_distance = TableGetMinColumnWidth() + table->CellPaddingX * 2.0f + table->CellSpacingX1 + table->CellSpacingX2; + if (table->Flags & ImGuiTableFlags_ScrollX) + { + // Frozen columns can't reach beyond visible width else scrolling will naturally break. + if (column->DisplayOrder < table->FreezeColumnsRequest) + { + max_width = (table->InnerClipRect.Max.x - (table->FreezeColumnsRequest - column->DisplayOrder) * min_column_distance) - column->MinX; + max_width = max_width - table->OuterPaddingX - table->CellPaddingX - table->CellSpacingX2; + } + } + else if ((table->Flags & ImGuiTableFlags_NoKeepColumnsVisible) == 0) + { + // If horizontal scrolling if disabled, we apply a final lossless shrinking of columns in order to make + // sure they are all visible. Because of this we also know that all of the columns will always fit in + // table->WorkRect and therefore in table->InnerRect (because ScrollX is off) + // FIXME-TABLE: This is solved incorrectly but also quite a difficult problem to fix as we also want ClipRect width to match. + // See "table_width_distrib" and "table_width_keep_visible" tests + max_width = table->WorkRect.Max.x - (table->ColumnsEnabledCount - column->IndexWithinEnabledSet - 1) * min_column_distance - column->MinX; + //max_width -= table->CellSpacingX1; + max_width -= table->CellSpacingX2; + max_width -= table->CellPaddingX * 2.0f; + max_width -= table->OuterPaddingX; + } + return max_width; +} + // 'width' = inner column width, without padding void ImGui::TableSetColumnWidth(int column_n, float width) { @@ -1859,14 +1868,11 @@ void ImGui::TableSetColumnWidth(int column_n, float width) ImGuiTableColumn* column_0 = &table->Columns[column_n]; float column_0_width = width; - // Constraints - const float min_width = TableGetMinColumnWidth(); - float max_width_0 = FLT_MAX; - if (!(table->Flags & ImGuiTableFlags_ScrollX)) - max_width_0 = (table->WorkRect.Max.x - column_0->MinX) - (table->ColumnsEnabledCount - (column_0->IndexWithinEnabledSet + 1)) * min_width; - column_0_width = ImClamp(column_0_width, min_width, max_width_0); - + // Apply constraints early // Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded) + const float min_width = TableGetMinColumnWidth(); + const float max_width = TableGetMaxColumnWidth(table, column_n); + column_0_width = ImClamp(column_0_width, min_width, max_width); if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width) return; From d497f112e7876fd21fbc164d67a7a49bd253d5c8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 22 Dec 2020 16:55:40 +0100 Subject: [PATCH 18/18] Tables: simplified and tidying up TableSetColumnWidth(), fixes resizing a fixed column surrounded by stretch column (manually or via auto-fit menu). TableHeader() showing highlighted when held. --- imgui.h | 2 +- imgui_tables.cpp | 75 +++++++++++++++++------------------------------- 2 files changed, 28 insertions(+), 49 deletions(-) diff --git a/imgui.h b/imgui.h index e01bdc5d..94422644 100644 --- a/imgui.h +++ b/imgui.h @@ -1050,7 +1050,7 @@ enum ImGuiTabItemFlags_ // - Using Stretch columns OFTEN DOES NOT MAKE SENSE if ScrollX is on, UNLESS you have specified a value for 'inner_width' in BeginTable(). // - Mixing up columns with different sizing policy is possible BUT can be tricky and has some side-effects and restrictions. // (their visible order and the scrolling state have subtle but necessary effects on how they can be manually resized). -// The typical use of mixing sizing policies is to have ScrollX disabled, one or two Stretch Column and many Fixed Columns. +// The typical use of mixing sizing policies is to have ScrollX disabled, first Fixed columns and then one or two TRAILING Stretch columns. enum ImGuiTableFlags_ { // Features diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 0c20f052..731063ba 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1876,6 +1876,7 @@ void ImGui::TableSetColumnWidth(int column_n, float width) if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width) return; + //IMGUI_DEBUG_LOG("TableSetColumnWidth(%d, %.1f->%.1f)\n", column_0_idx, column_0->WidthGiven, column_0_width); ImGuiTableColumn* column_1 = (column_0->NextEnabledColumn != -1) ? &table->Columns[column_0->NextEnabledColumn] : NULL; // In this surprisingly not simple because of how we support mixing Fixed and multiple Stretch columns. @@ -1897,66 +1898,44 @@ void ImGui::TableSetColumnWidth(int column_n, float width) // - W1 F2 F3 resize from F3| --> ok: no-op (disabled by Resize Rule 1) // - W1 F2 resize from F2| --> ok: no-op (disabled by Resize Rule 1) // - W1 W2 F3 resize from W1| or W2| --> ok - // - W1 F2 W3 resize from W1| or F2| --> FIXME + // - W1 F2 W3 resize from W1| or F2| --> ok // - F1 W2 F3 resize from W2| --> ok // - F1 W3 F2 resize from W3| --> ok - // - W1 F2 F3 resize from W1| --> ok: equivalent to resizing |F2. F3 will not move. (forwarded by Resize Rule 2) + // - W1 F2 F3 resize from W1| --> ok: equivalent to resizing |F2. F3 will not move. // - W1 F2 F3 resize from F2| --> ok // All resizes from a Wx columns are locking other columns. // Possible improvements: // - W1 W2 W3 resize W1| --> to not be stuck, both W2 and W3 would stretch down. Seems possible to fix. Would be most beneficial to simplify resize of all-weighted columns. - // - W1 F2 W3 resize W1| or F2| --> symmetrical resize is weird and glitchy. Seems possible to fix. // - W3 F1 F2 resize W3| --> to not be stuck past F1|, both F1 and F2 would need to stretch down, which would be lossy or ambiguous. Seems hard to fix. - // Rules: - // - [Resize Rule 1] Can't resize from right of right-most visible column if there is any Stretch column. Implemented in TableUpdateLayout(). - // - [Resize Rule 2] Resizing from right-side of a Stretch column before a fixed column forward sizing to left-side of fixed column. - // - [Resize Rule 3] If we are are followed by a fixed column and we have a Stretch column before, we need to ensure that our left border won't move. - table->IsSettingsDirty = true; + // [Resize Rule 1] Can't resize from right of right-most visible column if there is any Stretch column. Implemented in TableUpdateLayout(). + + // If we have all Fixed columns OR resizing a Fixed column that doesn't come after a Stretch one, we can do an offsetting resize. + // This is the preferred resize path if (column_0->Flags & ImGuiTableColumnFlags_WidthFixed) - { - // [Resize Rule 3] If we are are followed by a fixed column and we have a Stretch column before, we need to ensure - // that our left border won't move, which we can do by making sure column_a/column_b resizes cancels each others. - if (column_1 && (column_1->Flags & ImGuiTableColumnFlags_WidthFixed)) - if (table->LeftMostStretchedColumn != -1 && table->Columns[table->LeftMostStretchedColumn].DisplayOrder < column_0->DisplayOrder) - { - // (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b) - float column_1_width = ImMax(column_1->WidthRequest - (column_0_width - column_0->WidthRequest), min_width); - column_0_width = column_0->WidthRequest + column_1->WidthRequest - column_1_width; - column_1->WidthRequest = column_1_width; - } - - // Apply - //IMGUI_DEBUG_LOG("TableSetColumnWidth(%d, %.1f->%.1f)\n", column_0_idx, column_0->WidthRequested, column_0_width); - column_0->WidthRequest = column_0_width; - } - else if (column_0->Flags & ImGuiTableColumnFlags_WidthStretch) - { - // We can also use previous column if there's no next one (this is used when doing an auto-fit on the right-most stretch column) - if (column_1 == NULL) - column_1 = (column_0->PrevEnabledColumn != -1) ? &table->Columns[column_0->PrevEnabledColumn] : NULL; - if (column_1 == NULL) - return; - - if (column_1->Flags & ImGuiTableColumnFlags_WidthFixed) + if (!column_1 || table->LeftMostStretchedColumn == -1 || table->Columns[table->LeftMostStretchedColumn].DisplayOrder >= column_0->DisplayOrder) { - // [Resize Rule 2] - float off = (column_0->WidthGiven - column_0_width); - float column_1_width = column_1->WidthGiven + off; - column_1->WidthRequest = ImMax(min_width, column_1_width); - } - else - { - // At this point column_1 is the next OR previous column and we know it is a stretch column. - // (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b) - float column_1_width = ImMax(column_1->WidthRequest - (column_0_width - column_0->WidthRequest), min_width); - column_0_width = column_0->WidthRequest + column_1->WidthRequest - column_1_width; - column_1->WidthRequest = column_1_width; column_0->WidthRequest = column_0_width; - TableUpdateColumnsWeightFromWidth(table); + table->IsSettingsDirty = true; + return; } - } + + // We can also use previous column if there's no next one (this is used when doing an auto-fit on the right-most stretch column) + if (column_1 == NULL) + column_1 = (column_0->PrevEnabledColumn != -1) ? &table->Columns[column_0->PrevEnabledColumn] : NULL; + if (column_1 == NULL) + return; + + // Resizing from right-side of a Stretch column before a Fixed column forward sizing to left-side of fixed column. + // (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b) + float column_1_width = ImMax(column_1->WidthRequest - (column_0_width - column_0->WidthRequest), min_width); + column_0_width = column_0->WidthRequest + column_1->WidthRequest - column_1_width; + column_0->WidthRequest = column_0_width; + column_1->WidthRequest = column_1_width; + if ((column_0->Flags | column_1->Flags) & ImGuiTableColumnFlags_WidthStretch) + TableUpdateColumnsWeightFromWidth(table); + table->IsSettingsDirty = true; } // Disable clipping then auto-fit, will take 2 frames @@ -2715,7 +2694,7 @@ void ImGui::TableHeader(const char* label) bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowItemOverlap); if (g.ActiveId != id) SetItemAllowOverlap(); - if (hovered || selected) + if (held || hovered || selected) { const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); //RenderFrame(bb.Min, bb.Max, col, false, 0.0f);