Button at bottom of dialog with MigLayout

MigLayout is a powerfull Swing layout manager.

However, when you are used to the standard J2SE layout managers, it can take a while to understand how to do something using MigLayout.

For example, I needed to create this UI:

As you can see, the idea is to leave the OK button at the button of the dialog, even when the dialog is resized.

My initial attempt was to use a fake panel as the third row:

public class TestResize extends JDialog {
protected JPanel contentPane;
public TestResize() {
   super((Dialog) null, "Test resize", true);
   setupUI();
   setContentPane(contentPane);
}
private void setupUI() {
   contentPane = new JPanel(new MigLayout());
   // first row
   contentPane.add(new JLabel("Enter size"), "");
   contentPane.add(new JTextField(""), "grow, pushx, wrap");
   // second row
   contentPane.add(new JLabel("Enter weight"), "");
   contentPane.add(new JTextField(""), "grow, pushx, wrap");
   // third row = fake panel that is allowed to grow
   contentPane.add(new JPanel(), "span 2, grow, pushy, wrap");
   // fourth row = panel with centered button
   JPanel buttonPanel = new JPanel(new MigLayout("", "[center, grow]"));
   buttonPanel.add(new JButton("Ok"), "");
   contentPane.add(buttonPanel, "dock south");
}
public static void main(String[] args) {
   TestResize dialog = new TestResize();
   dialog.pack();
   dialog.setVisible(true);
}
}

This works… but it’s not the correct way to do it using MigLayout: you need to use  the “push” constraint.

public class TestResize extends JDialog {
protected JPanel contentPane;
public TestResize() {
   super((Dialog) null, "Test resize", true);
   setupUI();
   setContentPane(contentPane);
}
private void setupUI() {
   // Layout is constructed with "push" constraint
   contentPane = new JPanel(new MigLayout("", "", "[][]push[]"));
   // first row
   contentPane.add(new JLabel("Enter size:"), "");
   contentPane.add(new JTextField(""), "grow, pushx, wrap");
   // second row
   contentPane.add(new JLabel("Enter weight"), "");
   contentPane.add(new JTextField(""), "grow, pushx, wrap");
   // third row = panel with centered button
   JPanel buttonPanel = new JPanel(new MigLayout("", "[center, grow]"));
   buttonPanel.add(new JButton("Ok"), "");
   contentPane.add(buttonPanel, "dock south");
}
public static void main(String[] args) {
TestResize dialog = new TestResize();
dialog.pack();
dialog.setVisible(true);
}
}

That’s it: no more crappy fake panel ! 🙂

Laurent KUBASKI

Advertisements

JTable tips

Here are 2 common requirements when working with Swing’s JTable:

  • validating the editing of a cell without having to hit the “enter” key
  • preventing a JTable to intercept the “enter key” (so that the default button of your dialog is correctly activated)
Here is a little code sample that illustrates these 2 points:
public class TestTable extends JDialog {
  private JTable table;

  public TestTable() {
  super((Frame) null, "The dialog", true);
  setupUI();
  }

  private void setupUI() {
  table = new JTable();
  DefaultTableModel model = (DefaultTableModel) table.getModel();
  model.setColumnIdentifiers(new Object[]{"myColumn"});
  model.addRow(new String[]{"Row 1 (edit me)"});
  model.addRow(new String[]{"Row 2"});
  getContentPane().add(new JScrollPane(table), BorderLayout.CENTER);
  JButton defaultButton = new JButton("default button");
  defaultButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
    JOptionPane.showMessageDialog(TestTable.this, table.getValueAt(0, 0));
    }
  });
  getRootPane().setDefaultButton(defaultButton);
  getContentPane().add(defaultButton, BorderLayout.SOUTH);
  }

  public static void main(String[] args) {
    TestTable dialog = new TestTable();
    dialog.pack();
    dialog.setVisible(true);
    System.exit(0);
  }
}

Here is the output:


Now if you click on the first row and if you hit the “enter” key, the focus switches to the next row in the JTable (although you defined the “default button” button to be the default button of your dialog):



Now if you start editing the first cell (replacing “edit me” by “edit ME”) and click on the button, this is what happens:



Damn… why does the message box display the old value ? Well, this is because to validate the edition of a cell in a JTable, you are supposed to hit the “enter” key on your keyboard.
Both problems can be solved by calling this method inside the constructor:
private void changeTableProperties() {
  // now hitting "enter" will trigger the "default button" action:
  // (see source code of JTable.CellEditorRemover.propertyChange() method for more information)
  table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
       .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "none");
  // now you can click on the button without having to hit "enter" to finish editing the row:
  // see http://download.oracle.com/javase/tutorial/uiswing/misc/keybinding.html for more information
  table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
  }


From there, hitting the “enter” key will correctly trigger the default button action:



And when editing the cell, you’ll be able to directly click on the button to have your input taken into account (without the need to hit the “enter” key:


Laurent KUBASKI