JDesktopPane preferred size set by content
java, jdesktoppane, jscrollpane, preferredsize, swing
Solution
Adding a `MouseListener` to the `JInternalFrame` title pane while in `JDesktopPane.LIVE_DRAG_MODE` to `revalidate` the `JDesktopPane` after release is a way to get the exact same behavior in each mode.
final MouseListener testList = new MouseListener() {
@Override
public void mouseReleased(MouseEvent e) {
dt.revalidate();
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseClicked(MouseEvent e) {
}
};
// causes maximized internal frames to be resized..
dt.addComponentListener(componentListener);
for (JInternalFrame jif : dt.getAllFrames()) {
for (Component comp : jif.getComponents()) {
if (comp instanceof BasicInternalFrameTitlePane) {
comp.addMouseListener(testList);
}
}
}
final JCheckBox outLineDragMode = new JCheckBox("Outline Drag Mode");
ActionListener dragModeListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (outLineDragMode.isSelected()) {
dt.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
for (JInternalFrame jif : dt.getAllFrames()) {
for (Component comp : jif.getComponents()) {
if (comp instanceof BasicInternalFrameTitlePane) {
comp.removeMouseListener(testList);
}
}
}
} else {
dt.setDragMode(JDesktopPane.LIVE_DRAG_MODE);
for (JInternalFrame jif : dt.getAllFrames()) {
for (Component comp : jif.getComponents()) {
if (comp instanceof BasicInternalFrameTitlePane) {
comp.addMouseListener(testList);
}
}
}
}
}
};
I remove them in the `JDesktopPane.OUTLINE_DRAG_MODE` since it already reacts properly.
Problem
I have been trying to tame `JDesktopPane` to work nicely with a resizable GUI & a scroll pane, but am having some troubles doing so. It seems that unless the drag mode is outline, the desktop pane will not resize as expected (when an internal frame is dragged beyond the edge of the desktop pane) & therefore not produce scroll-bars. Am I doing something very silly in this source? Have I missed a far better approach? ``` import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.EmptyBorder; public class MDIPreferredSize { public static void main(String[] args) { Runnable r = new Runnable() { @Override public void run() { final JDesktopPane dt = new JDesktopPane() { @Override public Dimension getPreferredSize() { Dimension prefSize = super.getPreferredSize(); System.out.println("prefSize: " + prefSize); // inititialize the max to the first normalized bounds Rectangle max = getAllFrames()[0].getNormalBounds(); for (JInternalFrame jif : this.getAllFrames()) { max.add(jif.getNormalBounds()); } System.out.println("maxBounds(): " + max); int x1 = max.width + (max.x * 2) < prefSize.width ? prefSize.width : max.width + (max.x * 2); int y1 = max.height + (max.y * 2) < prefSize.height ? prefSize.height : max.height + (max.y * 2); System.out.println("x,y: " + x1 + "," + y1); return new Dimension(x1, y1); } }; dt.setAutoscrolls(true); int xx = 5; int yy = 5; int vStep = 10; int yStep = 22; for (int ii = 0; ii < 3; ii++) { JInternalFrame jif = new JInternalFrame( "Internal Frame " + (ii + 1), true, true, true); dt.add(jif); jif.setLocation(xx, yy); xx += vStep; yy += yStep; jif.setSize(200, 75); jif.setVisible(true); } ComponentListener componentListener = new ComponentListener() { @Override public void componentResized(ComponentEvent e) { e.getComponent().validate(); } @Override public void componentMoved(ComponentEvent e) { e.getComponent().validate(); } @Override public void componentShown(ComponentEvent e) { e.getComponent().validate(); } @Override public void componentHidden(ComponentEvent e) { // do nothing } }; // causes maximized internal frames to be resized.. dt.addComponentListener(componentListener); final JCheckBox outLineDragMode = new JCheckBox("Outline Drag Mode"); ActionListener dragModeListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (outLineDragMode.isSelected()) { dt.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE); } else { dt.setDragMode(JDesktopPane.LIVE_DRAG_MODE); } } }; outLineDragMode.addActionListener(dragModeListener); JPanel gui = new JPanel(new BorderLayout()); gui.add(outLineDragMode, BorderLayout.PAGE_START); gui.setBorder(new EmptyBorder(2, 3, 2, 3)); gui.add(new JScrollPane(dt), BorderLayout.CENTER); JFrame f = new JFrame("DTP Preferred"); f.add(gui); // Ensures JVM closes after frame(s) closed and // all non-daemon threads are finished f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // See http://stackoverflow.com/a/7143398/418556 for demo. f.setLocationByPlatform(true); // ensures the frame is the minimum size it needs to be // in order display the components within it f.pack(); f.setMinimumSize(f.getSize()); // should be done last, to avoid flickering, moving, // resizing artifacts. f.setVisible(true); printProperty("os.name"); printProperty("java.version"); printProperty("java.vendor"); } }; // Swing GUIs should be created and updated on the EDT // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html SwingUtilities.invokeLater(r); } public static void printProperty(String name) { System.out.println(name + ": \t" + System.getProperty(name)); } } ``` Edit Amongst the information printed, see also the 3 system properties: ``` os.name: Windows 7 java.version: 1.7.0_21 java.vendor: Oracle Corporation ``` Those are the values here. `MouseMotionListener` fixed code Thanks to Jonathan Drapeau's suggestion of a `MouseListener`, this fixed example actually uses a `MouseMotionListener` to allow the desktop pane to be resized actively while dragging. It might suffer some quirks beyond use of a `MouseListener` that cause problems (none yet known), if so, go back to the simpler technique of 'resize desktop pane on internal frame drop' (`MouseListener` only). ``` import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.EmptyBorder; import javax.swing.plaf.basic.BasicInternalFrameTitlePane; public class MDIPreferredSize { public static void main(String[] args) { Runnable r = new Runnable() { @Override public void run() { final JDesktopPane dt = new JDesktopPane() { @Override public Dimension getPreferredSize() { Dimension prefSize = super.getPreferredSize(); System.out.println("prefSize: " + prefSize); // inititialize the max to the first normalized bounds Rectangle max = getAllFrames()[0].getNormalBounds(); for (JInternalFrame jif : this.getAllFrames()) { max.add(jif.getNormalBounds()); } System.out.println("maxBounds(): " + max); int x1 = max.width + (max.x * 2) < prefSize.width ? prefSize.width : max.width + (max.x * 2); int y1 = max.height + (max.y * 2) < prefSize.height ? prefSize.height : max.height + (max.y * 2); System.out.println("x,y: " + x1 + "," + y1); return new Dimension(x1, y1); } }; int xx = 5; int yy = 5; int vStep = 10; int yStep = 22; for (int ii = 0; ii < 3; ii++) { JInternalFrame jif = new JInternalFrame( "Internal Frame " + (ii + 1), true, true, true); dt.add(jif); jif.setLocation(xx, yy); xx += vStep; yy += yStep; jif.setSize(200, 75); jif.setVisible(true); } /*final MouseListener mouseListener = new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { dt.revalidate(); } }; */ final MouseMotionListener mouseMotionListener = new MouseMotionAdapter() { @Override public void mouseDragged(MouseEvent e) { dt.revalidate(); } }; for (JInternalFrame jif : dt.getAllFrames()) { for (Component comp : jif.getComponents()) { if (comp instanceof BasicInternalFrameTitlePane) { //comp.addMouseListener(mouseListener); comp.addMouseMotionListener(mouseMotionListener); } } } dt.setAutoscrolls(true); final JCheckBox outLineDragMode = new JCheckBox("Outline Drag Mode"); ActionListener dragModeListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (outLineDragMode.isSelected()) { dt.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE); } else { dt.setDragMode(JDesktopPane.LIVE_DRAG_MODE); } } }; outLineDragMode.addActionListener(dragModeListener); JPanel gui = new JPanel(new BorderLayout()); gui.add(outLineDragMode, BorderLayout.PAGE_START); gui.setBorder(new EmptyBorder(2, 3, 2, 3)); gui.add(new JScrollPane(dt), BorderLayout.CENTER); JFrame f = new JFrame("DTP Preferred"); f.add(gui); // Ensures JVM closes after frame(s) closed and // all non-daemon threads are finished f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // See http://stackoverflow.com/a/7143398/418556 for demo. f.setLocationByPlatform(true); // ensures the frame is the minimum size it needs to be // in order display the components within it f.pack(); f.setMinimumSize(f.getSize()); // should be done last, to avoid flickering, moving, // resizing artifacts. f.setVisible(true); printProperty("os.name"); printProperty("java.version"); printProperty("java.vendor"); } }; // Swing GUIs should be created and updated on the EDT // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html SwingUtilities.invokeLater(r); } public static void printProperty(String name) { System.out.println(name + ": \t" + System.getProperty(name)); } } ``` Quirks It might suffer some quirks beyond use of a `MouseListener` that cause problems (none yet known). That was then.. - In full render mode, the desktop pane will grow dynamically as far as the user drags the internal frame (even off the GUI). (Good.) In outline mode, the container will only resize on drop, not drag. (Less good, but at least the scroll-bars appear/disappear reliably.)