imgui icon indicating copy to clipboard operation
imgui copied to clipboard

How to correctly mix ImGuiTableColumnFlags_WidthFixed and ImGuiTableColumnFlags_WidthStretch

Open aardappel opened this issue 1 year ago • 4 comments

Version/Branch of Dear ImGui:

Version: 1.88

Back-end/Renderer/Compiler/OS

Back-ends: SDL + GL3 Compiler: VS2022 Operating System: Windows

My Issue/Question:

No matter what I do, I can't seem to create a table where the first column is fixed (max of contents) and the second is stretch (rest of window or max of content). In all cases the stretch column uses a width that is smaller than its contents.

Yes, I've read the docs on COLUMNS SIZING POLICIES and the bit about About mixing Fixed/Auto and Stretch columns together but no combination of flags seems to work.

My workaround is to set it all to stretch, but that ends up giving too much space too either first or second column, depending on content.

Note I use SetNextItemWidth(-1) on all widgets in the second column, since otherwise it reserves space for the label, even if all labels are "". But that doesn't seem to cause the column sizing problem since commenting it out still gives the squashed column.

Screenshots/Video

image

    if (ImGui::BeginTable("", 2, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_BordersOuter)) {
        ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthFixed);
        ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthStretch);

Tried every possible value instead of ImGuiTableFlags_SizingFixedFit, no effect.

The problem seems to be related to this table being pretty much the only thing in the window to determine window with (sizing of window set to auto), if there's other elements forcing a wider width then the above code works as expected.

If this is not possible with tables, some other way to have a consistent label on the left of a widget would be nice. The whole reason I am using tables is because having UIs where you alternate between (closed) TreeNode (which have their label on the left) and other values (which have their label on the right, with no way to change this??) makes for very confusing UIs.

aardappel avatar Jul 18 '22 18:07 aardappel

Not sure if i understood your issue correctly. You should really provide a minimal reproducible sample that could be tested.

Anyhow, i found no issues creating a table that you described. See if this code snippet does what you want:

static ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize;
ImGui::Begin("Window", NULL, window_flags);
ImGui::CheckboxFlags("ImGuiWindowFlags_AlwaysAutoResize", &window_flags, ImGuiWindowFlags_AlwaysAutoResize);
static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders;
if (ImGui::TreeNode("Node"))
{
    if (ImGui::BeginTable("table1", 2, flags))
    {
        ImGui::TableSetupColumn("AAA", ImGuiTableColumnFlags_WidthFixed);
        ImGui::TableSetupColumn("BBB", ImGuiTableColumnFlags_WidthStretch);
        ImGui::TableHeadersRow();
        for (int row = 0; row < 5; row++)
        {
            ImGui::TableNextRow();
            for (int column = 0; column < 2; column++)
            {
                ImGui::TableSetColumnIndex(column);
                ImGui::Text("%s %d,%d", (column == 2) ? "Stretch" : "Fixed", column, row);
            }
        }
        ImGui::EndTable();
    }
    ImGui::TreePop();
}
ImGui::End();

I pretty much copied it from demo by the way.

rokups avatar Jul 20 '22 09:07 rokups

@rokups Your example is omitting the right-alignment. Here's the same example with it:

static ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize;
ImGui::Begin("Window", NULL, window_flags);
ImGui::CheckboxFlags("ImGuiWindowFlags_AlwaysAutoResize", &window_flags, ImGuiWindowFlags_AlwaysAutoResize);
if (ImGui::BeginTable("table1", 2, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter))
{
    ImGui::TableSetupColumn("AAA", ImGuiTableColumnFlags_WidthFixed);
    ImGui::TableSetupColumn("BBB", ImGuiTableColumnFlags_WidthStretch);
    ImGui::TableHeadersRow();
    for (int row = 0; row < 5; row++)
    {
        ImGui::TableNextRow();
        for (int column = 0; column < 2; column++)
        {
            ImGui::TableSetColumnIndex(column);
            if (column == 0)
            {
                ImGui::AlignTextToFramePadding();
                ImGui::Text("%*sSomePaddedText", row * 4, "");
            }
            else
            {
                ImGui::SetNextItemWidth(-1);
                static float f = 0.0f;
                ImGui::SliderFloat("blah", &f, 0.0f, 1.0f);
            }
        }
    }
    ImGui::EndTable();
}
ImGui::End();

