How to write your own widget

The best way to start writing your own widget is to study the existing widgets. These range from very simple (such as SelectAllOnFocusGainWidget - see example walk-through below) to very complex (such as ComboboxAutoCompletionWidget). In addition, widgets differ in the dependency on LAF support (explained below). First, here is an example of a very simple widget in org.jvnet.lafwidget.text.SelectAllOnFocusGainWidget class.

This widget adds "select all on focus gain" behaviour on text components that have LafWidget.TEXT_SELECT_ON_FOCUS client property set to Boolean.TRUE. In order to do this, the widget adds a focus listener to relevant text component. The focus listener implements the focusGained function which checks if the text component has the relevant client property set, and if so, calls the selectAll method of the component. A number of important things that this simple widget illustrates: The complete source of this widget is

/**
 * Adds "select all on focus gain" behaviour on text components.
 
 @author Kirill Grouchnikov
 */
public class SelectAllOnFocusGainWidget extends LafWidgetAdapter {
  protected JTextComponent textComp;

  protected FocusListener focusListener;

  /*
   * (non-Javadoc)
   
   * @see org.jvnet.lafwidget.LafWidgetAdapter#setComponent(javax.swing.JComponent)
   */
  public void setComponent(JComponent jcomp) {
    super.setComponent(jcomp);

    this.textComp = (JTextComponentjcomp;
  }

  /*
   * (non-Javadoc)
   
   * @see org.jvnet.lafwidget.LafWidget#requiresCustomLafSupport()
   */
  public boolean requiresCustomLafSupport() {
    return false;
  }

  /*
   * (non-Javadoc)
   
   * @see org.jvnet.lafwidget.LafWidgetAdapter#installListeners()
   */
  public void installListeners() {
    this.focusListener = new FocusAdapter() {
      public void focusGained(FocusEvent e) {
        if (LafWidgetUtilities.hasTextFocusSelectAllProperty(textComp))
          textComp.selectAll();
      }
    };
    this.textComp.addFocusListener(this.focusListener);
  }

  /*
   * (non-Javadoc)
   
   * @see org.jvnet.lafwidget.LafWidgetAdapter#uninstallListeners()
   */
  public void uninstallListeners() {
    this.focusListener = null;
    this.textComp.removeFocusListener(this.focusListener);
  }
}


This simple widget shows the importance of both delegate augmentation and LAF augmentation explained here. The installListeners and uninstallListeners of this (and all other widgets) will be called only if the corresponding look-and-feel UI delegate will call them (delegate augmentation). If some look-and-feel doesn't have delegate for some component, the widgets will not be instantiated at all (LAF augmentation).

The org.jvnet.lafwidget.text.LockBorderWidget and org.jvnet.lafwidget.text.PasswordStrengthCheckerWidget emply border manipulation in order to achieve desired behaviour. You can see the source code for the relevant life-cycle methods. Note that unlike the previous example, these two widgets are not completely unaware of the currently set LAF. Although they do not require custom LAF support, they consult the currently set LAF support for custom icon / stripe rendering. If not custom LAF support is set, they revert to the basic implementation.

The org.jvnet.lafwidget.combo.ComboboxAutoCompletionWidget and org.jvnet.lafwidget.tree.dnd.TreeDragAndDropWidget are two examples of very complex widgets that require elaborate lifecycle implementation in order to provide the correct and leak-free runtime behaviour. These widgets were contributed by Thomas Bierhance and Antonio Vieiro and refactored to fit the lifecycle interface. These two widgets are completely decoupled from the currently set look-and-feel. The org.jvnet.lafwidget.menu.MenuSearchWidget provides additional complex widget. Although it doesn't require custom LAF support, it requests the current support to make some buttons "flat" (no border, no background when inactive). The default implementation of this support is empty (does nothing).

Two additional widgets in org.jvnet.lafwidget.tabbed.TabHoverPreviewWidget and org.jvnet.lafwidget.tabbed.TabOverviewDialogWidget require custom LAF support as they require access (read / change) to the underlying (LAF-specific) UI delegate. As such, they are available only in look-and-feels that register custom LAF support on the LafWidgetRepository.

If you are planning to write your own widget, you should remember the following: