| Overview |
| Outlines |
| Colors |
| Source Code |
|

One of the more striking features inherent to Eclipse that contributes to its professional,
polished look are the drop-shadowed borders placed around each of the split-pane components. This gives off a slight three-dimensional look that, unlike other
heavy compound borders, doesn't detract from the overall appearance of the application. Instead, it gives off the impression of each docked view as floating slightly
above the default plane within the Z-axis (with a 3-pixel X and Y offset).
Borders are one of the easier custom pieces to create in Swing and so, while one of the more novel visual effects we'll create, this is also one
of the easiest. The first thing we want to do is take a screenshot and enlarge the image, as shown in this example, so we're able to get a good look at how the
effet is accomplished. What we find here is effectively a 3-pixel gradient between the background color of the underlying component and some darker color. So
we can tell immediately that we'll essentially be writing a class that is going to draw a series of lines in three different colors.
What we'll do here is break things down into two stages; drawing the border lines and choosing our colors.

The rectangle to the

left appears to have been raised up from the underlying background. In reality, there is no component within the shadowed area. This
effect is achived merely by drawing four lines and an rectangle. This drawing pattern is enlarged and shown over to the right, with
whitespace between each line to show them as separate components. In the actual implementation, each of these lines is adjacent.
The code for this is straightforward enough. The paintBorder() method on the Border interface provides us with everything
we need, including the Component we're going to be outlining, a Graphics instance, and the x, y, width, and height of our border. Obviously, by observing the
provided image, we know that the rectangle will have dimensions width-3 and height-3. We know that the inner two lines will have offsets of
x-2 and y-2, and the outer lines will have offsets of x-1 and y-1. This is everything we need to know to draw our outline.
// draw the rectangle
g.setColor(rect);
g.drawRect(x, y, width-3, height-3);
// draw the inner lines
g.setColor(mid);
g.drawLine(x+1, height-2, width-2, height-2);
g.drawLine(width-2, y+1, width-2, height-2);
// draw the outer lines
g.setColor(edge);
g.drawLine(x+2, height-1, width-2, height-1);
g.drawLine(width-1, y+2, width-1, height-2);
The three colors used to draw the shadow shouldn't necessarily be stock-gray as, depending on the color of the component
background, this may or may not look natural. The shadow colors should be a function of the current background color (the background of the
component's parent). On a positive note, for at least 2 of those colors the work is already done for us. The Color class has both brighter() and
darker methods. These methods apply a simple scale factor to the RGB components which are inverses of one another (though not
pure inverses, due to rounding. See the JavaDoc for details.). So, for the rectangle and inner lines, we merely take the component
background color and call darker().
Color bg = c.getBackground();
if(c.getParent()!=null)
bg = c.getParent().getBackground();
Color mid = bg.darker();
Color rect = mid.darker();
The third color here will be a function of the middle and background colors. We can't use a full brighter() or
darker() step here, as we would merely end up with either the background or mid-color as a result. We want to take a half-step
in between the two, which is going to give us a more natural look anyway as the intensity of a shadow around its edges falls off
logarithmically.
To accomplish this, we'll simply use the midpoint for each RGB component between the mid and background colors. This
average will give us a nice transition for a realistic looking shadow.
int red = mid.getRed() + (bg.getRed() - mid.getRed()) / 2;
int green = mid.getGreen() + (bg.getGreen() - mid.getGreen()) / 2;
int blue = mid.getBlue() + (bg.getBlue() - mid.getBlue()) / 2;
Color edge = new Color(red, green, blue);
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Insets;
import javax.swing.border.Border;
public class ShadowedBorder implements Border {
private Insets insets;
public ShadowedBorder() {
insets = new Insets(1, 1, 3, 3);
}
public Insets getBorderInsets(Component c) {
return insets;
}
public boolean isBorderOpaque() {
// we'll be filling in our own background.
return true;
}
public void paintBorder(Component c, Graphics g, int x,
int y, int w, int h) {
// choose which colors we want to use
Color bg = c.getBackground();
if(c.getParent()!=null)
bg = c.getParent().getBackground();
Color mid = bg.darker();
Color rect = mid.darker();
Color edge = average(mid, bg);
// fill in the corners with the parent-background
// so it looks see-through
g.setColor(bg);
g.fillRect(0, h-3, 3, 3);
g.fillRect(w-3, 0, 3, 3);
g.fillRect(w-3, h-3, 3, 3);
// draw the outline
g.setColor(rect);
g.drawRect(0, 0, w - 3, h - 3);
// draw the drop-shadow
g.setColor(mid);
g.drawLine(1, h - 2, w - 2, h - 2);
g.drawLine(w - 2, 1, w - 2, h - 2);
g.setColor(edge);
g.drawLine(2, h - 1, w - 2, h - 1);
g.drawLine(w - 1, 2, w - 1, h - 2);
}
private static Color average(Color c1, Color c2) {
int red = c1.getRed() + (c2.getRed() - c1.getRed()) / 2;
int green = c1.getGreen() + (c2.getGreen() - c1.getGreen()) / 2;
int blue = c1.getBlue() + (c2.getBlue() - c1.getBlue()) / 2;
return new Color(red, green, blue);
}
}
|