Changing the size of a JComboBox

Greetings,

I am trying to build a mechanism to perform zooming on a JPanel with an arbitrary collection of components (including nested JPanels). I've tried a number of things with little success. The following is my most promising contraption. It can zoom labels, textfields, checkboxes, and buttons. However, for some reason, the combo box refuses to accept a changes to its size. I'm not sure why this is the case. Its font changes size appropriately.

Anyway, I'm running in JDK 1.4, and the following program lays out a palette of components. To change the size of the components, hit F1 to zoom in, and F2 to zoom out.

import java.awt.Component;

import java.awt.Container;

import java.awt.Dimension;

import java.awt.Font;

import java.awt.KeyEventPostProcessor;

import java.awt.KeyboardFocusManager;

import java.awt.event.KeyEvent;

import java.awt.geom.AffineTransform;

import javax.swing.JButton;

import javax.swing.JCheckBox;

import javax.swing.JComboBox;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JPanel;

import javax.swing.JTextField;

publicclass Demoextends JPanel

{

publicclass Zoomer

{

privatedouble m_zoom = 1;

private JPanel m_panel;

public Zoomer(JPanel panel)

{

m_panel = panel;

}

private AffineTransform getTransform()

{

return AffineTransform.getScaleInstance(m_zoom, m_zoom);

}

private Font transform(Font font)

{

return font.deriveFont(getTransform());

}

private Dimension transform(Dimension dimension)

{

Dimension retval =new Dimension();

retval.setSize(dimension.getWidth() * m_zoom, dimension.getHeight() * m_zoom);

return retval;

}

privatevoid performZoom(Container container)

{

Component[] components = container.getComponents();

for(int i = 0; i < components.length; i++)

{

Component component = (Component)components[i];

component.setFont(transform(component.getFont()));

component.setSize(transform(component.getSize()));

}

for(int i = 0; i < components.length; i++)

{

Component component = components[i];

if(componentinstanceof Container)

{

performZoom((Container)component);

}

}

}

publicdouble getZoom()

{

return m_zoom;

}

publicvoid setZoom(double zoom)

{

if(zoom > 8.0 || zoom < 0.125)return;

m_zoom = zoom;

performZoom(m_panel);

}

publicvoid zoom(double factor)

{

setZoom(getZoom() * factor);

}

}

public Demo()

{

JPanel panel =new JPanel();

panel.add(buildPanel());

panel.add(buildPanel());

final Zoomer zoomer =new Zoomer(panel);

add(panel);

KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor(new KeyEventPostProcessor()

{

publicboolean postProcessKeyEvent(KeyEvent e)

{

if(e.getID() != KeyEvent.KEY_PRESSED)returnfalse;

if(e.getKeyCode() == KeyEvent.VK_F1)

{

zoomer.zoom(1.2);

}

if(e.getKeyCode() == KeyEvent.VK_F2)

{

zoomer.zoom(1/1.2);

}

returnfalse;

}

});

}

private JPanel buildPanel()

{

JPanel panel =new JPanel();

panel.add(new JLabel("label: "));

panel.add(new JTextField("Hello World"));

panel.add(new JCheckBox("checkbox"));

panel.add(new JComboBox(new String[]{"Bread","Milk","Butter"}));

panel.add(new JButton("Hit Me!"));

return panel;

}

/**

* Create the GUI and show it. For thread safety, this method should be

* invoked from the event-dispatching thread.

*/

privatestaticvoid createAndShowGUI()

{

// Create and set up the window.

JFrame frame =new JFrame("Demo");

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

// Create and set up the content pane.

Demo newContentPane =new Demo();

newContentPane.setOpaque(true);// content panes must be opaque

frame.setContentPane(newContentPane);

// Display the window.

frame.pack();

frame.setVisible(true);

}

publicstaticvoid main(String[] args)

{

// Schedule a job for the event-dispatching thread:

// creating and showing this application's GUI.

javax.swing.SwingUtilities.invokeLater(new Runnable()

{

publicvoid run()

{

createAndShowGUI();

}

});

}

}

[9190 byte] By [pspierena] at [2007-10-3 11:50:14]
# 1

> component.setSize(transform(component.getSize()));

First of all the above line is not needed. The LayoutManager will determine the bounds (size and location) of each component in the container based on the rules of the LayoutManager. The Flow Layout is the default layout manager for a panel and it simply uses the preferred size of the component as the size of the component.

So what happens is that when you change the font of the component you are changing the preferred size of the component.

So why doesn't the combo box work? Well I took a look at the preferred size calculation of the combo box (from the BasicComboBoxUI) and it actually caches the preferred size. The combo box uses a renderer, so the value is cached for performance one would assume. The method does recalculate the size when certain properties change. Note the isDisplaySizeDirty flag used in the code below:

else if (propertyName.equals("prototypeDisplayValue")) {

isMinimumSizeDirty = true;

isDisplaySizeDirty = true;

comboBox.revalidate();

}

else if (propertyName.equals("renderer")) {

isMinimumSizeDirty = true;

isDisplaySizeDirty = true;

comboBox.revalidate();

}

It also handles a Font property change as well:

else if ( propertyName.equals( "font" ) ) {

listBox.setFont( comboBox.getFont() );

if ( editor != null ) {

editor.setFont( comboBox.getFont() );

}

isMinimumSizeDirty = true;

comboBox.validate();

}

but notice that the isDisplaySizeDirty flag is missing. This would seem to be a bug (but I don't know why two flags are required).

Anyway, the following change to your code seems to work:

// component.setSize(transform(component.getSize()));

if (component instanceof JComponent)

{

((JComponent)component).updateUI();

}

camickra at 2007-7-15 14:23:57 > top of Java-index,Desktop,Core GUI APIs...