Skip to content

Improving layouting #4378

Open
Open
@emilk

Description

Understanding the problem

One of the main drawback of single-pass immediate mode GUIs is layouting. There is a cyclical dependency between position, size, and layout. This can be illustrated explained by an example:

Say you want to show a small dialog window in the center of the screen. To position the window we must first know the size of it. To know the size of it we must first layout the contents of the window. In immediate mode, the layout code also checks for interaction ("was the OK button clicked?"), thus we need to know where to place the OK button before doing the layout. So we need to know where to put the window before we can layout the contents of the window, but only after laying out the contents of the window can we know the size of the window, and thus where to place it so that it is centered on the screen.

The crux of the issue is really knowing the size of a thing before you can lay it out properly. So, how can we know the size of a thing before calling the layout code for it?

There are simple atomic widgets like egui::Button that calculates its size based on the text it contains and the border. This is where egui "bottoms out". These atomic widgets can be properly centered, because they only result in a single call to ui.allocate*.

Whenever you have a group of widgets though, you have a problem.

Figuring out sizes before layout

So how can we know the size of a thing before doing the layout?

There are a few different ways:

A) Using fixed size elements (e.g. decide that the dialog window is always exactly 300x200 no matter what it contains)
B) Remember sizes of elements from previous frames (first frame calculate sizes, all other frames: position things based on those sizes). egui uses this strategy for some stuff (like Window, Grid, Table, …)
C) An API for calculating the size of an element before adding it (#606 - has potential for exponential slowdowns)
D) Multi-pass immediate mode (rejected)

I think B) is the most interesting one to dig deeper into.

Whenever the B) strategy is used, one should be careful not to show the widget during the "layout" frame. For instance, a centered egui::Window is invisible for the first frame.

The B) strategy also fails if the thing changes size over time. Though it is self correcting, it has a frame-delay. So in the original example: if the dialog windows grows it will shift to the right for one frame, then re-center then next frame. This will look ugly.

Improving layout given sizes

Once you have the size you should be able to apply any advanced layouting technique. For instance using:

Here we have a lot of opportunity for improvement in egui. As a start, we should at least write good examples for all of these.

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions