12 July 2010

GUI in GridBagLayout

Basically, I do not like GridBagLayaout, but from my point of view it's the most effective way to build and maintain GUI. So before explaining my point of view, let's eliminate other path.

Graphical editor :

Alternative for writing a code is a creating GUI through a graphical editor, like the one in NetBeans. The effect obtained in this way is good, but it brings few problems as well:

  • Editor may lock GUI code.
  • Too many changes might:
    • Mess the code,
    • Force us to create GUI once again,
    • Remove some important part of code,
  • Mostly we are not able to move GUI code to other IDE freely.
For me, reasons for 'no' have been unacceptable.

GUI as code & GridConstraintsBuilder:

Let's back to GridBagLayout. The greatest obstacle in using this layout in a code form is GridBagConstraint. Wasting line for each property does not make it readable at all, especially when we have to realign GUI with more than 10 elements. Folding its construction to multi parameter util-method does not make it really easy to handle especially for newbie.
Because of this reasons GridBagConstraint was rewritten to the builder form. For me, this way allows to keep a correct proportion between code's readability and its size. So there is no need to stuck on code analysing to make a small change.

One of the most important part of the new code is its integration with the existing one, because no one will allow himself to manage new and old ways. .printAsJavaCode$(...) method was created to make this inequality to be possible to happen:
$time{conversion code to new form} + $time{work on new-like code} <= $time{work on old-like code}

Main problems:

  • Conversion to Java code is not able to determine if value is provided by constant or by variable. This makes some additional checking as required along with the conversion.
  • There is no interface for constrain, so passing builder to component may happen.
  • Rich methods' set has its disadvantages as well. On the one hand, we can easily pass interesting arguments, but in this way, we lose a lot of highlightings.

Builder:

  • Sample of usage:
    JFrame frame = new JFrame();
    JPanel panel = new JPanel(new GridBagLayout());
    frame.setContentPane(panel);
    GridConstraintsBuilder base = GridConstraintsBuilder.newOnCenter()//
        .setYAsRelative().setVerMarginY(2, 0, 4);
    GridConstraintsBuilder labels = base.clone();
    GridConstraintsBuilder fields = base.clone();

    labels.setAlignAsToRight()//
        .setX(1).setHorMarginX(5, 0, 10);
    fields.setAlignAsToLeft()//
        .setX(3).setHorMarginX(5, 0, 5);

    for (String text : new String[] { "Some", "One", "And other" }) {
        panel.add(new JLabel(text), labels.build());
        panel.add(new JTextField(text), fields.build());
    }
    frame.setSize(300, 200);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);
  • Sample of conversion of GridBagLayoutDemo.java :
    At first we can convert constraints to Java using:
    ...
    pane.add(button, GridConstraintsBuilder.printJavaCode$(c));
    ...
    Execution of such a code would produce:
    GridConstraintsBuilder.newOnCenter(0,0)//
        .fillHorizontal().provide() ;
    GridConstraintsBuilder.newOnCenter(1,0).setWeight(0.5,0.0)//
        .fillHorizontal().provide() ;
    GridConstraintsBuilder.newOnCenter(2,0).setWeight(0.5,0.0)//
        .fillHorizontal().provide() ;
    GridConstraintsBuilder.newOnCenter(0,1).setSize(3,1)//
        .fillHorizontal().setInternalPadding(0,40).provide() ;
    GridConstraintsBuilder.newOnPageEnd(1,2).setSize(2,1).setWeight(0.0,1.0)//
        .fillHorizontal().setInsets(0,10,0,0).provide() ;
    So after including 'shouldFill' and 'shouldWeightX' the final code would look like:
    GridConstraintsBuilder button1 = GridConstraintsBuilder.newCenter(0, 0)//
        .fillHorizontal();
    if (shouldWeightX) button1.setWeightX(0.5);
    pane.add(new JButton("Button 1"), button1.build());
    pane.add(new JButton("Button 2"), GridConstraintsBuilder//
        .newCenter(1, 0).setWeight(0.5, 0.0)//
        .fillHorizontal().build());
    pane.add(new JButton("Button 3"), GridConstraintsBuilder//
        .newCenter(2, 0).setWeight(0.5, 0.0)//
        .fillHorizontal().build());
    pane.add(new JButton("Long-Named Button 4"), GridConstraintsBuilder//
        .newCenter(0, 1).setSize(3, 1)//
        .fillHorizontal().setInternalPadding(0, 40).build());
    pane.add(new JButton("5"), GridConstraintsBuilder//
        .newPageEnd(1, 2).setSize(2, 1).setWeight(0.0, 1.0)//
        .fillHorizontal().setInsets(0, 10, 0, 0).build());

Resources:

GridConstraintsBuilder code
    Menadżerowie ułożenia.pdf

4 comments:

  1. Check out the MigLayout then you will change your ming about layouts in Swing :)

    ReplyDelete
  2. @Rafał Wasielewski
    I saw MigLayout a year or two ago, and I was impressed by it.
    They added really nice functions, but in my opinion it will be never accepted as official layout manager because of the facts:
    → It's based on string programming.
    → More functionality comes with more complexity. Because of that it's hard to accept a new complicated layout when there is a chance for one more to be added.
    → MigLayout still lacks a few functions.

    ReplyDelete
  3. MigLayout is so much better they can't really be compared. And it has a non-string mode as well.

    ReplyDelete
  4. @Anonymous
       Some times one level higher quality is not enough. We all know how short can developers work in one company, because of that it can be better to use a notch below solution but globally used, because this increase amount of documentation, decrease numbers of bug...

       For me do not matter if it has non-string mode as well, because even if one person will use objects, other may not. Allowing to bad habits is most often reason of discarding proposal from Java language and library.

       MigLayout is great tool, but I think and hope that it will be never accepted as official solution in current form.

    Greetings!

    ReplyDelete