diff --git a/imgui.h b/imgui.h index 7f93bd5f..5eee1e54 100644 --- a/imgui.h +++ b/imgui.h @@ -1092,7 +1092,6 @@ enum ImGuiTableFlags_ }; // Flags for ImGui::TableSetupColumn() -// FIXME-TABLE: Rename to ImGuiColumns_*, stick old columns api flags in there under an obsolete api block enum ImGuiTableColumnFlags_ { ImGuiTableColumnFlags_None = 0, diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 2298eb65..6f269945 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4021,47 +4021,87 @@ static void ShowDemoWindowTables() ImVec2 outer_size(0, TEXT_BASE_HEIGHT * 7); if (ImGui::BeginTable("##nways", column_count, flags, outer_size)) { - for (int row = 0; row < 10; row++) + for (int cell = 0; cell < 10 * column_count; cell++) { - ImGui::TableNextRow(); - for (int column = 0; column < column_count; column++) + ImGui::TableNextColumn(); + int column = ImGui::TableGetColumnIndex(); + int row = ImGui::TableGetRowIndex(); + + ImGui::PushID(cell); + char label[32]; + static char text_buf[32] = ""; + sprintf(label, "Hello %d,%d", column, row); + switch (contents_type) { - ImGui::TableSetColumnIndex(column); - char label[32]; - static char text_buf[32] = ""; - sprintf(label, "Hello %d,%d", column, row); - switch (contents_type) - { - case CT_ShortText: ImGui::TextUnformatted(label); break; - case CT_LongText: ImGui::Text("Some longer text %d,%d\nOver two lines..", column, row); break; - case CT_Button: ImGui::Button(label); break; - case CT_FillButton: ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); break; - case CT_InputText: ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText("##", text_buf, IM_ARRAYSIZE(text_buf)); break; - } + case CT_ShortText: ImGui::TextUnformatted(label); break; + case CT_LongText: ImGui::Text("Some longer text %d,%d\nOver two lines..", column, row); break; + case CT_Button: ImGui::Button(label); break; + case CT_FillButton: ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); break; + case CT_InputText: ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText("##", text_buf, IM_ARRAYSIZE(text_buf)); break; } + ImGui::PopID(); } ImGui::EndTable(); } + ImGui::TextUnformatted("Item Widths"); + ImGui::SameLine(); + HelpMarker("Showcase using PushItemWidth() and how it is preserved on a per-column basis"); + if (ImGui::BeginTable("##table2", 3, ImGuiTableFlags_Borders)) + { + ImGui::TableSetupColumn("small"); + ImGui::TableSetupColumn("half"); + ImGui::TableSetupColumn("right-align"); + ImGui::TableHeadersRow(); + + for (int row = 0; row < 3; row++) + { + ImGui::TableNextRow(); + if (row == 0) + { + // Setup ItemWidth once (instead of setting up every time, which is also possible but less efficient) + ImGui::TableSetColumnIndex(0); + ImGui::PushItemWidth(TEXT_BASE_WIDTH * 3.0f); // Small + ImGui::TableSetColumnIndex(1); + ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f); + ImGui::TableSetColumnIndex(2); + ImGui::PushItemWidth(-FLT_MIN); // Right-aligned + } + + // Draw our contents + static float dummy_f = 0.0f; + ImGui::PushID(row); + ImGui::TableSetColumnIndex(0); + ImGui::SliderFloat("float0", &dummy_f, 0.0f, 1.0f); + ImGui::TableSetColumnIndex(1); + ImGui::SliderFloat("float1", &dummy_f, 0.0f, 1.0f); + ImGui::TableSetColumnIndex(2); + ImGui::SliderFloat("float2", &dummy_f, 0.0f, 1.0f); + ImGui::PopID(); + } + ImGui::EndTable(); + } + + ImGui::TextUnformatted("Stretch + ScrollX"); + ImGui::SameLine(); HelpMarker( "Showcase using Stretch columns + ScrollX together: " "this is rather unusual and only makes sense when specifying an 'inner_width' for the table!\n" "Without an explicit value, inner_width is == outer_size.x and therefore using Stretch columns + ScrollX together doesn't make sense."); - static ImGuiTableFlags flags2 = ImGuiTableFlags_ColumnsWidthStretch | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg; + static ImGuiTableFlags flags3 = ImGuiTableFlags_ColumnsWidthStretch | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg; static float inner_width = 1000.0f; PushStyleCompact(); - ImGui::PushID("flags2"); - ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags2, ImGuiTableFlags_ScrollX); - if (ImGui::CheckboxFlags("ImGuiTableFlags_ColumnsWidthStretch", &flags2, ImGuiTableFlags_ColumnsWidthStretch)) - flags2 &= ~ImGuiTableFlags_ColumnsWidthFixed; // Can't specify both sizing polices so we clear the other - if (ImGui::CheckboxFlags("ImGuiTableFlags_ColumnsWidthFixed", &flags2, ImGuiTableFlags_ColumnsWidthFixed)) - flags2 &= ~ImGuiTableFlags_ColumnsWidthStretch; // Can't specify both sizing polices so we clear the other + ImGui::PushID("flags3"); + ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags3, ImGuiTableFlags_ScrollX); + if (ImGui::CheckboxFlags("ImGuiTableFlags_ColumnsWidthStretch", &flags3, ImGuiTableFlags_ColumnsWidthStretch)) + flags3 &= ~ImGuiTableFlags_ColumnsWidthFixed; // Can't specify both sizing polices so we clear the other + if (ImGui::CheckboxFlags("ImGuiTableFlags_ColumnsWidthFixed", &flags3, ImGuiTableFlags_ColumnsWidthFixed)) + flags3 &= ~ImGuiTableFlags_ColumnsWidthStretch; // Can't specify both sizing polices so we clear the other ImGui::SetNextItemWidth(TEXT_BASE_WIDTH * 10.0f); ImGui::DragFloat("inner_width", &inner_width, 1.0f, 0.0f, FLT_MAX, "%.1f"); ImGui::PopID(); PopStyleCompact(); - - if (ImGui::BeginTable("##table2", 7, flags2 | ImGuiTableFlags_ColumnsWidthStretch | ImGuiTableFlags_ContextMenuInBody, outer_size, inner_width)) + if (ImGui::BeginTable("##table3", 7, flags3 | ImGuiTableFlags_ColumnsWidthStretch | ImGuiTableFlags_ContextMenuInBody, outer_size, inner_width)) { for (int cell = 0; cell < 20 * 7; cell++) { diff --git a/imgui_internal.h b/imgui_internal.h index 3167728f..3f0a063d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1893,7 +1893,7 @@ struct ImGuiTabBar #define IMGUI_TABLE_MAX_COLUMNS 64 // sizeof(ImU64) * 8. This is solely because we frequently encode columns set in a ImU64. #define IMGUI_TABLE_MAX_DRAW_CHANNELS (4 + 64 * 2) // See TableUpdateDrawChannels() -// [Internal] sizeof() ~ 100 +// [Internal] sizeof() ~ 104 // We use the terminology "Visible" to refer to a column that is not Hidden by user or settings. However it may still be out of view and clipped (see IsClipped). struct ImGuiTableColumn { @@ -1910,6 +1910,7 @@ struct ImGuiTableColumn float WidthGiven; // Final/actual width visible == (MaxX - MinX), locked in TableUpdateLayout(). May be > WidthRequest to honor minimum width, may be < WidthRequest to honor shrinking columns down in tight space. float WorkMinX; // Start position for the frame, currently ~(MinX + CellPaddingX) float WorkMaxX; + float ItemWidth; float ContentMaxXFrozen; // Contents maximum position for frozen rows (apart from headers), from which we can infer content width. float ContentMaxXUnfrozen; float ContentMaxXHeadersUsed; // Contents maximum position for headers rows (regardless of freezing). TableHeader() automatically softclip itself + report ideal desired size, to avoid creating extraneous draw calls @@ -2009,7 +2010,9 @@ struct ImGuiTable ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable() ImRect HostBackupClipRect; // Backup of InnerWindow->ClipRect during PushTableBackground()/PopTableBackground() ImVec2 HostBackupCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable() - ImVec1 HostBackupColumnsOffset; // Backup of OuterWindow->ColumnsOffset at the end of BeginTable() + ImVec1 HostBackupColumnsOffset; // Backup of OuterWindow->DC.ColumnsOffset at the end of BeginTable() + float HostBackupItemWidth; // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable() + int HostBackupItemWidthStackSize;// Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable() ImGuiWindow* OuterWindow; // Parent window for the table ImGuiWindow* InnerWindow; // Window holding the table data (== OuterWindow or a child window) ImGuiTextBuffer ColumnsNames; // Contiguous buffer holding columns names diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 0dc9f4d6..5fd78de2 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -82,8 +82,8 @@ // | TableEndRow() - finish existing row // | TableBeginRow() - add a new row // - TableSetColumnIndex() / TableNextColumn() user begin into a cell -// | TableEndCell() - close existing cell -// | TableBeginCell() - enter into current cell +// | TableEndCell() - close existing column/cell +// | TableBeginCell() - enter into current column/cell // - [...] user emit contents //----------------------------------------------------------------------------- // - EndTable() user ends the table @@ -320,6 +320,8 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->HostBackupParentWorkRect = inner_window->ParentWorkRect; table->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset; table->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos; + table->HostBackupItemWidth = outer_window->DC.ItemWidth; + table->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; inner_window->ParentWorkRect = table->WorkRect; // Padding and Spacing @@ -826,6 +828,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) column->ClipRect.Max.y = FLT_MAX; column->ClipRect.ClipWithFull(host_clip_rect); column->IsClipped = column->IsSkipItems = true; + column->ItemWidth = 1.0f; continue; } @@ -866,6 +869,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) 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); column->ClipRect.Min.x = column->MinX; column->ClipRect.Min.y = work_rect.Min.y; column->ClipRect.Max.x = column->MaxX; // column->WorkMaxX; @@ -1128,11 +1132,14 @@ void ImGui::EndTable() // Layout in outer window IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table->ID + table->InstanceCurrent, "Mismatching PushID/PopID!"); + IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= table->HostBackupItemWidthStackSize, "Too many PopItemWidth!"); PopID(); inner_window->WorkRect = table->HostBackupWorkRect; inner_window->ParentWorkRect = table->HostBackupParentWorkRect; inner_window->SkipItems = table->HostSkipItems; outer_window->DC.CursorPos = table->OuterRect.Min; + outer_window->DC.ItemWidth = table->HostBackupItemWidth; + outer_window->DC.ItemWidthStack.Size = table->HostBackupItemWidthStackSize; outer_window->DC.ColumnsOffset = table->HostBackupColumnsOffset; if (inner_window != outer_window) { @@ -1927,6 +1934,7 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n) window->WorkRect.Min.y = window->DC.CursorPos.y; window->WorkRect.Min.x = column->WorkMinX; window->WorkRect.Max.x = column->WorkMaxX; + window->DC.ItemWidth = column->ItemWidth; // To allow ImGuiListClipper to function we propagate our row height if (!column->IsVisible) @@ -1961,6 +1969,7 @@ void ImGui::TableEndCell(ImGuiTable* table) p_max_pos_x = table->IsUnfrozen ? &column->ContentMaxXUnfrozen : &column->ContentMaxXFrozen; *p_max_pos_x = ImMax(*p_max_pos_x, window->DC.CursorMaxPos.x); table->RowPosY2 = ImMax(table->RowPosY2, window->DC.CursorMaxPos.y + table->CellPaddingY); + column->ItemWidth = window->DC.ItemWidth; // Propagate text baseline for the entire row // FIXME-TABLE: Here we propagate text baseline from the last line of the cell.. instead of the first one.