KappaLayout

KappaLayout

I've been working with Java for a long time now, and keep running into the same problem -- lack of a decent layout manager. I've tried the various IDE's, but found them incapable of producing an efficient and maintainable interface. For the most part, they are limited to the same old AWT layout managers and tend to produce inefficient and hard to follow code. Others use a proprietary language or file format to create the layout, which is difficult or impossible to maintain without using proprietary tools.

I've searched the internet for a decent layout manager, but haven't found one that is as flexible or powerful as GridBag. I have used GridBag a lot, I have yet to find a layout that GridBag is unable to handle. What I don't like about GridBag is that it is cumbersome to use.

I did find a TableLayout manager from Westhawk (www.westhawk.co.uk) that I liked and used a lot with Java 1.1.x, but for some reason it didn't work correctly with Java 2. Westhawk's TableLayout uses a simple constraint string that makes it real easy to create complex layouts. I was really disappointed to find that it had problems with Java 2. I started to look into the source code, and found it is very C-like and clearly shows its C origins. I thought, "Make your own!"

So I wrote the KappaLayout -- similar to the others, but this one's simpler, easier to use, flexible and as powerful as GridBag. I used several layouts for inspiration, notably GridBag and TableLayout, but also BoxLayout and Grid. I like the constraint string of TableLayout, but also like the flexibility and power of GridBagConstraints. I like the strut concept from Box, and the ability to make components (particularly buttons) the same size that Grid provides. KappaLayout incorporates all of these and improves and simplifies. KappaLayout's constraint string is more flexible than TableLayout, KappaLayout.Constraints is simpler and easier to use than GridBagConstraints.

Here's a real simple example:
This will put a button on a panel in the top of its cell, stretched to fill the cell width, with a 3 pixel pad:

Panel p = new Panel(new KappaLayout());
Button b = new Button("OK");
p.add(b, "0, 0, 1, 2, 2, w, 3");

The Constraints String

The constraints string has this format:
"x, y, w, h, a, s, p"
defined as follows:
Parameters may be omitted (default values will be used), e.g.,
 p.add(new Button("OK"), ",4,,,w,");
which means put the button at column 0, row 4, default 1 column wide, default 1 row tall, stretch to fit width of column, no padding.
White space in the parameter string is ignored, so these are identical:
 p.add(new Button("OK"), ",4,,,w,");
 p.add(new Button("OK"), " 0, 4,   , , w");
Here's an example of the minimal constraint string:
 p.add(new Button("OK"), "");
which defaults to "0,0,1,1,0,0,0".

The Constraints Object

Rather than use a constraints string, a KappaLayout.Constraints object may be used directly, similar to how GridBag uses a GridBagConstraints. For example,
Panel p = new Panel(new KappaLayout());
KappaLayout.Constraints con = KappaLayout.createConstraint();
con.x = 1;
con.y = 2;
con.w = 2;
con.h = 2;
con.s = "wh";
panel.add(new Button("OK"), con);
con.x += 3;
panel.add(new Button("Cancel"), con);
For the alignment parameter, use the numbers as described above. To use the alternate compass directions, use one of:
KappaLayout.N
KappaLayout.NE
KappaLayout.E
KappaLayout.SE
KappaLayout.S
KappaLayout.SW
KappaLayout.W
KappaLayout.NW
The center of the cell is always 0.
Note that the same Constraints can be reused, thereby reducing the number of objects created.

Boxes and Struts

KappaLayout also provides struts, similar to Box. Struts are invisible components that are useful for providing blank space between visible components or for setting a minimum size on a column or row. Horizontal struts are created by calling

KappaLayout.createHorizontalStrut(int width)

and have width but no height. Vertical struts are created by calling

KappaLayout.createVerticalStrut(int height)

and have height but no width. Calling the method

KappaLayout.createStrut(int width, int height)

creates a rectangular strut or box.

Struts as used above set a minimum size on a column or row. The column or row may be wider or taller if larger components are added in the same column or row. Struts can also be rigid, which means that they effectively lock the width or height of the column or row:

using
KappaLayout.createHorizontalStrut(int width, boolean rigid)

like this:
p.add(KappaLayout.createHorizontalStrut(10, true), "0, 0") sets the first column to exactly 10 pixels wide.

Convenience Methods

KappaLayout has several other methods that can be of help in laying out components.

Columns and rows can be set to a specific width or height, respectively, by calling

setColumnWidth(int column, int width)

or

setRowHeight(int row, int height)

These methods are useful for setting a maximum size on a column or row or setting equal width columns or rows. Note that empty columns or rows will have 0 size regardless of setting to a specific size, so put a strut in the column or row to make a blank column or row actually have some size. Use these methods with care since the component will be truncated if the component in the column or row is larger than the set width or height.

KappaLayout has better methods of making two or more columns or rows the same size:

makeColumnsSameWidth(int column1, int column2)
makeColumnsSameWidth(int[] columns)
makeRowsSameHeight(int row1, int row2)
makeRowsSameHeight(int[] rows)

These methods do just what their names imply. The actual column width or row height is determined by taking the widest or tallest and making the other columns or rows the same. These methods will not truncate a component in either direction.

Examples

Following are several complete examples. These examples use both Container.add(String, Component) and Container.add(Component, Object). JDK documentation recommends the later, although the former tends to line up the constraints strings making them easier to manage. As far as KappaLayout is concerned, either is acceptable. Of course, the add(Component, Object) method must be use when using KappaLayout.Components directly.

The first three examples make the same dialog, but in different ways.

Example 1



import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;

public class KappaLayoutTest {

   public KappaLayoutTest() {
      // set up a Frame
      Frame f = new Frame("KappaLayout Test");
      f.addWindowListener(new WindowAdapter() {
                             public void windowClosing(WindowEvent we) {
                                f.dispose();
                             }
                          });
      f.setLayout(new KappaLayout());

      // standard use of KappaLayout
      Panel p1 = new Panel(new KappaLayout());
      p1.add("0,0,1,1,7,,3", new Label("Enter name of file to copy:"));
      p1.add("0,1,1,1,,,3", new TextField(50));
      p1.add("1,1,1,1,,hw,3", new Button("Browse..."));

      // use KappaLayout.Constraints, this makes a panel identical to panel p1
      Panel p2 = new Panel(new KappaLayout());
      KappaLayout.Constraints con = KappaLayout.createConstraint();
      con.x = 0;
      con.y = 0;
      con.w = 1;
      con.h = 1;
      con.a = 7;
      con.p = 3;
      p2.add(new Label("Enter name for copy of file:"), con);
      con.y += 1;
      con.a = 0;
      p2.add(new TextField(50), con);
      con.x += 1;
      con.s = "wh";
      p2.add(new Button("Browse..."), con);

      // make a button panel -- make the buttons the same width by
      // stretching to fill cells horizontally and setting the columns to the
      // same width
      KappaLayout kl3 = new KappaLayout();
      Panel p3 = new Panel(kl3);
      p3.add("0,1,1,1,,w", new Button("Start Copy"));
      p3.add("1,1,1,1,,w", new Button("Cancel"));
      kl3.makeColumnsSameWidth(0, 1);

      // lay out the frame, using a couple of vertical struts to unclutter the parts
      f.add("0,0,1,1", p1);
      f.add("0,1,1,1", KappaLayout.createVerticalStrut(20));
      f.add("0,2,1,1", p2);
      f.add("0,3,1,1", KappaLayout.createVerticalStrut(20));
      f.add("0,4,1,1", p3);
      f.pack();
      f.show();
   }

   public static void main(String[] args) {
      KappaLayoutTest tlt = new KappaLayoutTest();
   }
}

Example 2

Here is the same example using Swing components. Note the minor tweaking to get a similar appearance as Swing uses bold font rather than plain font.



import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;

public class KappaLayoutTestSwing {

