GridBagLayout and its pitfals

N

nukleus

This is a repost attempting to fix some errors
related to fills and paddings and reorganize things.

- - - - - - - - - - - - - - - - - - - - - - -

I am working on one app that has few frames
to get/set various program parameters.
Some of those frames have several buttons,
several text fields with labels, several checkboxes,
a text area and, status field and a choice boxes.
To make it all look nice and clear from the standpoint
of GUI element layout, you would have to get into
some pretty hairy issues.

Working with it, what I was seeing is nothing less
than magic. Buttons would dissapear in run time,
text fields would have Y size several times smaller
than the font Y size and on and on an on.
I could not believe my eyes seeing all that madness,
and I ended up spending probably 10 times more time
on it all, than it should have taken me, or any
mortal for that matter.

I decided to use my gadget and filter the 25000
article archive of this group, going back at least
half a year, and see if this issue was ever discussed.
The result was nothing but shock. There were but a
handful of articles, most in Sept. 2006, addressing
this issue, and even there, the advices were so
scetchy, that they were virtually useless,
in case of just about any realistic frame imaginable
having more than a few similar GUI elements.

So, let us attempt to fix that problem
and put an end to the whole GridBagLayout debate.

I was recomended by one of people here to read the
Sun's tutorial on GridBagLayout. Yes, I agree, it
is humanly readable, but, unfortunately, the example
it provides is nothing more than a childish invention,
and there is very little chance it is going to be of
any use in any realistic sutuation.
It does not address ANY of subtleties of realistic layouts,
having not just a bunch of buttons,
filling the entire frame, but a mix of various GUI
elements. Not it addresses the boundary conditions,
such as 0 or 1.0 values of weights, or the overall
effect of changing various parameters and their
interdependence.

Finally, what i came up with, was a realization
that about the easiest and the best way of doing it,
was to use nested layouts.

Here is summary of what I have found.
It is all out of memory as I do not want to load
another piece of bloatware and wait for days
when I want to switch some tab. So, some things
may be named incorrectly, but that is not significant
as the very underlying meaning should be clear.

1. In just about any realistic GUI frame,
you are likely to have a mix of various elements,
positioned in such a way that it is not likely
to fit some fixed sized grid.
If you try to lay them all out using a single
GridBagLayout, you'll waste half of your life,
being blown out of the chair when you change
any parameters.

You may try to cheat and create more grid cells,
trying to properly position various unrelated
groups of elements, trying to make one GUI element
to occupy more than one cell in X/Y dimension,
to accomodate for your positioning requirements,
but it will likely to und up in nothing less than
utter frustration.

So, use nested layout instead.

Meaning: Organize all of your similar GUI
elements into logically related areas on your
frame. Group buttons in one group, labels and
text fields in another group, and so the checkboxes,
text areas and the rest of it.

2. Create separate panels for each group of related GUI elements

Use GridBagLayout for each of those panels.
For the group of checkboxes, GridBagLayout is not
the best choice as it does not guarantee the equal
X offsets between the checkboxes.

So, for a check box panel, use GridLayout instead
as it guarantees the equal X size distribution
between all the checkboxes.

Use GridBagLayout for the frame itself, and put
all those panels into separate cells of the frame's
GridBagLayout.

3. Avoid using paddings as much as possible

Their effect may be such that you'll never have
a handle on your layout. Say, for example, you have
a group of labels, in Y dimension, that label the
corresponding text fields. The entire column of labels
will be dimensioned at run time to have an X size
which is a sum of X size of a text string and a value
of padding. If paddings are set to 0, the column size
will be of the longest text string. And if some of
your labels in a column have shorter strings but
larger paddings, that label will take a priority
over all others including even those with the
longest text string. The overall behavior may
become unpredictable.

If you use paddings to pad the label column size,
and, later on, decide to change the text of your
longest label and make it much smaller, all of a
sudden, your whole layout will change.

Paddings are not the absolute size of some GUI element
and should not be used to control the column width.
They are what needs to be added to the size of a
text string, that element contains, be it label,
button, text field.

Do not attempt to use paddings to fix the X size
of your column at some minimal size. If you have to
do that, that means you've got something wrong with
your other constraints. Paddings may work fine for
the Y size of elements containing text.

Use external insets instead. They are guaranteed not to
change regardless of the size of your GUI element and
do not depend on paddings or other constraint variables.
The easiest way to think of them is margins.
They are expressed in absolute terms - pixels.

If you change the text of your label or your
text field, the effect of padding may end up being something
utterly different than what you were hoping to achive.

For example, using paddings on your text fields in Y dimension,
will increase the Y margin between the different label/text
fields in a group, but when you consider weights, the run time
behavior may be simply unpredictable.

You would probably want your fields and labels to stay equally
positioned regardless of the size of your frame
and to be guaranteed their minimum size as limited by the
font you are using and the length of your string.

In that case, set label fills to NONE.

4. Minimize the number of constraint parameters used

