Open merravid opened 4 days ago
There is a way but honestly at this point I strongly suggest to avoid it. It will make everything more complex and error prone.
A future update of the layout system will aim to allow it with more ease.
Hi @merravid.
Here's the code that I use to centre the text vertically in a row.
if (auto* table = ImGui::GetCurrentTable()) {
if (table->CurrentColumn >= 0) {
auto& style = ImGui::GetStyle();
auto colRect = ImGui::TableGetCellBgRect(table, table->CurrentColumn);
ImGui::SetCursorPosY(ImGui::GetCursorPos().y + (colRect.GetHeight() / 2.f) - style.FramePadding.y - style.CellPadding.y );
}
}
...
...
ImGui::Text (drawText.c_str());
Note: I was first testing this in a debug project, where I also had to take into account the text height, when I applied this solution to my main project I had to remove the text height to make it centred, I don't know why that was and since my project was too complex I didn't bother figuring out the reason, it depends on the style state I suppose. With that said, if the code above doesn't do it for you, replace the SetCursorPosY call with this
ImGui::SetCursorPosY(ImGui::GetCursorPos().y + (colRect.GetHeight() / 2.f) - style.FramePadding.y - style.CellPadding.y - ImGui::CalcTextSize(drawText.c_str()).y * 0.5f);
I hope it helps
The lesser evil and most consistent thing I found was to use a helper and call it before every item in the line:
// Remember that "current line height" is not the same concept as "table row height".
// Because a table cell can contains multiple lines.
// In practice, when vertical alignment is desired in a table row it is generally when there is a single line of items in it.
static void AlignItemYInCurrentLine(float item_height, float item_alignment = 0.5f)
{
// Use line extents
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
float line_y1 = (window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y);
float line_y2 = line_y1 + (window->DC.IsSameLine ? window->DC.PrevLineSize.y : window->DC.CurrLineSize.y);
float line_height = line_y2 - line_y1;
window->DC.CursorPos.y = line_y1 + ImCeil((line_height - item_height) * item_alignment);
window->DC.CurrLineTextBaseOffset = 0.0f; // FIXME: Disable text baseline offset
}
Again I don't recommend it until it is supported by the layout system. But this is more correct.
static void TestTableVerticalAlignment()
{
ImGuiIO& io = ImGui::GetIO();
static float item_alignment = 0.5f;
ImGui::SliderFloat("item_alignment", &item_alignment, 0.0f, 1.0f, "%.2f");
if (ImGui::BeginTable("##table", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg))
{
for (int row = 0; row < 5; row++)
{
// (1) New row
const float row_height = 50;
ImGui::TableNextRow(ImGuiTableRowFlags_None);
// Submitting row height explicitly can be done this way:
// - HOWEVER it's not useful as we submit an full height item below + our centering function behave on Line Height rather than Table Row Height.
// - In order to keep the centering function more generic (e.g. can work outside tables) they use Line Height rather than Table Row Height
// - TableNextRow() height currently specified with padding included, so we compute it. Which IHMO is not great API design might want to change but that's another topic.
//const float row_height_with_cell_padding = row_height + ImGui::GetStyle().CellPadding.y * 2.0f;
//ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height_with_cell_padding);
// (2) Submit a Selectable or full-height item as the first thing
// - this locks the Line Y1 value to our item/selectable Y1 value.
// - so we don't need to bother passing any value to TableNextRow() anyway
ImGui::TableSetColumnIndex(0);
#if 1
// (2-A) Submit a spanning selectable (will also start Line Y1 position and Line Height)
ImGui::Selectable("##selectable", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap, ImVec2(0.0f, row_height));
#else
// (2-B) Alternative if you don't need a Selectable(): Submit a dummy item to Lock Line Y1 and Line Height..
// If we don't do that and our first item submitted after is vertically centered, next item will set Line Y1 at wrong position.
ImGui::Dummy(ImVec2(0.0f, row_height));
#endif
ImGui::SameLine(0.0f, 0.0f);
if (io.KeyShift)
ImGui::DebugDrawLineExtents();
// Column 0
ImGui::TableSetColumnIndex(0);
AlignItemYInCurrentLine(ImGui::GetTextLineHeight(), item_alignment);
ImGui::Text("ICON1");
ImGui::SameLine();
AlignItemYInCurrentLine(ImGui::GetTextLineHeight(), item_alignment);
ImGui::Text("ICON2");
// Column 1
ImGui::TableSetColumnIndex(1);
// Call SameLine(0,0) when entering the column
// This is important as Line Height is not shared by default between columns (because they could contains different things)
// This is now demonstrated in 'Demo->Tables->Row Height'
// FIXME: As this is a relatively current/desirable pattern, may make it opt-in a table flags?
ImGui::SameLine(0.0f, 0.0f);
if (io.KeyShift)
ImGui::DebugDrawLineExtents();
// Submit item
if ((row & 1) == 0)
{
AlignItemYInCurrentLine(ImGui::GetTextLineHeight(), item_alignment);
ImGui::Text("ICON3");
}
else
{
AlignItemYInCurrentLine(32.0f, item_alignment);
ImGui::ColorButton("##button", ImVec4(0.0f, 1.0f, 0.0f, 1.0f), 0, ImVec2(32.0f, 32.0f));
}
ImGui::SameLine();
if (io.KeyShift)
ImGui::DebugDrawLineExtents();
AlignItemYInCurrentLine(ImGui::GetTextLineHeight(), item_alignment);
ImGui::Text("Text");
ImGui::SameLine();
AlignItemYInCurrentLine(ImGui::GetTextLineHeight(), item_alignment);
ImGui::Text("MoreText");
}
ImGui::EndTable();
}
}
Version/Branch of Dear ImGui:
Version 1.90.9, Branch: Docking
Back-ends:
imgui_impl_win32.cpp + imgui_impl_dx11.cpp
Compiler, OS:
Windows 11 + MSVC 2022
Full config/build information:
No response
Details:
When you add custom drawn items to one or more table cells, the row height increases to accommodate those items.
Any text in other cells in that row is aligned to the top of the cell instead of vertically centered.
What is the best way to achieve vertical centering of text in this case?
Screenshots/Video:
No response
Minimal, Complete and Verifiable Example code:
No response