"Flow layouts" inside a GUI -- how do I come up with a good algorithm?

I was trying to write some simple code for a "flow layout" manager and what I came up with initially was something like the following (semi-pseudocode):

int rowHeight = 0;
RECT rect = parent.getClientRect();
POINT pos = rect.position;  // Start at top-left corner, row by row

foreach (Window child in parent.children)
{
// POINT is a tuple of: (x, y)
// SIZE is a tuple of: (width, height)
// RECT is a tuple of: (left, top, right, bottom)
RECT proposed1 = RECT(rect.left + pos.x, rect.top + pos.y, rect.right, rect.bottom),
proposed2 = RECT(rect.left, rect.top + pos.y + rowHeight, rect.right, rect.bottom);
SIZE size1 = child.getPreferredSize(proposed1),
size2 = child.getPreferredSize(proposed2);
if (size1.width <= proposed1.width)
{
child.put(proposed1);  // same row
pos.x += size1.width;
rowHeight = max(rowHeight, size1.height);
}
else
{
child.put(proposed2);  // new row
pos.x = rect.left;
pos.y += rowHeight;
rowHeight = size2.height;
}
}


In other words, the algorithm is very simple:
The layout manager asks every component, "is the remaining portion of the row enough for you?" and, if the component says "no, my width is too long", it places the component on the next row instead.

There are two major problems with this approach:

• This algorithm results in very long, thin components, because it is essentially greedy with the width of each component -- if a component wants the whole row, it will use the whole row (ugly), even if it could use a smaller width (but larger height).

• It only works if you already know what the parent's size is -- but you might not! Instead, you might simply have a restriction, "the parent's size must be between these two dimensions", but the rest might be open-ended.

I am, however, at a loss of how to come up with a better algorithm -- how do I figure out what would be a good size to to 'propose' to the component? And even when I figure that out, what should I try to optimize, exactly? (The area, the width, the aspect ratio, the number of components on the screen, or something else?)

Any ideas on how I should approach this problem?

• The requirements for the algorithm's output are something you should provide, otherwise the question is overly broad and answering becomes guesswork (as you can see by the comment-style answers you got so far). Jun 10, 2012 at 11:27
• @Raphael: Hmmm... I agree, but the problem is I don't know how to come up with requirements for something like this! What should I do? Jun 10, 2012 at 16:00
• Try some and see where it leads you, then refine. Jun 10, 2012 at 22:02

I've encountered variations of this problem several times doing GUI layout work. Your question about "What should I optimize?" is key to answering your question. Until you've decided that you really can't create a better algorithm.

More than likely you'd like to consider the size-ratios. Each child will have likely a minimum size, possibly a maximum in each dimension, and a preferred ratio, or range of acceptable ratios. You can use this basic data to make a guess as to what the layout should be. Now, for the resulting layout of each child you need a way to produce the preference score.

Now you simply have an optimization problem: maximize the total score of all the children. You can also include global scoring attributes, like the parent size (to cover your second problem). Some layouts you can also reject if they produce invalid results (parent too wide, child not acceptable).

Depending on how many children you have, and how optimal your layout needs to be, will determine how good your algorithm needs to be. For a very simple greedy approach you can simply do line-based. For example, just consider two lines at a time, the current line, and the next line. You should be able to come up with a list of items that easily have enough space on this line, and some that maybe don't. Then just try a few combinations of those trailing components (this line or next) and find a maximum score.

If you're building the components from ground-up, you might never know what the right size is.

This is the classic chicken-egg problem. For flow-layout to work, you need to propagate parent changes to the child. That is the definition of flow-layout.

When you attach a layout to your component, you take on your component's size. When the child is added to the parent, the parent sends it's size to the child's default layout.

Technically, flow layout won't work if even one child's width is greater than the parent's width significantly. You'd then have an option to truncate, or resize.

Most flow layouts work like this: Keep, h = the height of the tallest child in the current row. x, y = moving cursor where children are kept. x keeps getting added by the width of the child, h getting adjusted. When x crosses the width, go to next row, where y is incremented by h. Padding is applicable to x and y.

After this, If width of the child is greater than your width, ask a preferred width option. After this, if the component is too thin, give it the current row height and get it's preferred width again.

You almost always determine the x-y of the child, nothing to do with their size. You should only consider resizing Iff the child goes out of the current whole line.