   public KappaLayoutTestSwing() {
      // set up a Frame
      JFrame f = new JFrame("KappaLayout Test with Swing");
      f.addWindowListener(new WindowAdapter() {
                             public void windowClosing(WindowEvent we) {
                                f.dispose();
                             }
                          });
      JPanel contents = new JPanel(new KappaLayout());
      f.setContentPane(contents);

      // standard use of KappaLayout
      JPanel p1 = new JPanel(new KappaLayout());
      p1.add("0,0,1,1,7,,3", new JLabel("Enter name of file to copy:"));
      p1.add("0,1,1,1,,,3", new JTextField(30));
      p1.add("1,1,1,1,,hw,3", new JButton("Browse..."));

      // use KappaLayout.Constraints, this makes a panel identical to panel p1
      JPanel p2 = new JPanel(new KappaLayout());
      KappaLayout.Constraints con = KappaLayout.createConstraint();
      con.x = 0;
      con.y = 0;
      con.w = 1;
      con.h = 1;
      con.a = 7;
      con.p = 3;
      p2.add(new JLabel("Enter name for copy of file:"), con);
      con.y += 1;
      con.a = 0;
      p2.add(new JTextField(30), con);
      con.x += 1;
      con.s = "wh";
      p2.add(new JButton("Browse..."), con);

      KappaLayout kl3 = new KappaLayout();
      JPanel p3 = new JPanel(kl3);
      p3.add("0,1,1,1,,w, 3", new JButton("Start Copy"));
      p3.add("1,1,1,1,,w, 3", new JButton("Cancel"));
      kl3.makeColumnsSameWidth(0,1);

      // lay out the frame, using a couple of vertical struts to unclutter the parts
      contents.setBorder(new EtchedBorder());
      contents.add("0,0,1,1", KappaLayout.createVerticalStrut(10));
      contents.add("0,1,1,1", p1);
      contents.add("0,2,1,1", KappaLayout.createVerticalStrut(20));
      contents.add("0,3,1,1", p2);
      contents.add("0,4,1,1", KappaLayout.createVerticalStrut(20));
      contents.add("0,5,1,1", p3);
      f.pack();
      f.show();
   }

   public static void main(String[] args) {
      KappaLayoutTestSwing tlts = new KappaLayoutTestSwing();
   }
}
Example 3

Same example, but using KappaLayout straight up, without subpanels. This produces the same layout as above. This shows KappaLayout at it's best: simple, quick, and powerful.


import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;

public class KappaLayoutTestSwing2 {

   public KappaLayoutTestSwing2() {
      // set up a Frame
      JFrame f = new JFrame("KappaLayout Test with Swing");
      f.addWindowListener(new WindowAdapter() {
                             public void windowClosing(WindowEvent we) {
                                f.dispose();
                             }
                          });
      KappaLayout kl = new KappaLayout();
      JPanel contents = new JPanel(kl);
      f.setContentPane(contents);

      // layout:
      // LLLLLLLL   1 label, 8 columns wide
      // TTTTTTTB   1 textfield, 7 columns wide, 1 button
      // S          a strut to unclutter the parts
      // LLLLLLLL   1 label, 8 columns wide
      // TTTTTTTB   1 textfield, 7 columns wide, 1 button
      // S          a strut to unclutter the parts
      //    BB      2 buttons

      contents.setBorder(new EtchedBorder());
      contents.add("", KappaLayout.createVerticalStrut(10));
      contents.add("0,1,8,1,7, ,3", new JLabel("Enter name of file to copy:"));
      contents.add("0,2,7,1,7,w,3", new JTextField(15));
      contents.add("7,2,1,1,7,w,3", new JButton("Browse..."));
      contents.add("0,3,1,1", KappaLayout.createVerticalStrut(20));
      contents.add("0,4,8,1,7,,3", new JLabel("Enter name of file to copy:"));
      contents.add("0,5,7,1,7,w,3", new JTextField(15));
      contents.add("7,5,1,1,7,hw,3", new JButton("Browse..."));
      contents.add("0,6,1,1", KappaLayout.createVerticalStrut(20));
      contents.add("4,7,1,1,,w, 3", new JButton("Start Copy"));
      contents.add("5,7,1,1,,w, 3", new JButton("Cancel"));
      kl.makeColumnsSameWidth(4,5);

      f.pack();
      f.show();
   }

   public static void main(String[] args) {
      KappaLayoutTestSwing2 tlts = new KappaLayoutTestSwing2();
   }
}
Example 4

Here's an example that is difficult to make look good, even with GridBag. In particular, getting the buttons between the lists centered vertically is difficult, centering the buttons on the bottom is hard, and keeping the lists the same width is a hassle. KappaLayout makes this easy. Here's what it looks like:

And here's the code:

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;

public class KappaLayoutTest3 {