Try to keep all the values of constraints structures
at zero instead of filling them with values you THINK
will make it look better. Use only those parameters
that are absolutely necessary, such as external insets.
Because they are margins and those are always needed.
You could achive the same effect with paddings, but
the result will depend on a size of a text string.
If you change your text string in the future, the
entire group may look very different than what you
expected.

The exception is the row/column numbers and a number
of cells you want your GUI element to occupy in X or Y
dimension.

5. Paddings of labels, text fields and text related elements

On all labels, use a small X padding of 3 - 6 pixels
for the labels so that the text field, following that
label in X dimension has some left margin and does not run
into the end of the label string.

Or you can set the label padding to 0 and use External insets
on the text fields following labels, setting Left inset
to achieve a desired margin. This will produce much
more predictable render time results.

Do not use fills or weights on labels, because you will
probably want them to stay of exactly the same size and
have the same margins in between when you resize the
frame unless they label some elements that will change
their position with resizing.

8. Using off the shelf GUI designer

If you use a GUI designer, and it generates Java code
for your constraints, panels, etc., do not modify that
code by hand. You'll waste half of your life if you
ever need to change your layout in the future, which
is pretty much inevitable. All your hand coded stuff
will be either overwritten, or might cause unpredictable
behavior of your new version.

The same exact code as generated by the GUI designer
should work like a champ in just about ANY situation
imaginable.

Forget about the minimum and preferred sizes.
They are virtually useless. You can put some
reasonable values in them, but when you try to
resize your frame, and hope they mean something,
you may be suprised that the minimum and preferred
sizes are simply ignored, as far as having anything
to do with the look and feel of your frame.
I just set them to some reasonable values and forget
about them. Even if they are set to 0, and frame
is correctly designed, you won't have problems
with it in run time.

9. Simplicity of proper structuring

If you do your design correctly, you'll have the
absolute minimum of various parameters to be set
as far as boundaries, sizes, paddings, etc.,
behaving in the most predictable way possible.

10. Separation of GUI and the even handling code

Separate your GUI related code for the frame
and do not add anything to that class beyond
get/set methods for each element.

Make a class that extends that frame and that
is where you can add the keyboard and mouse listeners,
and all the bells and whistes to your frame. That
class has access to its underlying frame's
variables and has the right to do anything it pleases
with those variable, even though it should not attempt
to modify the sizes, constraints etc.
It all has to be done by the code generated by your
GUI designer. Even if you code it by hand,
keep GUI and wrapper code separate. You won't regret.

11. Proprietary layout managers

Try not to use some proprietary layout managers
because they are not guaranteed to work with the
future versions, unless they rely on the existing
basic layouts, which you can do yourself.

Futhermore, they'll end up doing the same thing
the GridBagLayout or other basic layouts do, and,
if you are trying to be lazy, hoping to save some
time or effort, you are likely to regret it at the end,
it is all just a matter of time.

Also, think about the platform compatibility.
Are you sure you will be able to compile your code
under different compiler or even a different version
of the same compiler? Will it work with different version
of jre?

Some claim that by using some proprietary layouts,
they can save half the amount of code lines,
which is about the funniest argument possible.
How much of an overall code size your GUI takes?
Well, 5-10 percent in the worst case. Does it
worth it trying to minimize its size by few
hundred bytes or lines?

Your frame look and feel is about one of the most
important aspects of your entire application.
Just about ALL the users will see with your app
is those very frames. Trying to save a few hundred
bytes or code lines is simply foolish.

Instead, let your gui designer generate as many
constraints in wants. It does not matter at this
state of affairs. A few hundred bytes extra
won't change anything even worth mentioning,
compared to overall look and feel benefits.
But trying to hand tweak the automatically generated
code could cost you a bundle at the end.

12. Testing frame during run time

Try to make your frame resizable and resize it in
run time. If all the GUI elements and groups keep sane
proportions, it means you've got all your constraints
and structure of your nested layout correctly configured.

Spend enough time on making sure your frame
behaves in a very predictable way regardless of
its size. All the GUI elemenents have to look
consistent and corresponding margins and offsets
should not change when frame is resized, unless
you want them to.

13. Try not to use weights, if at all possible.

If your frame has several groups of GUI elements,
each having several elements, then what you think
is an overall space distribution may end up being
utterly different in run time and you'll waste hours
if not days, trying to play with those weights.

Even if you use weights with such elements as
buttons, you may be unpleasantly suprised at
how it looks during the render time, especially
when you resize your frame. Generally, buttons
should not change their size with resizing.
So, using weights is not appropriate. The same
thing is with labels, checkboxes, etc.

Weights refer to relative distribution of space,
WITHIN the group of gui elements, they call
"display area" - ugggghhhh,
and not the frame as such.

They do make sense on a main frame's
GridBagLayout, where you can adjust the
proportional relationships between different
groups. But be very cautious of using weights
within a group as by changing them the end
results may be simply unpredictable,
especially if you consider the boundary
conditions of 0 and 1.0.

