EEF/User Guide/Custom Widget Generation

From Eclipsepedia

Jump to: navigation, search

Contents

Need to generate new widgets ?

In the CustomElementEditor guide, we saw how to ponctually include specific widgets in EEF generated editing forms. If this need is becoming common, EEF offers the ability to extend its generators to generate new widgets.

Environment initialization

As we will create acceleo generator, we need to install the Acceleo SDK. It can be done via the release train:

acceleo install

Next, in an empty workspace, we create an Acceleo Project. In the new project wizard, we can initialize our first template:

Acceleo project creation

Let's name it spinnerGettersSetter. It need two metamodels:

The other data are marginal, we will erase the generated body of this module.

We also have to add to the generator project a dependency to the EEF Codegen plugin : org.eclipse.emf.eef.codegen.

All generator projects extending EEF must have a dependency on its generation project

And finally, we add an Extension to the generator project. The org.eclipse.acceleo.engine.dynamic.templates defines a project as an dynamic module able to extend existing generators. In this extension, we must specify the folders containing dynamic modules. In our case, we can specify the folder org/eclipse/emf/samples/eef/gen/spinner/common.

All generator projects extending EEF must be defined as Acceleo dynamic modules

After these step, we can create our modules to generate spinner with EEF.

Creating Acceleo modules to generate a specific widget with EEF

In the CustomElementEditor guide, we modified 4 classes in the code generated by EEF in order to add manually a specific widget in an EEF editing form. To generate a specific widget, we have to create 4 generation modules and to override the existing common module.

Generating PropertiesEditionPart

The first module is the "GettersSetters" module defining the methods' signature for the views interfaces and their implementations in the PartImpls and the PartForms. In our case, we just have to override the generation in order to define the getters and setters implementations. We have to override two templates from the abstract module widgetGettersSetters :

  • getterSignatureImplementation
  • setterSignatureImplementation

To do that, you can use the override view in Acceleo :

6 - EEF CostumGen GettersSettersTemplateOverride.png

This view initialize the two templates to implement :

EEF CostumGen GettersSettersTemplateToOverride.png

The easiest way to defines your templates is to copy the code from an existing example and to copy it into the templates:

8 - EEF CostumGen GeterrsSettersTemplateCodeCopy.png

Then you can replace the dynamic part of this code by an Acceleo dynamic code area:

9 - EEF CostumGen GettersSettersDynamicCode.png

In this example, we've used the
getterSignature()
and the
setterSignature()
templates which generate the getter and setter signatures for the given ElementEditor. We've also used the
elementEditorName()
template which refers to the name of the field in the generated views. To find these services you can browse the services package in the EEF generation project or look at the generation modules for the existing widgets.

For the moment, this generation module override all the other generation module for view interface generation. We must reduce the overriding scope the the ElementEditor having a Spinner for representation. For that, in our two templates, we add a guard reducing the overriding scope :

[template public getterSignatureImplementation(elementEditor : ElementEditor) overrides getterSignatureImplementation 
   ? (elementEditor.representationName('Spinner'))] 
   ...
[/template]

[template public setterSignatureImplementation(elementEditor : ElementEditor) overrides setterSignatureImplementation 
   ? (elementEditor.representationName('Spinner'))]
   ...
[/template]

After this step, the spinnerGettersSetters is finished.

Common module override

The common module provides a large set of services to the EEF generation. In our case, it provides a toJavaType service that define the Java Type to associate to a given widget. We must override this template to associate the Integer type to the Spinner.

The first step is to create a spinnerCommon module. This module must extends the org::eclipse::emf::eef::codegen::services::common module for the EEF generation project.

In this module you have to override the toJavaType template:

10 - EEF CostumGen CommonToJavaTypeOverride.png