   public KappaLayoutTest3() {
      // set up a Frame
      Frame f = new Frame("KappaLayout Test 3");
      f.addWindowListener(new WindowAdapter() {
                             public void windowClosing(WindowEvent we) {
                                f.dispose();
                             }
                          });
      KappaLayout kl = new KappaLayout();
      f.setLayout(kl);

      /*
      Design:
       012
      0L L     L = Labels
      1C C     C = List, span 6 rows
      2C C
      3CBC     B = Button, ->
      4CBC     B = Button, <-
      5C C
      6C C
      7PPP     P = Panel w/Buttons, span 3 columns
      */
      f.add("0,0,,1,7,w,2", new Label("Pick something from this list:"));
      f.add("2,0,,1,7,w,2", new Label("Selected items:"));
      f.add("0,1,,6,,wh,5", new List(10));
      f.add("2,1,,6,,wh,5", new List(10));
      f.add("1,3,,,,w", new Button("->"));
      f.add("1,4,,,,w", new Button("<-"));
      kl.makeColumnsSameWidth(0,2);

      KappaLayout kl2 = new KappaLayout();
      Panel button_panel = new Panel(kl2);
      button_panel.add("0,1,1,,,w", new Button("OK"));
      button_panel.add("1,1,1,,,w", new Button("Cancel"));
      kl2.makeColumnsSameWidth(0,1);
      f.add("0,7,3,1", button_panel);

      f.pack();
      f.show();
   }

   public static void main(String[] args) {
      new KappaLayoutTest3();
   }
}
Example 5

Here's one last example that shows the power of KappaLayout.Constraints.

import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class KappaLayoutTest4 {

   public KappaLayoutTest4() {
      // set up a Frame
      Frame f = new Frame("KappaLayout Test 4");
      f.addWindowListener(new WindowAdapter() {
                             public void windowClosing(WindowEvent we) {
                                f.dispose();
                             }
                          });
      KappaLayout kl = new KappaLayout();
      f.setLayout(kl);

      // make a calendar like Sean's
      KappaLayout kl1 = new KappaLayout();
      Panel p1 = new Panel(kl1);
      p1.add(new Button("<"),   "0,0,1,1");
      p1.add(new Label("Aug", Label.CENTER),  "1,0,1,1,0,w");
      p1.add(new Button(">"),   "2,0,1,1");
      p1.add(KappaLayout.createHorizontalStrut(10), "3,0");
      p1.add(new Button("<"),   "4,0,1,1");
      p1.add(new Label("2000", Label.CENTER), "5,0,1,1,0,w");
      p1.add(new Button(">"),   "6,0,1,1");
      kl1.makeColumnsSameWidth(new int[]{0,2,4,6});
      kl1.makeColumnsSameWidth(1,5);

      KappaLayout kl2 = new KappaLayout();
      Panel p2 = new Panel(kl2);
      p2.add(new Label("Su", Label.CENTER), "0,0");
      p2.add(new Label("Mo", Label.CENTER), "1,0");
      p2.add(new Label("Tu", Label.CENTER), "2,0");
      p2.add(new Label("We", Label.CENTER), "3,0");
      p2.add(new Label("Th", Label.CENTER), "4,0");
      p2.add(new Label("Fr", Label.CENTER), "5,0");
      p2.add(new Label("Sa", Label.CENTER), "6,0");
      KappaLayout.Constraints q = KappaLayout.createConstraint();
      q.s = "w";
      for ( int i = 0; i < 6; i++ ) {
         for ( int j = 0;  j < 7; j++ ) {
            q.x = j;
            q.y = i + 1;
            if ( i * 7 + j > 0 && i * 7 + j <= 31 ){
               Button b = new Button(String.valueOf(i * 7 + j));
               if ((i * 7 + j) % 7 == 0 || (i * 7 + j) % 7 == 6)
                  b.setBackground(Color.cyan.darker());
               p2.add(b, q);
            }
         }
      }
      kl2.makeColumnsSameWidth(new int[]{0,1,2,3,4,5,6});

      f.add(p1, "0,0,,,,,3");
      f.add(KappaLayout.createVerticalStrut(2), "0,1");
      f.add(p2, "0,2,,,,,3");

      f.pack();
      f.show();
   }

   public static void main(String[] args) {
      new KappaLayoutTest4();
   }
}

SourceForge Logo