Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "EEF/User Guide/Custom Element Editor"

(Modifying the PropertiesEditionPartForm)
 
(28 intermediate revisions by 3 users not shown)
Line 1: Line 1:
== Need of new widget ? ==
+
<blockquote style="color: #8a6d3b; background-color: #fcf8e3; border: solid thin #faf2cc;">
 +
This page is now deprecated starting with EEF 1.6.0, find the updated documentation on the EEF homepage.
 +
</blockquote>
  
EEF generators offer a set of widget to build EMF editing forms. Sometime other widget needs appear to create more efficient GUIs. In this case, EEF users can employ ''CustomElementEditor'' to generate user code areas inside the EEF properties code.
+
== Need of new widgets ? ==
  
Let's start this sample with this sample Ecore model :
+
EEF generators offer a set of widgets to build EMF editing forms. Sometimes needs for other widgets appear in order to create more efficient GUIs. In this case, EEF provides ''CustomElementEditor'' to generate user code areas inside the EEF properties code.
 +
 
 +
Let's start this tutorial with this sample Ecore model :
  
 
[[Image:EEF_SampleCustomPEE_Metamodel.png|Ecore model for CustomElementEditor sample]]
 
[[Image:EEF_SampleCustomPEE_Metamodel.png|Ecore model for CustomElementEditor sample]]
  
By following the [[../Tutorials/First_Generation | first generation tutorial]], EEF generates theses forms :
+
By following the [[../../Tutorials/First_Generation | first generation tutorial]], EEF generates these forms :
  
 
[[Image:EEF_CustomPEESample_Standard_result.png | Standard result]]
 
[[Image:EEF_CustomPEESample_Standard_result.png | Standard result]]
  
Now suppose we want to use a [http://www.eclipse.org/swt/snippets/#spinner spinner] instead of a Text for the age entry. Spinners aren't yet available in the EEF generation. We can, in this case use a ''CustomElementEditor''.
+
Now suppose we want to use a [http://www.eclipse.org/swt/snippets/#spinner spinner] instead of a Text for the age entry. Spinners aren't yet available in the EEF generation. We may, in this case, use a ''CustomElementEditor''.
 +
 
 +
{{tip | The ''CustomElementEditor'' is fully operational since the 1.1.0 version of EEF }}
  
 
== CustomElementEditor in the EEF models ==
 
== CustomElementEditor in the EEF models ==
Line 27: Line 33:
 
== Modifying the generated code ==
 
== Modifying the generated code ==
  
By using CustomElementEditor, EEF generates user code areas in four classes. We need to complete these classe to includes our own widget and to bind it with the EMF model. The four classes to modify are :
+
By using CustomElementEditor, EEF generates user code areas in four classes. We need to complete these classes to include our own widget and to bind it with the EMF model. The four classes to modify are :
 
* the IPropertiesEditionPart : '''XXXPropertiesEditionPart'''
 
* the IPropertiesEditionPart : '''XXXPropertiesEditionPart'''
 
* the PropertiesEditionPartForm : '''XXXPropertiesEditionPartForm'''
 
* the PropertiesEditionPartForm : '''XXXPropertiesEditionPartForm'''
Line 35: Line 41:
 
=== Modifying the IPropertiesEditionPart ===
 
=== Modifying the IPropertiesEditionPart ===
  
The IPropertiesEditionPart interfaces defines the view's getters and setters for each widget. As we used a CustomElementEditor, EEF has generated an user code area in the ''PersonPropertiesEditionPart'' :
+
The IPropertiesEditionPart interfaces defines the view's getters and setters for each widget. As we used a CustomElementEditor, EEF has generated a user code area in the ''PersonPropertiesEditionPart'' :
  
 
[[Image:EEF_SampleCEE_PartModifying.png | CustomElementEditor : User code area for PropertiesEditionPart ]]
 
[[Image:EEF_SampleCEE_PartModifying.png | CustomElementEditor : User code area for PropertiesEditionPart ]]
  
We must had getter and setter for our spinner :  
+
We need getter and setter for our spinner :  
  
 
<source lang="java">
 
<source lang="java">
Line 60: Line 66:
 
=== Modifying the PropertiesEditionPartForm ===
 
=== Modifying the PropertiesEditionPartForm ===
  
The PropertiesEditionPartForm is an EEF view used in a [http://www.eclipse.org/articles/Article-Forms/article.html | Eclipse Forms] Context (properties views, editors, ...). As in the interface, the PartForm code contains user code areas where we can include our own widget :
+
The PropertiesEditionPartForm is an EEF view used in a [http://www.eclipse.org/articles/Article-Forms/article.html Eclipse Forms] Context (properties views, editors, ...). As in the interface, the PropertiesEditionPartForm code contains user code areas where we can include our own widget :
  
[[Image:EEF_SampleCEE_PartFormModifying.png | CustomElementEditor : Part Form modifying]]
+
[[Image:EEF_SampleCEE_PartFormModifying.png | CustomElementEditor : User code areas for PropertiesEditionPartForm]]
 +
 
 +
In the first area, we can define the fields we need for our own widget :
 +
<source lang=java>
 +
// Start of user code for age widgets declarations
 +
protected Spinner age;
 +
// End of user code
 +
</source>
 +
 
 +
In the third area, we can create our getter and setter. In the PropertiesEditionPart we added two methods in the interface : getAge() and setAge(), we have to implement them in this class :
 +
 
 +
<source lang=java>
 +
// Start of user code for age specific getters and setters implementation
 +
/**
 +
* {@inheritDoc}
 +
* @see customsample.parts.PersonPropertiesEditionPart#getAge()
 +
*/
 +
public String getAge() {
 +
return String.valueOf(age.getSelection());
 +
}
 +
 
 +
/**
 +
* {@inheritDoc}
 +
* @see customsample.parts.PersonPropertiesEditionPart#setAge(String newValue)
 +
*/
 +
public void setAge(String newValue) {
 +
if (newValue != null) {
 +
Integer valueOf = Integer.valueOf(newValue);
 +
age.setSelection(valueOf);
 +
}
 +
}
 +
// End of user code
 +
</source>
 +
 
 +
In the second area, we have to invoke the method creating our widget in the editing form :
 +
 
 +
<source lang=java>
 +
// Start of user code for age addToPart creation
 +
if (key == CustomsampleViewsRepository.Person.Properties.age) {
 +
return createAgeSpinner(widgetFactory, parent);
 +
}
 +
// End of user code
 +
</source>
 +
 
 +
In this sample, we named this method ''createAgeSpinner''. This method is defined in the fourth area. We create our spinner at this point.
 +
 
 +
<source lang=java>
 +
// Start of user code additional methods
 +
protected Composite createAgeSpinner(FormToolkit widgetFactory, Composite parent) {
 +
createDescription(parent, CustomsampleViewsRepository.Person.Properties.age, CustomsampleMessages.PersonPropertiesEditionPart_AgeLabel);
 +
age = new Spinner(parent, SWT.NONE); //$NON-NLS-1$
 +
age.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
 +
widgetFactory.paintBordersFor(parent);
 +
GridData ageData = new GridData(GridData.FILL_HORIZONTAL);
 +
age.setLayoutData(ageData);
 +
FormUtils.createHelpButton(widgetFactory, parent, propertiesEditionComponent.getHelpContent(CustomsampleViewsRepository.Person.Properties.age, CustomsampleViewsRepository.FORM_KIND), null); //$NON-NLS-1$
 +
return parent;
 +
}
 +
// End of user code
 +
</source>
 +
 
 +
After this step, we can relaunch our plugin to see our new widget in a Form context (basically the properties view) :
 +
 
 +
[[Image:EEF_SampleCEE_FormResult.png | CustomElementEditor : Spinner in a form context]]
  
 
=== Modifying the PropertiesEditionPartImpl ===
 
=== Modifying the PropertiesEditionPartImpl ===
 +
 +
The PropertiesEditionPartImpl is an EEF view used in a standard SWT Context (wizards, dialogs, ...). As in the interface, the PropertiesEditionPartImpl code contains user code areas where we can include our own widget :
 +
 +
[[Image:EEF_SampleCEE_PartImplModifying.png | CustomElementEditor : User code areas for PropertiesEditionPartImpl]]
 +
 +
In the first area, we can define the fields we need for our own widget :
 +
<source lang=java>
 +
// Start of user code for age widgets declarations
 +
protected Spinner age;
 +
// End of user code
 +
</source>
 +
 +
In the third area, we can create our getter and setter. In the PropertiesEditionPart we added two methods in the interface : getAge() and setAge(), we have to implement them in this class :
 +
 +
<source lang=java>
 +
// Start of user code for age specific getters and setters implementation
 +
/**
 +
* {@inheritDoc}
 +
* @see customsample.parts.PersonPropertiesEditionPart#getAge()
 +
*/
 +
public String getAge() {
 +
return String.valueOf(age.getSelection());
 +
}
 +
 +
/**
 +
* {@inheritDoc}
 +
* @see customsample.parts.PersonPropertiesEditionPart#setAge(String newValue)
 +
*/
 +
public void setAge(String newValue) {
 +
if (newValue != null) {
 +
Integer valueOf = Integer.valueOf(newValue);
 +
age.setSelection(valueOf);
 +
}
 +
}
 +
// End of user code
 +
</source>
 +
 +
In the second area, we have to invoke the method creating our widget in the editing form :
 +
 +
<source lang=java>
 +
// Start of user code for age addToPart creation
 +
if (key == CustomsampleViewsRepository.Person.Properties.age) {
 +
return createAgeSpinner(parent);
 +
}
 +
// End of user code
 +
</source>
 +
 +
In this sample, we named this method ''createAgeSpinner''. This method is defined in the fourth area. We create our spinner at this point.
 +
 +
<source lang=java>
 +
// Start of user code additional methods
 +
protected Composite createAgeSpinner(Composite parent) {
 +
createDescription(parent, CustomsampleViewsRepository.Person.Properties.age, CustomsampleMessages.PersonPropertiesEditionPart_AgeLabel);
 +
age = new Spinner(parent, SWT.BORDER); //$NON-NLS-1$
 +
GridData ageData = new GridData(GridData.FILL_HORIZONTAL);
 +
age.setLayoutData(ageData);
 +
SWTUtils.createHelpButton(parent, propertiesEditionComponent.getHelpContent(CustomsampleViewsRepository.Person.Properties.age, CustomsampleViewsRepository.FORM_KIND), null); //$NON-NLS-1$
 +
return parent;
 +
}
 +
// End of user code
 +
</source>
 +
 +
After this step, we can relaunch our plugin to see our new widget in a Form context (basically the properties view) :
 +
 +
[[Image:EEF_SampleCEE_ImplResult.png | CustomElementEditor : Spinner in a standard swt context]]
  
 
=== Modifying the PropertiesEditionComponent ===
 
=== Modifying the PropertiesEditionComponent ===
 +
 +
At this point, all views contain a spinner to display and edit the age attribute of a Person, but these graphical elements aren't bound to the EMF model. The last part of the tutorial consists in modifying the PropertiesEditionComponent code to enable communication between views and model.
 +
 +
[[Image:EEF_SampleCEE_ComponentModifying.png | CustomElementEditor : User code areas for component ]]
 +
 +
We have to edit three different areas in order to activate a binding between model and views.
 +
 +
The first method is the ''initPart'' method. This method is called when the widgets of the managed view must be initialized with the current value of the model. In our case, we must set the current age of the person in the spinner via the setter we developed in the views.
 +
 +
<source lang=java>
 +
// Start of user code for age command update
 +
if (isAccessible(CustomsampleViewsRepository.Person.Properties.age)) {
 +
basePart.setAge(EEFConverterUtil.convertToString(EcorePackage.eINSTANCE.getEInt(), person.getAge()));
 +
}
 +
// End of user code
 +
</source>
 +
 +
The second method is the ''updateSemanticModel'' method. This method is called when the value of a widget has changed in the view so the new value must be set in the model. These method is implemented like this in our case :
 +
 +
<source lang=java>
 +
if (CustomsampleViewsRepository.Person.Properties.age == event.getAffectedEditor()) {
 +
// Start of user code for updateAge method body
 +
person.setAge((EEFConverterUtil.createIntFromString(EcorePackage.eINSTANCE.getEInt(), (String)event.getNewValue())));
 +
// End of user code
 +
}
 +
</source>
 +
 +
This method takes a IPropertiesEditionEvent as a parameter . This parameter is thrown by the widget in the view, we must add a piece of code to our views to handle this event. In the ''createAgeSpinner'' method of both ''PersonPropertiesEditionPartImpl'' and ''PersonPropertiesEditionPartForm'' classes, we add
 +
 +
<source lang=java>
 +
age.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(PersonPropertiesEditionPartForm.this, CustomsampleViewsRepository.Person.Properties.age, PropertiesEditionEvent.COMMIT, PropertiesEditionEvent.SET, null, String.valueOf(age.getSelection())));
 +
}
 +
});
 +
</source>
 +
 +
Finally, the last method is the ''updatePart'' method. This method is called when a property is changed in the model and we must update the value in the view. The code of this last method is :
 +
 +
<source lang=java>
 +
// Start of user code for age live update
 +
if (CustomsamplePackage.eINSTANCE.getPerson_Age().equals(msg.getFeature()) && basePart != null && isAccessible(CustomsampleViewsRepository.Person.Properties.age)) {
 +
if (msg.getNewValue() != null) {
 +
basePart.setAge(EcoreUtil.convertToString(EcorePackage.eINSTANCE.getEInt(), msg.getNewValue()));
 +
} else {
 +
basePart.setAge("");
 +
}
 +
}
 +
// End of user code
 +
</source>
 +
 +
== Conclusion ==
 +
 +
After these steps, our spinner is functional and can be used to set the age of the persons in our UIs.
 +
 +
The CustomElementEditor allows EEF users to integrate any kind of widgets in their editing forms without any change in the EEF generation.

Latest revision as of 11:23, 21 December 2017

This page is now deprecated starting with EEF 1.6.0, find the updated documentation on the EEF homepage.

Need of new widgets ?

EEF generators offer a set of widgets to build EMF editing forms. Sometimes needs for other widgets appear in order to create more efficient GUIs. In this case, EEF provides CustomElementEditor to generate user code areas inside the EEF properties code.

Let's start this tutorial with this sample Ecore model :

Ecore model for CustomElementEditor sample

By following the first generation tutorial, EEF generates these forms :

Standard result

Now suppose we want to use a spinner instead of a Text for the age entry. Spinners aren't yet available in the EEF generation. We may, in this case, use a CustomElementEditor.

Idea.png
The CustomElementEditor is fully operational since the 1.1.0 version of EEF


CustomElementEditor in the EEF models

The first step is to replace the ElementEditor generated by the EEF Initializer with a CustomElementEditor. This must be done in the View associated to our EClass Person, the "Person" View.

CustomElementEditor in views model

The removed ElementEditor was defined as the view of the "age" PropertiesEditionElement of the "Person" PropertiesEditionComponent. We need to define our CustomElementEditor as the new "age" PropertiesEditionElement view.

Referencing the CustomElementEditor in the associated PropertiesEditionElement

These changes made, we can regenerate the EEF code.

Modifying the generated code

By using CustomElementEditor, EEF generates user code areas in four classes. We need to complete these classes to include our own widget and to bind it with the EMF model. The four classes to modify are :

  • the IPropertiesEditionPart : XXXPropertiesEditionPart
  • the PropertiesEditionPartForm : XXXPropertiesEditionPartForm
  • the PropertiesEditionPartImpl : XXXPropertiesEditionPartImpl
  • the PropertiesEditionComponent : XXXPropertiesEditionComponent

