Auto-scroll not working when a table has a column with buttons
Opened this issue · 4 comments
Version/Branch of Dear ImGui:
Version 1.94 WIP, Branch: master
Back-ends:
imgui_impl_glfw.cpp + imgui_impl_opengl3.cpp
Compiler, OS:
Window 11 + MSVC 19.43.34809, Windows 11 + MingW-Clang 21.1.0
Full config/build information:
No response
Details:
My Issue/Question:
I have a panel that contains some tab bars, inside each tab bar there is a table. The table is populated with information generated over the time. What is notice is that the auto-scroll is not working, ImGui::GetScrollY() has always a constant value (as long as scroll is not manually done), meanwhile ImGui::GetScrollMaxY() is increasing with each new entry on the table, so the condition is never met. I have tried to set the flag ImGuiTabBarFlags_FittingPolicyScroll but no luck. If I remove the button and replace it with text or SmallButton it works ok. If I let the table be populated, and later I open the tab, the table is scrolled to the bottom, but once is opened, even if I switch tab the auto scroll stops working.
Screenshots/Video:
Minimal, Complete and Verifiable Example code:
// Put this somewhere outside the main loop
std::mutex items_mutex;
int items_count = 0;
int items[100] = {};
auto fnc_puh_items = [&]()
{
while (items_count < 100)
{
{
std::lock_guard<std::mutex> lock(items_mutex);
items[items_count++] = items_count + 100;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
};
std::thread(fnc_puh_items).detach();
// Test code for the bug (inside main loop)
constexpr int TABLE_FLAGS = ImGuiTableFlags_Resizable
| ImGuiTableFlags_HighlightHoveredColumn
| ImGuiTableFlags_SizingFixedFit
| ImGuiTableFlags_Reorderable
| ImGuiTableFlags_Hideable
| ImGuiTableFlags_ScrollX
| ImGuiTableFlags_ScrollY
| ImGuiTableFlags_Borders
;
if (ImGui::Begin("Test panel"))
{
if (ImGui::BeginTabBar("Test Tab Bar", ImGuiTabBarFlags_Reorderable))
{
if (ImGui::BeginTabItem("Test Tab Item"))
{
if (ImGui::BeginTable("Test Table", 3, TABLE_FLAGS))
{
ImGui::TableSetupColumn("Action");
ImGui::TableSetupColumn("Count");
ImGui::TableSetupColumn("Event");
ImGui::TableSetupScrollFreeze(1, 1);
ImGui::TableHeadersRow();
std::lock_guard<std::mutex> lock(items_mutex);
for (size_t i = 0; i < items_count; ++i)
{
ImGui::TableNextRow();
{
ImGui::TableNextColumn();
ImGui::PushID(i);
if (ImGui::Button("View"))
{
} ImGui::PopID();
}
{
ImGui::TableNextColumn();
ImGui::Text("%lld", i + 1);
}
{
ImGui::TableNextColumn();
ImGui::Text("%d", items[i]);
}
}
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
{
ImGui::SetScrollHereY(1.0f);
}
ImGui::EndTable();
}
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
}
ImGui::End();
Hello,
Could you simplify your code so it is more standalone and may be pasted anywhere in a dear imgui enabled app?
It probably doesn't need 6 tabs and it is currently relying on type/data we don't have.
Update the main point with a small source code showing the problem. It seems to be related to the table. There are 2 comments, one to put outside the main loop (for initialization) and another one for the main loop.
Here's a simplified repro that exhibits the issue with less unrelated code (no need for tab-bar or threads):
void Issue8984()
{
// Test code for the bug (inside main loop)
constexpr int TABLE_FLAGS = ImGuiTableFlags_Resizable
| ImGuiTableFlags_SizingFixedFit
| ImGuiTableFlags_ScrollY
| ImGuiTableFlags_Borders
;
static int items_count = 0;
static int items[100] = {};
if (ImGui::IsKeyPressed(ImGuiKey_F1))
items[items_count++] = items_count + 100;
if (ImGui::Begin("Test panel"))
{
float scroll_y = -1.0f;
float scroll_max_y = -1.0f;
if (ImGui::BeginTable("Test Table", 3, TABLE_FLAGS, ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing())))
{
ImGui::TableSetupColumn("Action");
ImGui::TableSetupColumn("Count");
ImGui::TableSetupColumn("Event");
ImGui::TableSetupScrollFreeze(1, 1);
ImGui::TableHeadersRow();
for (size_t i = 0; i < items_count; ++i)
{
ImGui::TableNextRow();
ImGui::PushID((int)i);
ImGui::TableNextColumn();
ImGui::Button("View");
ImGui::TableNextColumn();
ImGui::Text("%lld", i + 1);
ImGui::TableNextColumn();
ImGui::Text("%d", items[i]);
ImGui::PopID();
}
scroll_y = ImGui::GetScrollY();
scroll_max_y = ImGui::GetScrollMaxY();
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
ImGui::SetScrollHereY(1.0f);
ImGui::EndTable();
}
ImGui::Text("Scroll %.3f/%.3f", scroll_y, scroll_max_y);
}
ImGui::End();
}The problem is a combination of two things:
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
ImGui::SetScrollHereY(1.0f);The bottom most of the item is not the bottom-most of the scroll region because there's an additional CellPadding.y below it.
As thus, the logic we use in imgui_demo.cpp indeed does not work and should be reworked.
The best workaround is simply to use:
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
ImGui::SetScrollY(999999.0f);Another workaround is to use:
if (ImGui::GetScrollY() + ImGui::GetStyle().CellPadding.y >= ImGui::GetScrollMaxY())
ImGui::SetScrollHereY();But I will want to investigate this further.
Very disturbingly,
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
ImGui::SetScrollHereY();Also works!
Whereas:
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
ImGui::SetScrollHereY(1.0f);Doesn't. The parameter is 0.0..1.0 factor between item top and item bottom so in theory 1.0f should be bottom edge of item.
(it might be related to #1114)
