 |
April 2007 Archives
I Must Be Dim
Posted by invalidname on April 13, 2007 at 07:19 PM | Permalink
| Comments (6)
So, a day after I finished my AB5k desklet, Josh goes and totally breaks it with a wildly ambitious new version of the desklet container, which "virtualizes" all the components. In other words, all the components really live off-screen in memory and blit their pixels into buffers that Josh can play with, render on a 3D surface, whatever.
Among the breakage is an animated dimming effect I was using. It doesn't seem like desklets should be able to spawn their own windows or dialogs, so for open and save dialogs, and some HTML "about" text, what I decided to do was to use a glass-pane hack: I'd "dim" the glasspane to darken the desklet, then insert the JFileChooser or JScrollPane, with large insets, in the center of the glass-pane. By "darken", I mean that I'd set its background to a black color with a non-trivial alpha value (ie, a translucent black), and fill the pane. I posted a screenshot of it to the Desklet Contributors group back when it worked.
But like I said, the virtualized component scheme broke this, because it depended on my component climbing the heirarchy to find a RootPaneContainer, which would provide access to the glass-pane. In the virtualized world, the components don't live in real frames or root-pane containers anymore, so this doesn't work.
OK, plan B: do my own JLayeredPane. The contents of a RootPaneContainer are just a big JLayeredPane so this should work, right?
Well, um, no. Not like you'd think it would. It seems to break in really interesting ways. Specifically, if the "dim box" -- the component that fills the layer above the background -- is less than the size of the JLayeredPane in both dimensions, it works as before. On the other hand, if it equals the container's size exactly, or equals or exceeds in both dimensions, then the dim box loses its translucence.
Weird? Let's play. Here's an applet version. You turn on the dim layer with the checkbox, then set its size with the text areas. You'll need to turn it on and set non-zero dimensions in order to see anything. You can also download dimlayertest.jar and run it as an application with java -classpath dimlayertest.jar DimLayerTest.
So, here are a few screenshots, in case you'd rather not play with the applet. First, a 200x200 dim box, just to show it works:

Next, I max out the horizontal dimension, but stay one pixel back from maxing out the vertical.

And finally, a dim box that is exactly the dimensions of the container:

Oops! So much for that approach. Actually, I'm probably screwed anyways, for two reasons. In the desklet, the "too big" dimensions seem to be a good 5 or 6 pixels in, which leaves a weird un-dimmed area. Second, it seems the translucence may not be cross-platform. Here's what happened when I tried it in Windows XP on Virtual PC (my Boot Camp seems hosed at the moment):

Yuck! Ptui! So much for alpha channels, Duke!
Anyways, time to punt. It was a neat idea, but it doesn't work, I've spent too much time on it, and Josh and Cooper now have a simple "get me a dialog" approach that will suffice for the file chooser and the about HTML. Enough of this wild layer chase...
Oh, for the record, here's source: it's only 150 lines, so it shouldn't be too painful to just in-line:
import java.awt.*;
import java.awt.event.*;
import javax.swing.event.*;
import javax.swing.*;
import java.applet.*;
public class DimLayerTest extends Applet {
JLayeredPane layeredPane;
JComponent dimBox;
JLabel iconLabel;
JTextField widthField, heightField;
JCheckBox showDimBoxCheckBox;
public static void main (String[] arrrImAPirate) {
JFrame f = new JFrame ("Dim Layer Test");
f.getContentPane().add (new DimLayerTest());
f.pack();
f.setVisible(true);
}
public DimLayerTest () {
doMyLayout();
}
private void doMyLayout() {
setLayout (new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
// controls for the dim pane
gbc.gridx=0;
gbc.gridy=0;
gbc.gridwidth=1;
gbc.gridheight=1;
gbc.weightx=1;
gbc.weighty=1;
gbc.anchor=GridBagConstraints.NORTH;
gbc.fill=GridBagConstraints.BOTH;
add (new JLabel ("Dim width:"), gbc);
widthField = new JTextField ("0", 5);
gbc.gridx=1;
add (widthField, gbc);
gbc.gridx=2;
add (new JLabel ("Dim height:"), gbc);
heightField = new JTextField ("0", 5 );
gbc.gridx=3;
add (heightField, gbc);
gbc.gridx=1;
gbc.gridy=1;
gbc.gridwidth=4;
showDimBoxCheckBox = new JCheckBox ("Show Dim Layer", false);
add (showDimBoxCheckBox, gbc);
// our layers
java.net.URL imageURL =
getClass().getClassLoader().getResource
("resources/Duke_library_javax_swing.gif");
iconLabel = new JLabel (new ImageIcon (imageURL));
dimBox = new JPanel();
dimBox.setBackground (new Color (0, 0, 0, 128));
layeredPane = new JLayeredPane();
layeredPane.setPreferredSize (iconLabel.getPreferredSize());
layeredPane.add (iconLabel, JLayeredPane.DEFAULT_LAYER);
iconLabel.setBounds (0, 0,
iconLabel.getPreferredSize().width,
iconLabel.getPreferredSize().height);
layeredPane.add (dimBox, JLayeredPane.MODAL_LAYER);
dimBox.setOpaque (true);
dimBox.setBounds (0, 0, 0, 0);
dimBox.setVisible (false);
gbc.gridx=0;
gbc.gridy=10;
gbc.gridwidth=4;
gbc.gridheight=1;
gbc.weightx=1;
gbc.weighty=1;
gbc.anchor=GridBagConstraints.NORTH;
gbc.fill=GridBagConstraints.BOTH;
add (layeredPane, gbc);
// bottom row shows image size... also a row that stretches
// to handle extra applet space
gbc.gridy=20;
gbc.anchor=GridBagConstraints.NORTHWEST;
add (new JLabel ("Image is " +
iconLabel.getSize().width + " x " +
iconLabel.getSize().height),
gbc);
gbc.gridy=100;
gbc.weighty=1000000;
add (Box.createVerticalGlue(), gbc);
resetDimBox();
// wire up listeners to reset dim box any time text is typed
// into fields or button is clicked
showDimBoxCheckBox.addActionListener (new ActionListener() {
public void actionPerformed (ActionEvent e) {
resetDimBox();
}
});
DocumentListener textFieldListener = new DocumentListener() {
public void insertUpdate (DocumentEvent e) {
resetDimBox();
}
public void removeUpdate (DocumentEvent e) {
resetDimBox();
}
public void changedUpdate (DocumentEvent e) {
resetDimBox();
}
};
widthField.getDocument().addDocumentListener (textFieldListener);
heightField.getDocument().addDocumentListener (textFieldListener);
}
// get settings from control widgets and reset bounds of dimBox
private void resetDimBox() {
int oldDimWidth = dimBox.getBounds().width;
int oldDimHeight = dimBox.getBounds().height;
int newDimWidth = -1;
int newDimHeight = -1;
try {
newDimWidth = Integer.parseInt(widthField.getText());
} catch (Exception e) {} // ignore garbage
try {
newDimHeight = Integer.parseInt(heightField.getText());
} catch (Exception e) {} // ignore garbage
boolean newDimVisible = showDimBoxCheckBox.isSelected();
dimBox.setVisible (newDimVisible);
dimBox.setBounds (0, 0,
((newDimWidth > 0) ? newDimWidth : oldDimWidth),
((newDimHeight > 0) ? newDimHeight : oldDimHeight));
repaint();
}
}
|