Spin Open a Detail Pane #39
Chapter 5, Windows, Dialogs, and Frames
|
203
HACK
The Invisible Man
This hack is fairly simple and relies on one fact: you can add a component to
a layout and alternately make it visible and invisible. Its position relative to
other components is preserved when it’s invisible, but it takes up no
onscreen space. So, a spin-open container consists of three components:
The top component, which is always visible
The spinner
The bottom component, whose visibility can be set by clicking on the
spinner
The layout of these three is pretty straightforward, as seen in Example 5-6,
which lists the
MoreInfoPanel class but omits an inner class (for now).
Example 5-6. Laying out the three panel components
public class MoreInfoPanel extends JPanel {
public Component topComponent;
protected SpinWidget spinWidget;
public Component bottomComponent;
public static final int SPIN_WIDGET_HEIGHT = 14;
public MoreInfoPanel (Component tc, Component mic) {
topComponent = tc;
spinWidget = new SpinWidget( );
bottomComponent = mic;
doMyLayout( );
}
protected void doMyLayout( ) {
setLayout (new BoxLayout (this, BoxLayout.Y_AXIS));
add (topComponent);
add (spinWidget);
add (bottomComponent);
resetBottomVisibility( );
}
protected void resetBottomVisibility( ) {
if ((bottomComponent == null) ||
(spinWidget == null))
return;
bottomComponent.setVisible (spinWidget.isOpen( ));
revalidate( );
if (isShowing( )) {
Container ancestor = getTopLevelAncestor( );
if ((ancestor != null) && (ancestor instanceof Window))
((Window) ancestor).pack( );
204
|
Chapter 5, Windows, Dialogs, and Frames
#39 Spin Open a Detail Pane
HACK
The constructor simply assigns the top and bottom Components to local vari-
ables, creates a
SpinWidget (see Example 5-7), and calls doMyLayout( ). The
latter method puts the components into a vertical
BoxLayout and calls the
convenience method
resetBottomVisibility( ).
resetBottomVisibility( ) is responsible for setting the visibility of the bot-
tom component based on whether the
SpinWidget is currently open or
closed. It then finds and re-
pack( )s its parent Window, so that if the bottom
component is wider than the top, the enclosing window will resize to fit the
now-visible contents. Of course, this behavior might not be appropriate in
some cases: you might put a
MoreInfoPanel in a complex GUI and not want
to re-
pack( ) the parent Window. Imagine an IDE in which you browse items
in a list or tree on the left, and show varying levels of detail on the right with
nested
MoreInfoPanels. The right pane might want to get sizing-related
events when its child
MoreInfoPanels change sizes, but it wouldn’t be appro-
priate for them to try to resize the entire
JFrame. This might call for hacking
the hack to set up an event-listener system, so that you could deliver sizing-
related events to a specific parent.
Now for the
SpinWidget, shown in Example 5-7. I wrote this as a custom
JPanel that draws the triangles with graphics primitives. This has the advan-
tage of being self-contained, but if you’d rather just use a
JButton with hand-
drawn triangle GIFs, go for it.
repaint( );
}
}
public void showBottom (boolean b) {
spinWidget.setOpen (b);
}
public boolean isBottomShowing ( ) {
return spinWidget.isOpen( );
}
// See below for SpinWidget inner class
}
Example 5-7. Inner class spin triangle
public class SpinWidget extends JPanel {
boolean open;
Dimension mySize = new Dimension (SPIN_WIDGET_HEIGHT,
SPIN_WIDGET_HEIGHT);
final int HALF_HEIGHT = SPIN_WIDGET_HEIGHT / 2;
int[] openXPoints =
{ 1, HALF_HEIGHT, SPIN_WIDGET_HEIGHT-1};
Example 5-6. Laying out the three panel components (continued)
Spin Open a Detail Pane #39
Chapter 5, Windows, Dialogs, and Frames
|
205
HACK
As you can see, the code does some work to create the points for the two tri-
angle polygons, which are used in the
paint( ) method. Other than that, it’s
a simple state machine with two
boolean states: when it gets a click, it
switches states. The call to
setOpen( ) calls the enclosing class’s
resetButtonVisibility( ), which will make the bottom component visible.
int[] openYPoints =
{ HALF_HEIGHT, SPIN_WIDGET_HEIGHT-1, HALF_HEIGHT};
int[] closedXPoints =
{ 1, 1, HALF_HEIGHT};
int[] closedYPoints =
{ 1, SPIN_WIDGET_HEIGHT-1, HALF_HEIGHT };
Polygon openTriangle =
new Polygon (openXPoints, openYPoints, 3);
Polygon closedTriangle =
new Polygon (closedXPoints, closedYPoints, 3);
public SpinWidget( ) {
setOpen (false);
addMouseListener (new MouseAdapter( ) {
public void mouseClicked (MouseEvent e) {
handleClick( );
}
});
}
public void handleClick( ) {
setOpen (! isOpen( ));
}
public boolean isOpen( ) {
return open;
}
public void setOpen (boolean o) {
open = o;
resetBottomVisibility( );
}
public Dimension getMinimumSize( ) { return mySize; }
public Dimension getPreferredSize( ) { return mySize; }
// don't override update( ), get the default clear
public void paint (Graphics g) {
if (isOpen( ))
g.fillPolygon (openTriangle);
else
g.fillPolygon (closedTriangle);
}
}
Example 5-7. Inner class spin triangle (continued)

Get Swing Hacks now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.