imgui icon indicating copy to clipboard operation
imgui copied to clipboard

Feature request: Horizontal layout

Open emilk opened this issue 10 years ago • 21 comments

The only way I've found to do horizontal layout in ImGui is to call SameLine() in between widgets that should go on the same line. This is problematic when the widgets are painted by some other code. In particular, I want to lay out an unknown number of Text:s horizontally on the same line.

May I suggest adding something like:

ImGUI::PushHorizontalLayout(int spacing_w = -1);  // All subsequent widgets will be positioned to the right of the previous one
ImGUI::PopHorizontalLayout();                     // Ends the line

emilk avatar Dec 12 '14 12:12 emilk

Yes!

The reason I haven't added the simple version yet (like your suggested API) is that I wanted to spend more time considering the layout problem holistically.

We'd probably want

  • Horizontal layout, contiguous (optional spacing override)
  • Horizontal layout, evenly spaced (given width or count)
  • Grid layout
  • Vertical layout for consistency could have the same options as horizontal

Notes

  • For the sake of keeping the API terse/optimal it would probably be exposed as different entry points.
  • Can't decide if the functions could be called Push/Pop or Begin/End. Push/Pop is more in line with the current uses. We can have a generic ImGui::PopLayout() function as well.
  • ImGui::PushLayoutHorizontal() makes more sense than ImGui::PushHorizontalLayout() ? in term of categorizing by family, getting completion on IDE, etc.
  • I don't really fancy such a long function name for such as a common feature but cannot find a better one yet.
  • Could have simple "setter" for cases where the code is trivial, to shorten use, so something like ImGui::LayoutHorizontal() since each window will have its stack.

Thanks for the suggestion!

ocornut avatar Dec 12 '14 13:12 ocornut

But you are right, a simple helper would be welcome asap.

ocornut avatar Dec 12 '14 13:12 ocornut

Could also skip on the Layout word. ?

Horizontal() / BeginHorizontal() / EndHorizontal() Vertical() / BeginVertical() / EndVertical()

ocornut avatar Dec 12 '14 13:12 ocornut

And add ImGui::NextLine() as well.

ocornut avatar Dec 12 '14 14:12 ocornut

Yeah, for now I've created my own NextLine based on Separator - that kind of works =)

I feel push/pop is more descriptive iff nesting is supported. I'm all for skipping the "Layout" part, i.e. PushHorizontal/PopHorizontal.

PushHorizontal(); Text("a"); Text("b"); PushVertical(); Text("c"); Text("d"); PopVertical(); PopHorizontal();

->

a b c
    d

Something like that?

emilk avatar Dec 12 '14 23:12 emilk

But:

Text("a"); SameLine(); Text("b"); Text("c"); Text("d");

Currently becomes:

  a b
  c
  d

So starting a vertical layout should "lock" the X current coordinate? Which is what happens with your example. In which case how to do achieve the layout above with the Push/Pop system?

PushHorizontal(); Text("a"); Text("b"); PopHorizontal(); 
NextLine(); // Could be implicit ?
Text("c");
Text("d");

If locking is done on "pushing" the vertical layout and not "popping" then we can't have Horizontal() / Vertical().

Horizontal(); Text("a"); Text("b"); Vertical(); Text("c"); Text("d"); 

Does that become the layout in your example or in mine? Gets a bit confusing.

So my proposal is that all those gives the same output:

PushHorizontal(); Text("a"); Text("b"); PushVertical(); Text("c"); Text("d"); PopVertical(); PopHorizontal();
PushHorizontal(); Text("a"); Text("b"); PopHorizontal(); Text("c"); Text("d"); 
Horizontal(); Text("a"); Text("b"); Vertical(); Text("c"); Text("d");

That is:

  a b
  c
  d

So there's less stored state.

Now if we want to implement your layout example (which is more of a rare case) we can have a way to set the value internally called "ColumnsStartX" (those variables can be cleaned up/renamed before being publicly exposed).

ImGui::SetColumnStartX(float x)
// this
ImGui::PushVertical(bool use_pos_x_as_column_start = false);
ImGui::Vertical(bool use_pos_x_as_column_start = false);
// or that
ImGui::PushVertical(float column_x = 0.0f);  // default left side. passing -1 uses current cursor x
ImGui::Vertical(float column_x = 0.0f);  // default left side. passing -1 uses current cursor x

Passing a float is more flexible but the difference between 0.0f (left side) and -1.0f (current cursor x) may be a bit arbitrary ? I guess you can always do ImGui::Vertical(ImGui::GetCursorPosX()); as well.

Does it makes sense?

ocornut avatar Dec 13 '14 11:12 ocornut

Also - the devil is in the details - we would probably want to add WindowPadding.x to the provided offset_x. Which becomes a problem because:

  ImGui::Vertical(0.0f);             // >> 0.0f + WIndowPadding.x
  ImGui::Vertical(GetCursorPosX()); // >> GetCursorPosX() + WindowPadding.x // Undesireable, cursor pos already include the padding.