Modifying the IPropertiesEditionPart

The IPropertiesEditionPart interfaces defines the view's getters and setters for each widget. As we used a CustomElementEditor, EEF has generated a user code area in the PersonPropertiesEditionPart :

CustomElementEditor : User code area for PropertiesEditionPart

We need getter and setter for our spinner :

// Start of user code for age specific getters and setters declaration
/**
 * @return the age
 * 
 */
public String getAge();
 
/**
 * Defines a new age
 * @param newValue the new age to set
 * 
 */
public void setAge(String newValue);
// End of user code

Modifying the PropertiesEditionPartForm

The PropertiesEditionPartForm is an EEF view used in a Eclipse Forms Context (properties views, editors, ...). As in the interface, the PropertiesEditionPartForm code contains user code areas where we can include our own widget :

CustomElementEditor : User code areas for PropertiesEditionPartForm

In the first area, we can define the fields we need for our own widget :

// Start of user code for age widgets declarations
protected Spinner age;
// End of user code

In the third area, we can create our getter and setter. In the PropertiesEditionPart we added two methods in the interface : getAge() and setAge(), we have to implement them in this class :

// Start of user code for age specific getters and setters implementation
/**
 * {@inheritDoc}
 * @see customsample.parts.PersonPropertiesEditionPart#getAge()
 */
public String getAge() {
	return String.valueOf(age.getSelection());
}
 
/**
 * {@inheritDoc}
 * @see customsample.parts.PersonPropertiesEditionPart#setAge(String newValue)
 */
public void setAge(String newValue) {
	if (newValue != null) {
		Integer valueOf = Integer.valueOf(newValue);
		age.setSelection(valueOf);
	} 
}
// End of user code

In the second area, we have to invoke the method creating our widget in the editing form :

// Start of user code for age addToPart creation
if (key == CustomsampleViewsRepository.Person.Properties.age) {
	return createAgeSpinner(widgetFactory, parent);
}				
// End of user code

In this sample, we named this method createAgeSpinner. This method is defined in the fourth area. We create our spinner at this point.

// Start of user code additional methods
protected Composite createAgeSpinner(FormToolkit widgetFactory, Composite parent) {
	createDescription(parent, CustomsampleViewsRepository.Person.Properties.age, CustomsampleMessages.PersonPropertiesEditionPart_AgeLabel);
	age = new Spinner(parent, SWT.NONE); //$NON-NLS-1$
	age.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
	widgetFactory.paintBordersFor(parent);
	GridData ageData = new GridData(GridData.FILL_HORIZONTAL);
	age.setLayoutData(ageData);
	FormUtils.createHelpButton(widgetFactory, parent, propertiesEditionComponent.getHelpContent(CustomsampleViewsRepository.Person.Properties.age, CustomsampleViewsRepository.FORM_KIND), null); //$NON-NLS-1$
	return parent;
}
// End of user code

After this step, we can relaunch our plugin to see our new widget in a Form context (basically the properties view) :

CustomElementEditor : Spinner in a form context

Modifying the PropertiesEditionPartImpl

The PropertiesEditionPartImpl is an EEF view used in a standard SWT Context (wizards, dialogs, ...). As in the interface, the PropertiesEditionPartImpl code contains user code areas where we can include our own widget :

CustomElementEditor : User code areas for PropertiesEditionPartImpl

In the first area, we can define the fields we need for our own widget :

// Start of user code for age widgets declarations
protected Spinner age;
// End of user code

In the third area, we can create our getter and setter. In the PropertiesEditionPart we added two methods in the interface : getAge() and setAge(), we have to implement them in this class :

// Start of user code for age specific getters and setters implementation
/**
 * {@inheritDoc}
 * @see customsample.parts.PersonPropertiesEditionPart#getAge()
 */
public String getAge() {
	return String.valueOf(age.getSelection());
}
 
/**
 * {@inheritDoc}
 * @see customsample.parts.PersonPropertiesEditionPart#setAge(String newValue)
 */
