Laf-Widget : How To Write Your Own Widget

# 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:

- Since the configuration file registers this widgets on javax.swing.text.JTextComponent, we can safely assume that we are dealing with text components (casting-wise).
- We extend the LafWidgetAdapter that provides default implementation of setComponent method (storing the component) and empty implemenations of the lifecycle methods. This allows us to implement only relevant methods (installListeners and uninstallListeners).
- The setComponent implementation stores the casted text component to cut down on the number of casts in the lifecycle methods. This is not required but recommended if you're going to use the specific component type extensively.
- It's extremely important to unregister all registered listeners (otherwise you will start retaining memory and leaking resources).

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 = (JTextComponent) jcomp;
}
/*
* (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:

- Unregister everything that you register on the components.
- Bear in mind that the widget is expected to work under all look-and-feels.
- In the configuration file you can use a superclass name (like javax.swing.text.JTextComponent) in order to make it more terse. This is relevant only if your widget is applicable to all descendants of this class.
- Provide support for RTL orientation (if relevant).
- Provide i18n and l10n support.
- Make as few assumptions as possible about the current UI delegate of the component you're working on. Remember that not all look-and-feels extend Metal.
- Provide configurable behaviour (so that your widget can be easily disabled on per-component basis). This is preferably done via client properties that can be set on either component or UIManager.
- If your widget adds substantial functionality, consider making it disabled by default. Use client properties to enable them (let the application decide that it wishes to use your widget). This way you will not alienate users with unexpected widgets.
- Expose as little API as possible to the application. With no API the application will be able to run with or without your widget, preventing the "lock-in".