Saturday, February 07, 2009

JInternalFrame lesson learned

I am not a big fan of MDI (Multiple Document Interface) UI in general. I much prefer SDI or, better, UIs with docking capability.

Hence I don't have much experience with Swing JDesktopPane & JInternalFrame (the basic building bricks for creating an MDI UI in Swing).

However, a few days ago, an engineer from my company, developing a sample MDI UI (for an exercise related to a "GUI training"), was using DesignGridLayout LayoutManager and found a strange behavior in one of his windows, as demonstrated in the following screenshot, taken after a few pixels increase of the frame height (note the table overlapping the "Open" button):


The layout code is there:
UIHelper.addGroup(layout, "Criteria");
layout.row().grid(projectNameLabel).add(projectName).grid(projectNumberLabel).add(projectNumber);
layout.row().grid(workloadMinLabel).add(workloadMin).grid(workloadMaxLabel).add(workloadMax);
layout.row().grid(startDateFromLabel).add(startDateFrom).grid(startDateToLabel).add(startDateTo);
layout.row().center().add(searchButton, resetButton);

UIHelper.addGroup(layout, "Results");
layout.row().center().fill().add(projectScrollPane);
layout.row().center().add(openButton);
I found it strange because I had never faced this bug with DesignGridLayout before. Hence I have started investigating. The first surprise to me was that I could not reproduce this problem in a JFrame or a JDialog!

Hence I've written the simplest Test Case application, using JInternalFrame, to reproduce this problem:
JFrame mainFrame = new JFrame();
mainFrame.setName(getClass().getSimpleName());
mainFrame.setBounds(30, 30, 800, 600);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

JDesktopPane desktop = new JDesktopPane();
mainFrame.setContentPane(desktop);
mainFrame.setVisible(true);

JInternalFrame frame = new JInternalFrame("", true, true, true, true);
frame.setLocation(30, 30);

JPanel top = new JPanel();
JTable table = new JTable(CONTENTS_PLAYERS, COLUMNS_PLAYERS);
setTableHeight(table, 3);
DesignGridLayout layout = new DesignGridLayout(top);
layout.row().center().fill().add(new JScrollPane(table));
layout.row().center().add(new JButton("OK"));

frame.setContentPane(top);
frame.pack();
desktop.add(frame);
frame.setVisible(true);
try
{
frame.setSelected(true);
}
catch (java.beans.PropertyVetoException e)
{
}
Here is the screenshot (at preferred size):

After a long debugging session, I found out that the preferred height of the JScrollPane in the above example was incorrect at the time Swing calls LayoutManager.preferredSize(); but when the frame is displayed (LayoutManager.layoutContainer() is called), the height of the JScrollPane is correct!

Further investigation (and pixels counting on the screen!) has shown me that during the call to pack(), the preferred height of the JScrollPane is equal to the preferred height of the embedded JTable, plus the JScrollPane borders, but it does not include the JTableHeader!

After checking Swing source code and trying to change slightly the code that packs and shows the JInternalFrame, I found out that the problem is that pack() is called too early!

The following change just removed the problem:
// Order of calling desktop.add() is important wrt frame.pack() call!
desktop.add(frame);
frame.pack();
//desktop.add(frame);
frame.setVisible(true);

The rant

This problem is specified nowhere in JDesktop or JInternalFrame javadoc! In addition, the JInternalFrame section of the Swing tutorial is totally unclear and even misleading about that point!

Well that's a lesson learned for me:
Always make sure you add JInternalFrame to the JDesktopPane before calling pack().

Event better: avoid MDI like the plague (I remember, a long time ago, being allergic to Swing MDI because of various other problems...)

2 comments:

  1. Hi Jean,
    I'm curious what inside the method "setTableHeight(table, 3);"? My JTable is always taking too many height in DesignGridLayout

    ReplyDelete
  2. Hello, I have described this method in another post: http://jfpoilpret.blogspot.ch/2009/01/swing-ui-layout-best-practices.html (in section 7).

    ReplyDelete