image

You can see the problem if you set the window to auto-resize.

@aardappel

Note I use SetNextItemWidth(-1) on all widgets in the second column, [...] The problem seems to be related to this table being pretty much the only thing in the window to determine window with (sizing of window set to auto), if there's other elements forcing a wider width then the above code works as expected.

Those are contradicting constraints.

  • You have a widget which is configured to take all available width in the column.
  • You have a column which is configured to take all remaining width in the table.
  • You have a table which is configured to take all width of the window.

And yet

  • You have a window which is configured to always auto-resize based on its content and has no other content than this table.

This can't be solved it doesn't make sense. Either the window is not auto-resizing, either SOME of its content needs to be NOT based on fitting available window width.

(edited)

ocornut avatar Jul 20 '22 12:07 ocornut

Thanks for the example!

However, @ocornut's version renders for me as (using almost the entire width of the host window, which is certainly too much):

image

If I comment out SetNextItemWidth(-1), I instead get:

image

Those are contradicting constraints.

I bet they are, I am no ImGui expert.. I am wondering what set of constraints does the trick, then.

What I want is more like the 2nd image, except:

  • I want there to be no label (blah).
  • If any item in column BBB is wider than the example SliderFloat, I want the SliderFloat to take up all of the new space.
  • If any item outside the table makes the whole window wider, I want all items in BBB to benefit from that.

Is that possible?

Actually, I already have this working, if manually force the window to a certain width. So the big question is if it can happen with automatic sizing.

aardappel avatar Jul 20 '22 18:07 aardappel

I also tried, in your example, setting "blah" to "" and was briefly excited that this seemed to actually work, but then I noticed it relied on the label "ImGuiWindowFlags_AlwaysAutoResize" being the longest item in the window. When I replaced that label by "short" (and in addition, "BBB" to "B" to make the sizing issue more obvious), I get:

image

So it seems column B is sized by its label now, with the SliderFloat having no minimum. And the above layout happens regardless of whether I use SetNextItemWidth(-1) or not, which is very surprising.

aardappel avatar Jul 20 '22 19:07 aardappel

Any idea for the best approach?

aardappel avatar Sep 09 '22 23:09 aardappel

My answer still stands you should carefully read it again until you understand why as specified it’s an impossible problem to solve. That’s unrelated to dear imgui per-se, with precisely those constraints there’s no answer afaik. Some have to be added or removed.

What I imagine would solve your issue is either:

  • You give a minimum width to slider (using SetNextItemWidth(ImMax(200, GetContentSizeAvail().x))
  • You give a minimum width to the host window.
  • You stop making the window auto-resize.

ocornut avatar Sep 10 '22 06:09 ocornut

ImGui::SetNextItemWidth(std::max(200.0f, ImGui::GetContentRegionAvail().x)); indeed works best so far, thanks!

Ideally I'd find the best value for 200 for each widget somehow, but I can live with a default for now.

Again, my understanding of the layout algorithm isn't deep enough (in fact it is rather shallow, given that I tried many things and mostly got surprising results) to know that something is not possible. What I am trying to do didn't seem like a crazy use case and is possible in many UI toolkits, that's why I was asking.

aardappel avatar Sep 13 '22 00:09 aardappel

I'm not sure it is a matter of layout algorithm here, the "Those are contradicting constraints." paragraph (in https://github.com/ocornut/imgui/issues/5478#issuecomment-1190239626) isn't really dear imgui specific. You are just using dear imgui here with conflicting constraints. Perhaps other toolkits are better designed in order to not allow you to get toward a corner like that. There's most often a resizable element in the chain (e.g. top level window) to alleviate that sort of issues.

ocornut avatar Sep 13 '22 09:09 ocornut