Boundary conditions (according to Sun):
0.0 - keep the size of GUI element at its minimal size.
1.0 - enlarge the size of GUI element if frame becomes
larger.

Sun tutorial states:

"When you enlarge GridBagLayoutDemo's window,
the columns grow proportionately.
This is because each component in the first row,
where each component is one column wide, has weightx = 1.0."

*** "The actual value of these components' weightx is unimportant."

[About the most mind blowing statement imaginable.
In this case, the render time behavior is simply
unpredictable.
So, what do you need them for, if their actual value
inside the boundary conditions is unimportant?]

"What matters is that all the components, and consequently,
all the columns, have an equal weight that is greater than 0."

[EQUAL weight?
But what happens if the sum of those weights is > 1.0?
and how the space is distributed if they are > 0 and < 1?
If you have several columns, you'd intuitively try
to set their weights so that the sum will end up being
1.0. But, if some of those components have 0 or 1.0 weights,
then how the space is distributed among them and what
is that space? Is it an extra space left after computing
the string X size, or is it the gui element's overall
size, computed using its insets and padding?
How does the minimal component size relate to the
X size of its text string for example?

Can anyone describe the end result?

You see, how they can possibly render it,
if it violates the common sense arithmetic principles?
If you have n elements in a row, then the sum of their
weights should be 1.0.

If weight of some element = 0, it means it is not rendered.
If weight of some element = 1, it means it is the ONLY
thing that is going to get rendered, as the sum of
weights can not possibly exceed 1.0. But what if you
have several elements with weight 1.0?

But the turorial states that 0 means do not increase
size and 1.0 means do increase it. All other values
in between are "unimportant".

The whole logic of weight concept is UTTERLY inconsistent.

I bet they have ALL sorts of problems with this approach
and even the chief architect could not possibly predict
how is it going to be rendered, even if he looks at their
own source code.
]

"If no component managed by the GridBagLayout had weightx set,
then when the components' container was made wider,
the components would stay clumped together in the center
of the container..."

End of quote.

So, try to predict the overall behavior.

If you only have one group of elements
in your frame, weights MAY work fine,
but, as in any realistically complex frame,
where you have more than one row/column of
your main GridBagLayout, the things may and WILL
become simply bizzare. You'll sooner go nuts
than get the results you expect from all that
mess. By changing any element in a different
group of elements, the whole frame may look
utterly ugly.

Even to use weights for each cell of the main
gridbag of your frame, hoping that relationship
between groups will remain consistent, you may
be very unpleasantly surprised with the results
while resizing that frame or adding/removing
some elements.

Weights is probably close to useless if you consider
all the remifications of using them in relatively
complex frames. The overall effect of weights
depends on your other constraints, such as fills,
paddings and insets. The overall results is
complexity of behavior that can hardly be predicted.

But yes, they do work, at least in the heads of the
architects that created them. But the consequences of
using them are much harder to predict than it may look
at first glance.

Summary:

Instead of using a single GridBagLayout on just about
any realistically complex frame, that has several
different types of GUI elements, group the related
elements and put them into separate panels.

Select the layout for each panel that suits your
frame design concepts in the easiest and most
natural way. Do not try to make things more complex
than necessary hoping to make it look better,
as about ALL you are going to get at the end
is a royal grade pain on the neck.
It won't work. Trust me.

Try to use GridBagLayout for all those panels
whenever possible as this is the most general
purpose layout having all the power to do just
about anyting imaginable. But it has its limitations
and unpredictability of behavior if you expect
some rows or columns to be of equal size
regarless of the label, text field or button text
string sizes.

GridBagLayout is not a good choice for that because the
column size relationships may change in run time when
user tries to resize the frame, and your columns will
end up of not equal size, and, trying to make them of
equal size, by using paddings or weights, could end up
being a pain on the neck and of a grand proportions.

Use as few parameters in your constraints structure
as possible.

By using nested gridbags, your design will end up
being the simpliest possible with WAY more predictable
results, the most powerful and flexible, have the smallest
amount of various cells possible, and with the smallest
amount of various constraint parameters, which will
eventually translate into the most stable, predictable
and flexible run time behavior that assures minimal amount
of maintenance efforts of your code and provide for ease
of future modifications.

If you follow these few simple advises,
you'll be amazed at how much power and flexibility
you can achieve with nested GridBagLayout.

Good luck.

Finally, if any experts around have any comments,
it would probably be a good idea to clarify some
more subtle points and issues, or correct some
errors or philosophical fine points as this information
will become a permanent reference on this issue
and will be available through argives for generations
to come.

Simmply sending people to read some Sun "tutorial",
you are effectively sending them to hell,
as those "tutorials" cover only the most primitive
and most basic concepts having virtually nothing
to do with a realistic application.

Instead, you can spend half an hour of your time
and spill out the fine points of this most important
thing there is in the entire concept of GUI design,
GridLayout, which all would have to learn eventually
no matter what.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,189
Members
46,734
Latest member
manin

Latest Threads

Top