For the Spinner case (don't forget to specify a guard), we must provide the String java type:

[template public toJavaType(view : ViewElement) overrides toJavaType ? (view.representationName('Spinner'))]
String
[/template]

This is all you have to do in this module.

Generating the PropertiesEditingParts

For generating the propertiesEditingPart you have to generate two modules:

  • spinnerFormImpl.mtl extending org::eclipse::emf::eef::codegen::widgets::api::widgetFormImpl
  • spinnerSWTImpl.mtl extending org::eclipse::emf::eef::codegen::widgets::api::widgetSWTImpl

In each template you have to override four templates:

12 - EEF CostumGen PartTemplatesToOverride.png


Idea.png
Don't forget to add a guard to each overriding template


The first template manageImports let you define the imports to add to the java class in order to generate a well compiling code. In our case we can add the Spinner imports via the service addImports from the org::eclipse::emf::eef::codegen::services::imports module:

[template public manageImports(widget : Widget, pec : PropertiesEditionComponent, view : View, basePackage : String) overrides manageImports 
   ? (widget.name = 'Spinner')]
[pec.addImports(Sequence{
	'org.eclipse.swt.widgets.Spinner'
	})/]
[/template]

The addImports can be called on any element in the template.

The widgetDeclaration allow you to declare all the needed fields in the PropertiesEditingParts. In our case, we only need a Spinner:

[template public toJavaType(view : ViewElement) overrides toJavaType ? (view.representationName('Spinner'))]
String
[/template]

The third template implementation let you specify the way to build the GUI for our widget. Let's invoke a custom method:

[template public implementation(editor : ViewElement) overrides implementation ? (editor.representationName('Spinner'))]
return create[editor.name.toJavaClassifier()/]Spinner(widgetFactory, parent);
[/template]

This example is for the PartForm implementation, in the PartImpl we don't have widgetFactory.

Finally, the additionalImplementation template let you defines all the other needed methods. In our case the createXXXSpinner. Here is the code for the FormImpl:

[template public additionalImplementation(editor : ElementEditor, pec : PropertiesEditionComponent, inputPEC : PropertiesEditionComponent) 
   overrides additionalImplementation ? (editor.representationName('Spinner'))]
	protected Composite create[editor.name.toJavaClassifier()/]Spinner(FormToolkit widgetFactory, Composite parent) {
		[if (editor.nameAsLabel)]
		FormUtils.createPartLabel(widgetFactory, parent, 
                    [include(editor.owningViewsRepository().qualifiedMessagesProvider())/].[editor.editorKey(editor.owningView())/], 
                    propertiesEditionComponent.isRequired([editor.editorID()/], 
                    [editor.owningViewsRepository().vrClass()/].FORM_KIND));
		[else]
		createDescription(parent, [editor.editorID()/], 
                                  [include(editor.owningViewsRepository().qualifiedMessagesProvider())/].[editor.editorKey(editor.owningView())/]);
		[/if]
		[editor.elementEditorName()/] = new Spinner(parent, SWT.NONE); //$NON-NLS-1$
		[editor.elementEditorName()/].setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
		widgetFactory.paintBordersFor(parent);
		[editor.elementEditorName()/].addSelectionListener(new SelectionAdapter() {

			/**
			 * {@inheritDoc}
			 * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
			 */
			@Override
			public void widgetSelected(SelectionEvent e) {
				if (propertiesEditionComponent != null)
					propertiesEditionComponent.firePropertiesChanged(
                                                 new PropertiesEditionEvent([editor.editionPart()/].this, [editor.editorID()/], 
                                                                            PropertiesEditionEvent.COMMIT, PropertiesEditionEvent.SET, null,
                                                                            String.valueOf([editor.elementEditorName()/].getSelection())));
			}
			 
		});
		GridData [editor.elementEditorName()/]Data = new GridData(GridData.FILL_HORIZONTAL);
		[editor.elementEditorName()/].setLayoutData([editor.elementEditorName()/]Data);
		FormUtils.createHelpButton(widgetFactory, parent, 
                                           propertiesEditionComponent.getHelpContent([editor.editorID()/], 
                                           [editor.owningViewsRepository().vrClass()/].FORM_KIND), null); //$NON-NLS-1$
		return parent;
	}
[/template]

And here is the code for the SWTImpl:

[template public additionalImplementation(editor : ElementEditor, pec : PropertiesEditionComponent, inputPEC : PropertiesEditionComponent) 
   overrides additionalImplementation ? (editor.representationName('Spinner'))]
	protected Composite create[editor.name.toJavaClassifier()/]Spinner(Composite parent) {
		[if (editor.nameAsLabel)]
		SWTUtils.createPartLabel(widgetFactory, parent, 
                                         [include(editor.owningViewsRepository().qualifiedMessagesProvider())/].[editor.editorKey(editor.owningView())/],
                                         propertiesEditionComponent.isRequired([editor.editorID()/], [editor.owningViewsRepository().vrClass()/].FORM_KIND));
		[else]
		createDescription(parent, [editor.editorID()/], 
                                  [include(editor.owningViewsRepository().qualifiedMessagesProvider())/].[editor.editorKey(editor.owningView())/]);
		[/if]
		[editor.elementEditorName()/] = new Spinner(parent, SWT.NONE); //$NON-NLS-1$
		[editor.elementEditorName()/].addSelectionListener(new SelectionAdapter() {

			/**
			 * {@inheritDoc}
			 * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
			 */
			@Override
			public void widgetSelected(SelectionEvent e) {
				if (propertiesEditionComponent != null)
					propertiesEditionComponent.firePropertiesChanged(
                                                 new PropertiesEditionEvent([editor.editionPart()/].this, 
                                                                            [editor.editorID()/], PropertiesEditionEvent.COMMIT, 
                                                                             PropertiesEditionEvent.SET, null, 
                                                                             String.valueOf([editor.elementEditorName()/].getSelection())));
			}
			 
		});
		GridData [editor.elementEditorName()/]Data = new GridData(GridData.FILL_HORIZONTAL);
		[editor.elementEditorName()/].setLayoutData([editor.elementEditorName()/]Data);
		SWTUtils.createHelpButton(parent, 
                               propertiesEditionComponent.getHelpContent([editor.editorID()/], 
                                                                         [editor.owningViewsRepository().vrClass()/].FORM_KIND), 
                                                                         null); //$NON-NLS-1$
		return parent;
	}
[/template]

Generating the PropertiesEditingComponent

Finally we have to generate process the generation in the PropertiesEditingComponent. We create the last module spinnerControl.mtl extending org::eclipse::emf::eef::codegen::widgets::api::widgetControl. We have to override four templates:

13 - EEF CostumGen ControlTemplateToOverride.png

The declareEObjectUpdater template generate the code defining the view initialization process. In our case, we have to use the spinner setter to initialize its value with the current value of the edited EObject:

[template public declareEObjectUpdater(editionElement : PropertiesEditionElement, view : View, pec : PropertiesEditionComponent) 
   overrides declareEObjectUpdater ? (editionElement.representationName('Spinner'))]
[pec.pecModelClass().toJavaIdentifier()set[editionElement.model.name.toUpperFirst()/](
   ([include('org.eclipse.emf.eef.runtime.impl.utils.EEFConverterUtil')/]
      .create[editionElement.model.eType.instanceClassName.toUpperFirst()/]FromString([editionElement.metamodelTypeGetter()/], 
   (String)event.getNewValue())));
[/template]

The liveUpdater template generates the code defining the component behavior when the edited EObject is modified:

[template public liveUpdater(editionElement : PropertiesEditionElement, view : View, pec : PropertiesEditionComponent) 
   overrides liveUpdater ? (editionElement.representationName('Spinner'))]
if ([editionElement.metamodelGetter()/].equals(msg.getFeature()) 
      && [view.viewIdentifier(pec)/] != null 
      && isAccessible([editionElement.views->first().editorID()/])) {
	if (msg.getNewValue() != null) {
		[view.viewIdentifier(pec)/].[editionElement.views->first().viewPackageSetter()/](
                     EcoreUtil.convertToString([editionElement.metamodelTypeGetter()/], msg.getNewValue()));
	} else {
		[view.viewIdentifier(pec)/].[editionElement.views->first().viewPackageSetter()/]("");
	}
}
[/template]

The updater template generates the code defining the component behavior when the view is modified:

[template public updater(editionElement : PropertiesEditionElement, view : View, pec : PropertiesEditionComponent) 
   overrides updater ? (editionElement.representationName('Spinner'))]
if (isAccessible([editionElement.views->first().editorID()/])) {
	[view.viewIdentifier(pec)/].[editionElement.views->first().viewPackageSetter()/](
              EEFConverterUtil.convertToString([editionElement.metamodelTypeGetter()/], 
             [pec.pecModelClass().toJavaIdentifier()/].get[editionElement.model.name.toUpperFirst()/]()));
}

[/template]

Finally the filterUpdater template generated the initialization code for adding filter in JFace viewer. Our Spinner isn't a JFace viewer so this template is empty:

[template public filterUpdater(editionElement : PropertiesEditionElement, view : View, pec : PropertiesEditionComponent) 
   overrides filterUpdater ? (editionElement.representationName('Spinner'))]
[comment  No filter needed/]
[/template]

Generating the code

After these steps, the generation module is ready to use. You can launch an Eclipse runtime and launch a generation in the new Eclipse instance of build this plugin and install-it in the eclipse you want to generate Spinners.