public void setAge(String newValue) {
	if (newValue != null) {
		Integer valueOf = Integer.valueOf(newValue);
		age.setSelection(valueOf);
	} 
}
// End of user code

In the second area, we have to invoke the method creating our widget in the editing form :

// Start of user code for age addToPart creation
if (key == CustomsampleViewsRepository.Person.Properties.age) {
	return createAgeSpinner(parent);
}				
// End of user code

In this sample, we named this method createAgeSpinner. This method is defined in the fourth area. We create our spinner at this point.

// Start of user code additional methods
protected Composite createAgeSpinner(Composite parent) {
	createDescription(parent, CustomsampleViewsRepository.Person.Properties.age, CustomsampleMessages.PersonPropertiesEditionPart_AgeLabel);
	age = new Spinner(parent, SWT.BORDER); //$NON-NLS-1$
	GridData ageData = new GridData(GridData.FILL_HORIZONTAL);
	age.setLayoutData(ageData);
	SWTUtils.createHelpButton(parent, propertiesEditionComponent.getHelpContent(CustomsampleViewsRepository.Person.Properties.age, CustomsampleViewsRepository.FORM_KIND), null); //$NON-NLS-1$
	return parent;
}
// End of user code

After this step, we can relaunch our plugin to see our new widget in a Form context (basically the properties view) :

CustomElementEditor : Spinner in a standard swt context

Modifying the PropertiesEditionComponent

At this point, all views contain a spinner to display and edit the age attribute of a Person, but these graphical elements aren't bound to the EMF model. The last part of the tutorial consists in modifying the PropertiesEditionComponent code to enable communication between views and model.

CustomElementEditor : User code areas for component

We have to edit three different areas in order to activate a binding between model and views.

The first method is the initPart method. This method is called when the widgets of the managed view must be initialized with the current value of the model. In our case, we must set the current age of the person in the spinner via the setter we developed in the views.

// Start of user code for age command update
if (isAccessible(CustomsampleViewsRepository.Person.Properties.age)) {
	basePart.setAge(EEFConverterUtil.convertToString(EcorePackage.eINSTANCE.getEInt(), person.getAge()));
}
// End of user code

The second method is the updateSemanticModel method. This method is called when the value of a widget has changed in the view so the new value must be set in the model. These method is implemented like this in our case :

if (CustomsampleViewsRepository.Person.Properties.age == event.getAffectedEditor()) {
	// Start of user code for updateAge method body
	person.setAge((EEFConverterUtil.createIntFromString(EcorePackage.eINSTANCE.getEInt(), (String)event.getNewValue())));
	// End of user code
}

This method takes a IPropertiesEditionEvent as a parameter . This parameter is thrown by the widget in the view, we must add a piece of code to our views to handle this event. In the createAgeSpinner method of both PersonPropertiesEditionPartImpl and PersonPropertiesEditionPartForm classes, we add

age.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(PersonPropertiesEditionPartForm.this, CustomsampleViewsRepository.Person.Properties.age, PropertiesEditionEvent.COMMIT, PropertiesEditionEvent.SET, null, String.valueOf(age.getSelection())));
	}
});

Finally, the last method is the updatePart method. This method is called when a property is changed in the model and we must update the value in the view. The code of this last method is :

// Start of user code for age live update
if (CustomsamplePackage.eINSTANCE.getPerson_Age().equals(msg.getFeature()) && basePart != null && isAccessible(CustomsampleViewsRepository.Person.Properties.age)) {
	if (msg.getNewValue() != null) {
		basePart.setAge(EcoreUtil.convertToString(EcorePackage.eINSTANCE.getEInt(), msg.getNewValue()));
	} else {
		basePart.setAge("");
	}
}
// End of user code

Conclusion

After these steps, our spinner is functional and can be used to set the age of the persons in our UIs.

The CustomElementEditor allows EEF users to integrate any kind of widgets in their editing forms without any change in the EEF generation.

Back to the top