I'm writing a drop down component with colors for toolbar. So I've taken ideas from 'Swing hacks' book, changed conception a little bit and added Swing's standard JColorChooser to drop down. The behaviour shold be following: I click a button and a window with color chooser appears; I pick a color and the drop down window closes, and text of the button chages color to the picked one. In whole everything works, but there is one unpleasant bug. After these operations UI freezes and the button even doesn't accept mouse events like 'mouse over'. And this happens until I click. Then the UI behaves as wanted.
Here is the code with conception.
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.AbstractButton;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.colorchooser.AbstractColorChooserPanel;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.metal.MetalComboBoxIcon;
class DropDownComponent2 {
private JWindow _window;
private boolean _windowShouldBeShown = false;
private JComponent _component;
private AbstractButton _button;
private JFrame _ownerFrame;
public DropDownComponent2(JFrame ownerFrame, JComponent component, AbstractButton button) {
_ownerFrame = ownerFrame;
_component = component;
_button = button;
_button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
_window.setVisible(false);
Point pt = _button.getLocationOnScreen();
pt.translate(0, _button.getHeight());
_window.setLocation(pt);
showWindow();
_windowShouldBeShown = true;
}
});
_button.addAncestorListener(new AncestorListener() {
public void ancestorAdded(AncestorEvent event){
_window.setVisible(false);
}
public void ancestorRemoved(AncestorEvent event){
_window.setVisible(false);
}
public void ancestorMoved(AncestorEvent event){
if (event.getSource() != _window) {
System.out.println("Ansestor moved");
_window.setVisible(false);
}
}
});
Toolkit.getDefaultToolkit().addAWTEventListener(
new AWTEventListener() {
public void eventDispatched(AWTEvent event) {
if (event.getID() == MouseEvent.MOUSE_CLICKED) {
if ( !_window.getBounds().contains( MouseInfo.getPointerInfo().getLocation() )) {
if (_windowShouldBeShown)
_windowShouldBeShown = false;
else {
_window.setVisible(false);
}
}
}
}
}, AWTEvent.MOUSE_EVENT_MASK);
_window = new JWindow(_ownerFrame);
_window.getContentPane().add(component);
_window.addWindowFocusListener(new WindowAdapter() {
public void windowLostFocus(WindowEvent evt) {
System.out.println("window lost focus");
_window.setVisible(false);
}
});
_window.pack();
}
private Rectangle getScreenRect() {
return new Rectangle(java.awt.Toolkit.getDefaultToolkit().getScreenSize());
}
public void showWindow() {
Rectangle screenRect = getScreenRect();
Rectangle windowRect = _window.getBounds();
int sx1 = screenRect.x;
int sx2 = screenRect.x + screenRect.width;
int sy1 = screenRect.y;
int sy2 = screenRect.y + screenRect.height;
int wx1 = windowRect.x;
int wx2 = windowRect.x + windowRect.width;
int wy1 = windowRect.y;
int wy2 = windowRect.y + windowRect.height;
if (wx2 > sx2) {
_window.setLocation(wx1-(wx2-sx2), _window.getY());
}
if (wx1 < sx1) {
_window.setLocation(0, _window.getY());
}
if (wy2 > sy2) {
_window.setLocation(_window.getX(), wy1-(wy2-wy1));
}
if (wy2 < sy1) {
_window.setLocation(_window.getX(), 0);
}
_window.setVisible(true);
}
public void hideWindow() {
_window.setVisible(false);
}
}
public class DropDownFrame extends JFrame {
JButton _button;
JColorChooser _colorChooser;
DropDownComponent2 _dropDown;
JWindow _window;
public DropDownFrame() {
_colorChooser = new JColorChooser();
_colorChooser.setPreviewPanel(new JPanel());
_colorChooser.setColor(Color.RED);
// Remove panels other than Swatches
AbstractColorChooserPanel[] panels = _colorChooser.getChooserPanels();
for (int i=0; i<panels.length; i++) {
if (!panels[i].getDisplayName().equals("Swatches"))
_colorChooser.removeChooserPanel(panels[i]);
}
_colorChooser.getSelectionModel().addChangeListener(new ChangeListener() {
// ### I think the key point is there
@Override
public void stateChanged(ChangeEvent e) {
_dropDown.hideWindow();
_button.setForeground(_colorChooser.getColor());
}
});
_button = new JButton("Show JWindow");
_button.setIcon(new MetalComboBoxIcon());
_button.setHorizontalTextPosition(SwingConstants.LEFT);
this.getContentPane().add(_button);
_dropDown = new DropDownComponent2(DropDownFrame.this, _colorChooser, _button);
pack();
setVisible(true);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new DropDownFrame();
}
});
}
}
I'm sure there is something with JColorChooser and selection model. But I can't catch the idea. I tried requestFocus() and requestFocusInWindow(). No success. I tried to use JDialog instead of JWindow. When I press [x] on dialog, everything is as wanted, but when I pick color the UI also freezes!
Another point! If I use a label inside drop down window instead of color chooser and handle a click on the label, everything works fine: window closes, and no freezing!
I was placing _dropDown.hideWindow() inside SwingUtilities.invokeLater(). And without success.
What am I missing?
_window.dispose();
instead of_window.setVisible(false);
insidehideWindow()
– Furious