How to change existing LAF

Manual changes

A look-and-feel that wishes to use this project, needs to change the relevant UI delegate classes to integrate with LafWidgetRepository. This can be done either manually by changing the code or automatically by using supplied Ant task or batch files.

In order to get an instance of LafWidgetRepository class, the following static function is used:

  /**
   * Returns the widget repository.
   
   @return Widget repository.
   */
  public static synchronized LafWidgetRepository getRepository()


The UI delegate class needs to call the following function in its installUI method:

  /**
   * Returns a set of widgets that match the specified component. The
   * component hierarchy is scanned bottom-up and all matching widget classes
   * are used to instantiate new instance of widgets. In case the
   {@link #isCustomLafSupportSet} is <code>false</code>, only widgets
   * that return <code>false</code> in
   {@link LafWidget#requiresCustomLafSupport()} are returned.
   
   
   @param jcomp
   *            UI component.
   @return Set of widgets that match the specified component.
   */
  public synchronized Set getMatchingWidgets(JComponent jcomp)


The result Set contains instances of LafWidget interface. Each instance implements some widget (behavioural trait). This interface defines the lifecycle of the widget (and the associated component). The relevant functions mirror the usual approach of Swing UI delegates, four function for install phase and four functions for uninstall phase (see below). These functions should be called from the main UI delegate in order to install / uninstall the relevant listeners, components and default settings for each widget. Two additional functions in this interface set the associated component and check whether the widget requires custom LAF support (explained later):

public interface LafWidget {
  /**
   * Associates a component with <code>this</code> widget.
   
   @param jcomp
   *            Component.
   */
  public void setComponent(JComponent jcomp);

  /**
   * Returns indication whether <code>this</code> widget requires custom LAF
   * support. Some widgets such as {@link TabOverviewDialogWidget} or
   {@link TabHoverPreviewWidget} require custom implementation based on the
   * internals of the specific LAF. Relevant functions in the base
   {@link LafWidgetSupport} support throw
   {@link UnsupportedOperationException}.
   
   @return <code>true</code> if <code>this</code> widget requires custom
   *         LAF support, <code>false</code> otherwise.
   */
  public boolean requiresCustomLafSupport();

  /**
   * Installs UI on the associated component.
   */
  public void installUI();

  /**
   * Installs default settings for the associated component.
   */
  public void installDefaults();

  /**
   * Installs listeners for the associated component.
   */
  public void installListeners();

  /**
   * Installs components for the associated component.
   */
  public void installComponents();

  /**
   * Uninstalls UI on the associated component.
   */
  public void uninstallUI();

  /**
   * Uninstalls default settings for the associated component.
   */
  public void uninstallDefaults();

  /**
   * Uninstalls listeners for the associated component.
   */
  public void uninstallListeners();

  /**
   * Uninstalls components for the associated component.
   */
  public void uninstallComponents();
}


As stated above, the UI delegates in the look-and-feel should fetch the set of widgets for the associated component (using getMatchingWidgets function on LafWidgetRepository) and set it on the UI delegate instance. All install / uninstall methods in the UI delegate class should call the corresponding lifecycle methods on all the registered widgets. For example:

public class MyLafTabbedPaneUI extends MetalTabbedPaneUI {
  protected Set lafWidgets;

  public static ComponentUI createUI(JComponent x) {
    return new MyLafTabbedPaneUI();
  }