We could treat 0.0f as a special case but that would be dodgy.

So a more consistent solution would be:

 column_x < 0.0f    (default value to all calls) gets turned into WindowPadding.x
 column_x > 0.0f    untouched

But then to implement your example you would need to call GetCursorPosX() and we lose the "lock current x position" shortcut.

ocornut avatar Dec 13 '14 11:12 ocornut

I think Horizontal()/Vertical() plus PushCurrentLayout() can simplify things: wanna change layout? -- change, wanna save? -- push/pop.

dkrikun avatar Feb 06 '15 20:02 dkrikun

Awesome library but I really hope this gets worked on soon as the current solution to use ImGui::CalcTextSize is non-ideal for performance since this gets then get called twice (since it is also used the control like the text box itself). This is my code just to display a centered text on the screen which is twice as big as the default size. Btw any way to SetNextWindowFontScale since we are supposed to use SetNextWindowSize and the like which doesn't work properly if you change the font scale?

const ImGuiIO& guiIO = ImGui::GetIO();

const static char* titleWindowTitle = "title";
ImGui::Begin(titleWindowTitle, nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove);

const static char* title = "Centered Title";
ImVec2 textSize = ImGui::CalcTextSize(title);
ImVec2 windowSize = ImVec2(textSize.x + Constants::UiPadding * 2.f, textSize.y + Constants::UiPadding * 2.f);

ImGui::SetWindowPos(ImVec2((guiIO.DisplaySize.x - windowSize.x) * 0.5f, (guiIO.DisplaySize.y - windowSize.y) * 0.5f));
ImGui::SetWindowSize(windowSize);

ImGui::SetWindowFontScale(2.f);

ImGui::TextUnformatted(title);
ImGui::End();

DomGries avatar Jun 30 '16 21:06 DomGries

There is something for me. Basic implementation of layouts in form of BeginHorizontal/EndHorizontal.

            ImGui::BeginHorizontal("example_h1", spanToWindow ? bounds : ImVec2(0, 0));
                ImGui::TextUnformatted("Left");
                ImGui::Spring(middleWeight);
                ImGui::TextUnformatted("Middle");
                ImGui::Spring(1.0f - middleWeight);
                ImGui::TextUnformatted("Right");
            ImGui::EndHorizontal();

It is rough around the edges so please give me a feedback how it can be improved.

stack_layout

thedmd avatar Jul 22 '16 04:07 thedmd

It's been 7 years already, any updates on builtin layouts? Or maybe any well-maintained addon?

caxapexac avatar Dec 20 '21 20:12 caxapexac

What's wrong with Stack Layout? #846?

thedmd avatar Dec 20 '21 21:12 thedmd

@thedmd the only thing wrong with StackLayout is that it is not merged :D

mnesarco avatar Dec 21 '21 00:12 mnesarco

In some time I think a change can be proposed to allow other layout engines too. Truth is Stack Layouts are one of the possible solutions. Grids, forms (labels on the left anyone?), overflow layouts are examples of alternatives better suited for their respective use cases.

As for today few small modifications in ImGui are necessary to make custom layout code possible. (https://github.com/ocornut/imgui/pull/846#issuecomment-986006070).

There is a branch where Stack Layout code is moved to eventually became a plugin/extension to ImGui feature/layout-external.

thedmd avatar Dec 21 '21 01:12 thedmd

What's wrong with Stack Layout? #846?

It's exactly what I want but It's still not merged( Any ETA?

caxapexac avatar Dec 21 '21 07:12 caxapexac

In some time I think a change can be proposed to allow other layout engines too.

Yes importing yoga/flexbox would be a great addition too

caxapexac avatar Dec 21 '21 07:12 caxapexac

Any updates on this?

joshcamas avatar Dec 05 '22 20:12 joshcamas

It's taking ages 😂

MohammadMDSA avatar Jan 07 '23 14:01 MohammadMDSA

One day...

Ou7law007 avatar Oct 01 '23 13:10 Ou7law007

This works for me

int maxColumns = 10;
ImGui::Columns(maxColumns, "MyLayout", false); 
ImGui::SetColumnWidth(0, 80);
for (int i = 1; i < maxColumns - 1; i++)
{
    ImGui::SetColumnWidth(i, 80);
    ImGui::Text("item");
    ImGui::NextColumn();
}
ImGui::EndColumns();

GabrielJadderson avatar Dec 09 '23 00:12 GabrielJadderson

I'm just in the process of studying, and I'm unlikely to be useful in terms of project development right now. But, if someone decides to improve it, then I suggest considering the implementation https://developer.android.com/reference/android/widget/LinearLayout

I don't mean the code itself, but the capabilities of this Layout, which has been built into the Android OS since the first version. What would be cool to see: weightSum, orientation and gravity setup

nikkorejz avatar Apr 10 '24 16:04 nikkorejz