Dimitry Karpenko
Java/Eclipse developer in MyEclipse and Webclipse teams.
Java/Eclipse developer in MyEclipse and Webclipse teams.
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.
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.
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.
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!
A custom editor for color selection
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.
A custom editor for selecting color and style formatting
Sample Project—sapphire_customeditor_tutorial
Colors.xml (zipped)—colors.xml
If you have any comments or questions, we would love to hear from you @MyEclipseIDE on twitter or via the MyEclipse forum. Happy coding!