facebook
Dimitry Karpenko
Java/Eclipse developer in MyEclipse and Webclipse teams.
Posted on Oct 27th 2015

In a previous article we discussed how to create custom bindings for xml editing with Eclipse Sapphire. Custom bindings are a rather powerful way of xml editing customization, but sometimes we need to customize editing on the UI side. In this case we need to use custom editors. Let’s see how this is accomplished.

An Example Custom Editor

To demonstrate, let’s make a simple Sapphire editor for an Android color resources xml file. This editor will allow you to add a new color and edit the color’s name and value. Let’s make this interesting by creating a custom hyperlink control for selecting the color value. This hyperlink has a hex color string (like #ff0000) as text, and given text has a color matching given hex value. The hyperlink can be clicked to change the color value represented by the editor.

Create a Model

Our Model is quite simple – it contains two classes: root element `IAndroidResources` containing a list of colors and `IColor`, describing color itself with properties `Name` and `Value`, please see the sample project for details.

Create the Editor

Now let’s see how to create the custom editor. We need to implement `Presentation` and `Presentation Factory` for our editor. Presentation is the core part here – it’s responsible for the editor UI, lifecycle, showing changed data and setting data after user modification. Since we are creating a custom editor for value property, we need to extend `org.eclipse.sapphire.ui.forms.swt.ValuePropertyEditorPresentation`.

@SuppressWarnings("restriction")
public class ColorPropertyEditorPresentation extends
   	 ValuePropertyEditorPresentation {
        
    private Hyperlink chooseLink;
    private Font boldFont;
    

    public ColorPropertyEditorPresentation(FormComponentPart part,
   		 SwtPresentation parent, Composite composite) {
   	 super(part, parent, composite);
    }   

In `createContents()` we create a composite containing hyperlink and assist decorator for it; hyperlink is set as the main editing control. But as you can see, you can create any SWT component hierarchy you want here. For additional details, refer to the sample project.

    @Override
    /**
     * Here we create SWT presentation of our custom control, bind it to data control layer
     * and etc.
     */
    protected void createContents(Composite parent) {
   	 final PropertyEditorPart part = part();
   	 
    	final Composite composite = createMainComposite(parent, new CreateMainCompositeDelegate( part )); //Create composite to place our controls on
   	 
    	composite.setLayout( glspacing( glayout( 2 , 0, 0 ), 2 ) );

    	final PropertyEditorAssistDecorator decorator = createDecorator( composite ); //Create assist decorator
    	decorator.control().setLayoutData( gdvalign( gd(), SWT.CENTER ) );
    	this.chooseLink = new Hyperlink(composite, SWT.NONE);
       	this.chooseLink.setLayoutData( gd() );
       	this.chooseLink.setText("Select...");

    	attachAccessibleName( this.chooseLink );
   	 
    	this.chooseLink.addHyperlinkListener(new IHyperlinkListener() {
   		 
   		 @Override
   		 public void linkExited(HyperlinkEvent e) {
   			 // Do nothing
   		 }
   		 
   		 @Override
   		 public void linkEntered(HyperlinkEvent e) {
   			 // Do nothing
   		 }
   		 
   		 @Override
   		 public void linkActivated(HyperlinkEvent e) {
   			 doSelectValue();
   		 }
    	});
    	addControl( this.chooseLink );
   	 
    	decorator.addEditorControl( composite );
   	 
    }   

 Now let’s create `Presentation Factory` which is responsible for creating editor presentation for some particular property:

public PropertyEditorPresentation create(final PropertyEditorPart part,
   		 final SwtPresentation parent, final Composite composite) {
   	 final PropertyDef property = part.property().definition();

   	 if (property instanceof ValueProperty) {
   		 return new ColorPropertyEditorPresentation(part, parent,
   				 composite);
   	 }

   	 return null;
    }

As you can see, in this case it’s quite simple – it just returns our newly created `Presentation` instance if we have `ValueProperty`, but this can be much more complex (e.g., analyzing rendering hints, property annotations, tuning newly created presentation accordingly, etc.).

After this we need to create an sdef file with a UI description for our editor and register our editor in plugin.xml. The UI description sdef file is plain, except `Value` property, for which we want to use our custom editor:

<property-editor>
	<property>Value</property>
	<hint>
<name>factory</name>
		<value>ColorPropertyEditorPresentationFactory</value>
	</hint>
<property-editor>

`<hint>` with name=factory tells the Sapphire framework which Presentation Factory class to use for presenting this property editor. As you can see, `<value>` tag contains our Factory’s class name without package name. To make this work, add the following line in the imports section at the beginning of the sdef file.

<package>com.genuitec.sapphire.customeditor.propertyeditors</package>

Now let’s add our editor to plugin.xml, in the same way as our previous example. Please refer to the sample project for details.

And that’s it, our editor is finished!
CustomEditor1

A custom editor for color selection

More Customization

But what if we need to use our custom editor for representing properties that require a different appearance or behavior? No problem, Sapphire suggests several ways for doing this.

Let’s allow the user to customize the look of our hyperlink and make it bold and/or underlined if needed. To accomplish this, let’s use hints, like we used for specifying Presentation Factory above. So, let’s add one more hint to our `<property-editor>`:

<hint>
<name>style</name>
<value>bold, underlined</value>
</hint>

In our `ColorPropertyEditorPresentation`, let’s add the following code. Two string constants for bold and underlined styles:

private static final String BOLD_STYLE = "bold";
private static final String UNDERLINED_STYLE = "underlined";

After the line `this.chooseLink = new Hyperlink(composite, SWT.NONE);` in `createContents` method, let’s add the following code, which obtains our hint and makes necessary styling:

 String style = definition.getHint("style");
String[] styles = style.split(",");
boolean bold = checkStyle(BOLD_STYLE, styles);
boolean underlined = checkStyle(UNDERLINED_STYLE, styles);
    
this.chooseLink.setUnderlined(underlined);
if (bold) {
boldDescriptor = FontDescriptor.createFrom(chooseLink.getFont()).setStyle(SWT.BOLD);
        	boldFont = boldDescriptor.createFont(chooseLink.getDisplay());
        	this.chooseLink.setFont(boldFont);
}

Next, let’s add helper method `checkStyle`, which checks whether a given string array contains string constant we want:

private boolean checkStyle(String wantedStyle, String[] styles) {
   	 for (String curStyle : styles) {
   		 curStyle = curStyle.trim();
   		 if (wantedStyle.equalsIgnoreCase(curStyle)) {
   			 return true;
   		 }
   	 }
   	 return false;
}

But what if we need to make some customization in the model, not in the editor’s sdef? That’s not a problem, we just need to create a simple annotation:

@Retention(RetentionPolicy.RUNTIME) //Annotation data would be available on runtime
@Target({java.lang.annotation.ElementType.FIELD }) //Annotation can be used for fields
public @interface Style {
    
    public abstract String value();    

}

Let’s change how we obtain a string with styles in `ColorPropertyEditorPresentation.createContents()`:

Value<String> property = definition.getProperty();
styleAnnotation = property.definition().getAnnotation(Style.class);
String style = styleAnnotation!=null ? styleAnnotation.value() : "";

And let’s add `Style` annotation to `Value` property in `IColor`:

// *** Color value ***
@XmlBinding(path = "")
@Label(standard = "Value")
@Style("bold, underlined")
ValueProperty PROP_VALUE = new ValueProperty(TYPE, "Value"); //$NON-NLS-1$

Value<String> getValue();

void setValue(String value);

Now let’s see what the editor looks like.

CustomEditor2
A custom editor for selecting color and style formatting

Notes on Custom Editors

  • Custom editors should only be used for truly custom UI (e.g., color chooser, image selector, music file selector with ability to play, etc.). If you only need to customize how your property value is being read/written into the xml file, use custom bindings instead. If you need a simple text field, but have a custom dialog to select/modify its value, it can be done with a custom browsing action.
  • Before building your own custom editor, take a closer look at hints provided by Sapphire for customizing the look and behavior of standard Sapphire controls – maybe you’ll find what you need there.
  • Before implementing your own Presentation, take a look at `org.eclipse.sapphire.ui.forms.swt.ValuePropertyEditorPresentation` subclasses for how standard presentations are implemented. If you create a custom editor placed near another editor, it is good practice to add the same assist/error decorator, indent controls in the same way, etc.

Attachments

Sample Project—sapphire_customeditor_tutorial

Colors.xml (zipped)—colors.xml

Let Us Hear from You!

If you have any comments or questions, we would love to hear from you @MyEclipseIDE on twitter or via the MyEclipse forum. Happy coding!