  public void installUI(JComponent c) {
    // Fetches all widgets that were registered for the
    // specified component
    this.lafWidgets = LafWidgetRepository.getRepository()
        .getMatchingWidgets(c);
    super.installUI(c);
    
    // Some custom logic
    
    // Installs all widgets
    for (Iterator it = this.lafWidgets.iterator(); it.hasNext();) {
      LafWidget lw = (LafWidgetit.next();
      lw.installUI();
    }
  }

  protected void installListeners() {
    super.installListeners();
    
    // Install custom listeners
    // ...
    
    // Installs listeners on all widgets
    for (Iterator it = this.lafWidgets.iterator(); it.hasNext();) {
      LafWidget lw = (LafWidgetit.next();
      lw.installListeners();
    }
  }

  protected void installComponents() {
    super.installComponents();
    
    // Install custom components
    // ...
    
    // Installs components on all widgets
    for (Iterator it = this.lafWidgets.iterator(); it.hasNext();) {
      LafWidget lw = (LafWidgetit.next();
      lw.installComponents();
    }
  }

  protected void installDefaults() {
    super.installDefaults();
    
    // Install custom defaults
    // ...
    
    // Installs defaults on all widgets
    for (Iterator it = this.lafWidgets.iterator(); it.hasNext();) {
      LafWidget lw = (LafWidgetit.next();
      lw.installDefaults();
    }
  }

  public void uninstallUI(JComponent c) {
    super.uninstallUI(c);
    
    // Some custom logic
    
    // Uninstalls all widgets
    for (Iterator it = this.lafWidgets.iterator(); it.hasNext();) {
      LafWidget lw = (LafWidgetit.next();
      lw.uninstallUI();
    }
  }

  protected void uninstallComponents() {
    super.uninstallComponents();
    
    // Uninstall custom components
    // ...
    
    // Uninstalls components on all widgets
    for (Iterator it = this.lafWidgets.iterator(); it.hasNext();) {
      LafWidget lw = (LafWidgetit.next();
      lw.uninstallComponents();
    }
  }

  protected void uninstallDefaults() {
    super.uninstallDefaults();
    
    // Uninstall custom defaults
    // ...
    
    // Uninstalls defaults on all widgets
    for (Iterator it = this.lafWidgets.iterator(); it.hasNext();) {
      LafWidget lw = (LafWidgetit.next();
      lw.uninstallDefaults();
    }
  }

  protected void uninstallListeners() {
    super.uninstallListeners();
    
    // Uninstall custom listeners
    // ...
    
    // Uninstalls listeners on all widgets
    for (Iterator it = this.lafWidgets.iterator(); it.hasNext();) {
      LafWidget lw = (LafWidgetit.next();
      lw.uninstallListeners();
    }
  }

}


Note that if a UI delegate doesn't override some install / uninstall method, it needs to do so for proper lifecycle management of the widgets. In this case you will need to call the super implementation and call relevant method on all widgets. In addition, some look-and-feels do not provide custom UI delegates for some components. If the relevant components have associated widgets, you will have to create (and register) forwarding UI delegate (that looks as above) without any custom logic (only integration with LafWidgetRepository).

Automatic changes

This project provides an Ant task and a collection of sample batch scripts that allow automating the above tasks of adapting the existing look-and-feel UI delegates and creating forwarding UI delegates. The automatic support uses ASM bytecode manipulation framework in order to take the existing look-and-feel classes and augment the compiled classes with the code described in the previous section. Note that in this case you don't have to make any changes to your code. You can even take an existing third-party LAF jar (tested on InfoNode, Liquid, Looks, Napkin, Pagosoft and Squareness) and change it locally.

Currently, the automatic support can handle any look-and-feel that extends Basic, Metal or Windows core look-and-feels and follow the conventional extension points. The supported look-and-feel must: There are two types of automatic support (that match the relevant tasks in the manual support) - delegate augmentation and LAF augmentation. The LAF augmentation takes the main look-and-feel class and changes the initClassDefaults function, adding entries for the missing UI delegates. In addition, it creates forwarding UI delegates (see examples below). The delegate augmentation takes an existing UI delegate that is not using LafWidgetRepository and changes the relevant install / uninstall methods (adding forwarding ones as necessary). Note that when using automatic support, the LAF augmentation must be performed before the delegate augmentation since the latter needs to augment the generated forwarding delegates.

All the classes mentioned below are located in org.jvnet.lafwidget.ant package. Note that in order to use the automatic support, you need to use the asm-all-2.2.2.jar (available in CVS repository under /lib or from the ASM project site).

The LAF augmentation is provided by the LafMainClassAugmenter. Its main function gets the following parameters: The matching Ant task is defined in AugmentMainTask which has the following attributes and elements: The delegate augmentation is provided by the UiDelegateAugmenter. Its main function gets the following parameters: The matching Ant task is defined in AugmentTask which has the following attributes and elements: Examples of batch files (in CVS repository under /3rd):