What would you say if you opened the Options dialog in your application and
it looked like this (click to see larger version):
I've blogged
about adding watermark image before, but pay attention to the buttons. They are shaped
like little rhinos. Don't like rhinos (or Beyonce)? No biggie, just change a few VM flags
and here you go (click for larger view):
And now to the reason why would you want to do such a thing. It didn't appear to me
until after i have started to think about pluggable button shapers that i
realized a whole market that Java is missing - non-male non-geek non-blind-Dvorak-typing
one. What about applications that are mainly targetting female / child audience. Wouldn't
it be nice to make an application that has dolphin-shaped buttons when the application
is a marine encyclopedia?
Swing already has support for different button shapes - you need to implement your
own ButtonUI delegate and override a couple of functions (notably the one
that paints the background, the one that paints the border and the one that tests
the mouse hit). So, the button
shaper plugin for Substance look-and-feel provides exactly that - a
collection of button shapers. The currently available shapers are:
The implementation is quite simple - each button shaper has an associated contour
(implemented by GeneralPath). When a button needs to be drawn, the contour
is stretched to accomodate the text. Note that the contour maintains the original
proportion (unless the application explicitly sets the dimension, like in NetBeans
first screenshot). In addition, each button has a shine spot that follows the outline
of the button itself (more on this later):
The button contour is created, edited and saved in the shape editor
(click
here to launch Web Start version). Working with editor (on new shape) involves the
following steps:
int shineHeight = (int) (height / 1.8); int kernelSize = (int) Math.min(12, Math.pow(Math .min(width, height), 0.8) / 4); if (kernelSize == 0) kernelSize = 1; BufferedImage ghostContour = getBlankImage(width + 2 * kernelSize, height + 2 * kernelSize); Graphics2D ghostGraphics = (Graphics2D) ghostContour.getGraphics() .create(); ghostGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); ghostGraphics.setStroke(new BasicStroke(2 * kernelSize)); ghostGraphics.setColor(Color.black); ghostGraphics.translate(kernelSize, kernelSize); ghostGraphics.draw(contour);The result is:
int kernelMatrixSize = (2 * kernelSize + 1) * (2 * kernelSize + 1); float[] kernelData = new float[kernelMatrixSize]; for (int i = 0; i < kernelMatrixSize; i++) kernelData[i] = 1.0f / kernelMatrixSize; Kernel kernel = new Kernel(2 * kernelSize, 2 * kernelSize, kernelData); ConvolveOp convolve = new ConvolveOp(kernel); BufferedImage blurredGhostContour = getBlankImage(width + 2 * kernelSize, height + 2 * kernelSize); convolve.filter(ghostContour, blurredGhostContour); Graphics2D blurredGraphics = (Graphics2D) blurredGhostContour .getGraphics();The result is:
BufferedImage reverseGhostContour = getBlankImage(width + 2 * kernelSize, height + 2 * kernelSize); Graphics2D reverseGraphics = (Graphics2D) reverseGhostContour .getGraphics(); Color bottomShineColorTransp = new Color(bottomShineColor.getRed(), bottomShineColor.getGreen(), bottomShineColor.getBlue(), 32); GradientPaint gradientShine = new GradientPaint(0, kernelSize, topShineColor, 0, kernelSize + shineHeight, bottomShineColorTransp); reverseGraphics.setPaint(gradientShine); reverseGraphics.fillRect(0, kernelSize, width + 2 * kernelSize, kernelSize + shineHeight); reverseGraphics.setComposite(AlphaComposite.DstOut); reverseGraphics.drawImage(blurredGhostContour, 0, 0, null); graphics.drawImage(reverseGhostContour, 0, 0, width - 1, shineHeight, kernelSize, kernelSize, kernelSize + width - 1, kernelSize + shineHeight, null);The result is:
BufferedImage overGhostContour = getBlankImage(width + 2 * kernelSize, height + 2 * kernelSize); Graphics2D overGraphics = (Graphics2D) overGhostContour .getGraphics(); overGraphics.setPaint(new GradientPaint(0, kernelSize, topFillColor, 0, kernelSize + height / 2, midFillColor)); overGraphics.fillRect(kernelSize, kernelSize, kernelSize + width, kernelSize + shineHeight); overGraphics.setComposite(AlphaComposite.DstIn); overGraphics.drawImage(blurredGhostContour, 0, 0, null); graphics.drawImage(overGhostContour, 0, 0, width - 1, shineHeight, kernelSize, kernelSize, kernelSize + width - 1, kernelSize + shineHeight, null);The result is: