Tuesday, September 21, 2010

Metawidget Neat Trick: Enhancing the RichFaces Color Picker

I thought I'd post a few blog entries about 'neat tricks' I've discovered while using Metawidget for my own clients (eating my own dog food, as it were).

The first one concerns a bugbear I've had with the RichFaces color picker widget. The widget looks like this:
It's a cool component, but for me it lacks an important feature: once you've chosen a color there's no way to unchoose it - to clear the box. This is annoying because typically getColor is a nullable field, so we need a way to null it. One approach would be to extend HtmlColorPicker (or its Renderer) and try to add the extra functionality - but that can be a bit scary.

Metawidget affords us a different approach. We can write a small WidgetProcessor and it will process every ColorPicker (across our entire application) and add the extra button. Here's the code:

package com.myapp;

import java.util.Map;

import javax.faces.application.Application;
import javax.faces.component.UIComponent;
import javax.faces.component.html.HtmlGraphicImage;
import javax.faces.context.FacesContext;

import org.metawidget.faces.component.UIMetawidget;
import org.metawidget.widgetprocessor.iface.WidgetProcessor;
import org.richfaces.component.html.HtmlColorPicker;

public class ColorPickerWidgetProcessor
   implements WidgetProcessor<UIComponent, UIMetawidget> {

   public UIComponent processWidget( UIComponent component, String elementName, Map<String, String> attributes,
                     UIMetawidget metawidget ) {

      if ( !( component instanceof HtmlColorPicker ) ) {
         return component;

      FacesContext context = FacesContext.getCurrentInstance();
      Application application = context.getApplication();

      UIComponent stubComponent = application.createComponent( "org.metawidget.Stub" );
      stubComponent.getChildren().add( component );

      HtmlGraphicImage graphicImage = (HtmlGraphicImage) application.createComponent( "javax.faces.HtmlGraphicImage" );
      graphicImage.setStyle( "vertical-align: middle; margin-left: 2px; cursor: pointer" );
      graphicImage.setValue( "/media/core.delete.gif" );
      graphicImage.setTitle( "Remove colour" );
      graphicImage.setOnclick( "if ( confirm( 'Okay to remove this colour?' )) { var picker = document.getElementById( '" + component.getClientId( context ) + "' ); picker.childNodes[0].value = ''; picker.childNodes[1].style.backgroundColor = '#ffffff'; }" );
      stubComponent.getChildren().add( graphicImage );

      return stubComponent;

And here's what it produces:
Now you can click the red 'X' next to each ColorPicker, and it will use JavaScript to clear the box ready for POST back.

Feedback welcome!