package org.limewire.ui.swing.painter;
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Paint;
import org.jdesktop.swingx.painter.AbstractPainter;
import org.jdesktop.swingx.util.PaintUtils;
import org.limewire.ui.swing.util.PainterUtils;
/**
* Paints a rounded border like box with one pixel inner
* shadowing/beveling. Used a the base painter of almost
* all components.
* <p>
*
* NOTE: This painter does NOT use resources for
* the colours defined by the accents. This
* is the most commonly used painter in the
* application and the idea was to keep it as
* simple as possible. If resources are desired
* the class must be refactored with a factory.
* This class MUST NOT import resources directly
* since it is used early in the startup cycle.
*/
public class BorderPainter<X> extends AbstractPainter<X> {
private final int arcWidth;
private final int arcHeight;
private final Paint border;
private final Paint bevelTop1;
private final Paint bevelTop2;
private final Paint bevelBottom;
private Paint bevelLeft;
private Paint bevelRight;
private int tabHeightCache = -1;
private final Paint accentPaint1;
private final Paint accentPaint2;
private final Paint accentPaint3;
private Insets insets = PainterUtils.BLANK_INSETS;
private final AccentType accentType;
// DO NOT CONVERT THESE TO RESOURCES IN THIS CLASS
private static final Paint BUBBLE_PAINT1 = new Color(0xeeeeee);
private static final Paint BUBBLE_PAINT2 = new Color(0xededed);
private static final Paint BUBBLE_PAINT3 = new Color(0xf0f0f0);
private static final Paint SHADOW_PAINT1 = new Color(0x5f5f5f);
private static final Paint SHADOW_PAINT2 = new Color(0x5e5e5e);
private static final Paint SHADOW_PAINT3 = new Color(0x646464);
private static final Paint GREEN_SHADOW_PAINT1 = new Color(0xc3d9a1);
private static final Paint GREEN_SHADOW_PAINT2 = new Color(0xb9d78d);
private static final Paint GREEN_SHADOW_PAINT3 = new Color(0xe1eecc);
public BorderPainter(int arcWidth, int arcHeight, Paint border,
Paint bevelLeft, Paint bevelTop1, Paint bevelTop2,
Paint bevelRight, Paint bevelBottom, AccentType accentType) {
this.arcWidth = arcWidth;
this.arcHeight = arcHeight;
this.border = border;
this.bevelLeft = bevelLeft;
this.bevelTop1 = bevelTop1;
this.bevelTop2 = bevelTop2;
this.bevelRight = bevelRight;
this.bevelBottom = bevelBottom;
this.accentType = accentType;
switch (accentType) {
case BUBBLE :
accentPaint1 = BUBBLE_PAINT1;
accentPaint2 = BUBBLE_PAINT2;
accentPaint3 = BUBBLE_PAINT3;
break;
case SHADOW :
accentPaint1 = SHADOW_PAINT1;
accentPaint2 = SHADOW_PAINT2;
accentPaint3 = SHADOW_PAINT3;
break;
case GREEN_SHADOW :
accentPaint1 = GREEN_SHADOW_PAINT1;
accentPaint2 = GREEN_SHADOW_PAINT2;
accentPaint3 = GREEN_SHADOW_PAINT3;
break;
default:
accentPaint1 = PainterUtils.TRANSPARENT;
accentPaint2 = PainterUtils.TRANSPARENT;
accentPaint3 = PainterUtils.TRANSPARENT;
}
this.setCacheable(true);
}
public int getArcHeight() {
return arcHeight;
}
public int getArcWidth() {
return arcWidth;
}
/**
* Allows the painting to be offset by certain values to
* remove rounding as desired on the sides. Sides that
* fall painted offscreen will be capped with the normal
* border.
* <p>
* NOTE: at the moment only horizonal insets are supported
* and capping will only work properly if the inset
* is larger than the arc size (ie. can not correctly cap
* partially flattened edges)
* <pre>
* Example: setInsets(0,-10,0,-10)
* - left side will be moved 10 pixels off the
* canvas and thus be cut off, left side will
* be capped
* - right side will be moved 10 pixels off the canvas
* and thus be cut off, right side will be capped
*
* setInsets(-10,0,10,0)
* - will have no effect at this time
* </pre>
*/
public void setInsets(Insets insets) {
this.insets = insets;
}
@Override
protected void doPaint(Graphics2D g, X object, int width, int height) {
int ix1 = insets.left;
int ix2 = insets.right;
int singleArcHeight = arcHeight/2;
// Draw upper bevels
g.setClip(0+ix1, 0, width-2-ix1-ix2, 7);
g.setPaint(bevelTop2);
g.drawRoundRect(1+ix1, 2, width-2-ix1-ix2, height-5, arcWidth, arcHeight);
g.setPaint(bevelTop1);
g.drawRoundRect(1+ix1, 1, width-3-ix1-ix2, height-4, arcWidth, arcHeight);
// Update gradients if height has changed
if (tabHeightCache != height) {
bevelLeft = PaintUtils.resizeGradient(bevelLeft, 0, height-singleArcHeight+1);
bevelRight = PaintUtils.resizeGradient(bevelRight, 0, height-singleArcHeight+1);
tabHeightCache = height;
}
// Draw side and bottom bevels
g.setClip(0+ix1, singleArcHeight, width-2-ix1-ix2, height);
g.setPaint(bevelBottom);
g.drawRoundRect(1+ix1, 1, width-4-ix1-ix2, height-4, arcWidth, arcHeight);
g.setClip(0+ix1, singleArcHeight-1, width-2-ix1-ix2, height);
g.setPaint(bevelLeft);
g.drawLine(2+ix1,singleArcHeight-1,2+ix1,height-singleArcHeight);
g.setPaint(bevelRight);
g.drawLine(width-3-ix2,singleArcHeight-1,width-3-ix2,height-singleArcHeight);
if (this.accentType != AccentType.NONE) {
// Draw the bottom accent bubble or shadow
g.setClip(0+ix1, singleArcHeight, width-ix1-ix2, height);
g.setPaint(accentPaint3);
g.drawRoundRect(0+ix1, 0, width-1-ix1-ix2, height-1, arcWidth, arcHeight);
g.setPaint(accentPaint2);
g.drawLine(0+ix1,singleArcHeight,0+ix1,height/2);
g.drawLine(width-1-ix2,singleArcHeight,width-1-ix2,height/2);
g.setPaint(accentPaint1);
g.drawLine(0+ix1,height/2,0+ix1,height-singleArcHeight);
g.drawLine(width-1-ix2,height/2,width-1-ix2,height-singleArcHeight);
}
g.setClip(0+ix1, 0, width-ix1-ix2, height);
// Draw final border
g.setPaint(PaintUtils.resizeGradient(border, 0, height));
g.drawRoundRect(1+ix1, 0, width-3-ix1-ix2, height-2, arcWidth, arcHeight);
// Cap the left border if it is not rounded on the left
if (ix1 < 0) {
g.drawLine(0, 1, 0, height-2);
}
// Cap the right border if it is not rounded on the right
if (ix2 < 0) {
GradientPaint spanGradient
= new GradientPaint(0,1, PainterUtils.getColour(bevelTop1),
0, height-3, PainterUtils.getColour(bevelBottom), false);
g.setPaint(spanGradient);
g.drawLine(width-1, 2, width-1, height-3);
}
}
/**
* Specifies which accent to draw around the bottom edge of a rounded border.
* Accents are used to add emphasis or depth to a component but at this time
* there are only the few preset types to choose in this enum.
*/
public enum AccentType {
/**
* Standard shadow -- a gray bottom outline -- to be used
* on dark coloured panels.
*/
SHADOW,
/**
* Similar to the standard shadow but with a green tinge
* that is used on green backgrounds.
*/
GREEN_SHADOW,
/**
* A kind of bubble that makes a button look like it is popping
* out of a panel. This bubble effect only looks good on
* lightly coloured panels.
*/
BUBBLE,
/**
* No accent, no shadow, no bubble, this works everywhere but
* looks a little bit boring. Used on panels with non standard
* colouring or buttons where no emphasis is needed.
*/
NONE
}
}