The Source for Java Technology Collaboration
User: Password:



Kirill Grouchnikov

Kirill Grouchnikov's Blog

Spring effects on buttons - now at your nearest look and feel

Posted by kirillcool on March 15, 2007 at 12:07 AM | Comments (8)

This is the third part in series about ghosting image (aka spring) effects on Swing buttons.

  • The first part introduced the "ghosting image" effects (aka "spring" in Romain presentations). When you rollover the mouse over a button, its icon has an outward decaying "ghost" image painted (starts from 50% opacity and decays linearly to zero).
  • The second part incorporated feedback from the readers, adding "spilling" the effects outside the button borders and adding the press animation.

Since then (last November) the implementation has been improved to provide the following:

  • The spilling effect is painted globally across the entire frame, including panels, toolbars, menubars, tabbed panes, desktop panes and title panes. It can also be easily extended to custom controls such as task pane container and status bar from SwingX.
  • The effects are available on JButtons and JToggleButton. They can also be easily extended to custom controls that have icons.

Last, but most certainly not the least, the implementation itself is no longer tied to Substance. It has been moved to the laf-widget project and has been successfully integrated with six other third-party look and feels. There are two ways to "inject" this functionality into the look and feel delegates (as promised in my presentation at Desktop Matters):

  • Java classes with main() methods that change existing binary UI delegates.
  • Ant tasks that change existing binary UI delegates.

The batch scripts in this CVS folder (look for augment-*.bat) show how an existing third-party LAF can be augmented with ghosting image effects. There are two separate classes, one injecting the functionality in icon-painting method in a specific UI delegate, and another injecting the spilling functionality in the update() method in a specific UI delegate.

The Ant tasks allow injecting this functionality at build time. Here is the relevant snippet from Substance build script:



   <taskdef name="icon-ghosting-augment" 
         classname="org.jvnet.lafwidget.ant.AugmentIconGhostingTask" 
         classpath="${substance.lib.dir}/laf-widget.jar;${substance.lib.dir}/asm-all-2.2.2.jar" />
   <taskdef name="container-ghosting-augment" 
         classname="org.jvnet.lafwidget.ant.AugmentContainerGhostingTask" 
         classpath="${substance.lib.dir}/laf-widget.jar;${substance.lib.dir}/asm-all-2.2.2.jar" />

      <!-- Icon ghosting augmentation -->
      <icon-ghosting-augment verbose="true">
         <classpathset dir="${substance.output.dir}" />
         <iconghosting className="org.jvnet.substance.SubstanceButtonUI" methodName="paintIcon" />
         <iconghosting className="org.jvnet.substance.SubstanceToggleButtonUI" methodName="paintIcon" />
      </icon-ghosting-augment>

      <!-- Container ghosting augmentation -->
      <container-ghosting-augment verbose="true">
         <classpathset dir="${substance.output.dir}" />
         <containerghosting className="org.jvnet.substance.SubstanceDesktopPaneUI" toInjectAfterOriginal="true" />
         <containerghosting className="org.jvnet.substance.SubstanceMenuBarUI" toInjectAfterOriginal="true" />
         <containerghosting className="org.jvnet.substance.SubstanceMenuUI" toInjectAfterOriginal="true" />
         <containerghosting className="org.jvnet.substance.SubstancePanelUI" toInjectAfterOriginal="true" />
         <containerghosting className="org.jvnet.substance.SubstanceScrollBarUI" toInjectAfterOriginal="true" />
         <containerghosting className="org.jvnet.substance.SubstanceToolBarUI" toInjectAfterOriginal="true" />
      </container-ghosting-augment>

And now for some demonstrations. The following short videos show icon ghosting (rollover) and press ghosting effects on the same application under different look and feels. Note that for demo purposes i use larger icons and high-contrast implementation (high initial values for the fade out).

Here is how it looks under the InfoNode:

Here is how it looks under the Liquid:

Here is how it looks under the Looks PlasticXP:

Here is how it looks under the Napkin:

Here is how it looks under the Pagosoft:

Here is how it looks under the Squareness:

This technique will be presented at this year JavaOne conference at the "Bringing Life to Swing Applications" technical session that i have the honor to present with Alex Potochkin. Stay tuned for more and don't hesitate to leave comments.


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • I'm guessing that it's not possible to plug this into the platform's system look-and-feel without doing some extra work?

    -Chris

    Posted by: chris_e_brown on March 15, 2007 at 12:20 AM

  • This augmentation technique is very cool! Congratulation!

    Posted by: gfx on March 15, 2007 at 03:53 AM

  • Chris - indeed some extra work needs to be done, like with the mentioned third-party LAFs. The bytecode manipulation doesn't depend on the "type" of UI delegates it works on, so it is theoretically possible to take the compiled Metal / Windows LAF classes and run them through the matching batch scripts, create a jar with these classes and instruct VM to load this jar and take precedence over the rt.jar classes. May be one day i'll get to do this, but it would be a lower priority for me. The main purpose of this article is to show that this approach is not specific to Substance and can be extended to any look and feel that follows the general API contract.
    Romain - thanks. It has its own disadvantages though (because it operates on bytecode level) - no refactoring, poor readability, very hard to debug (line numbers no longer match the bytecode), very easy to mess up and end with an invalid bytecode (same method defined twice, parameters don't match), very easy to end up with what you didn't intend (since @Override is not available at bytecode level, if you create a method that gets an AbstractButton instead of a JToggleButton, it'll never get called) and more.

    Posted by: kirillcool on March 15, 2007 at 01:51 PM

  • Kirill: I am aware of those problems/limitations for having experienced them first hand in the past. That said, it's a lovely hack. And I love hacks :))

    Posted by: gfx on March 15, 2007 at 04:15 PM

  • Nice work kirill!

    I like how you are implementing these effects at the LAF level ... I'm wondering what you think about enabling/disabling some of these on a per component basis. I'm guessing this effect for example is enabled for all buttons in an application, what about cases were you want to just enable this more selectively?

    Posted by: augusto on March 15, 2007 at 07:50 PM

  • augusto - the fade effects can be configured by the FadeConfigurationManager API. You can enable / disable an effect globally, for all components of certain class or for a specific component. These two are disabled by default.

    Posted by: kirillcool on March 15, 2007 at 08:08 PM

  • Would it be possible to implement this with SwingX-style painters perhaps?
    - Chris

    Posted by: chris_e_brown on March 16, 2007 at 01:31 AM

  • Chris - you're tailoring the problem to the solution. This specific effect paint a portion of button icon or a portion of a button on the specific container. What does this have to do with painters? See the implementation for more details, look for icon.paintIcon() and comp.paint().

    Posted by: kirillcool on March 16, 2007 at 09:08 AM





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds