Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStéphane Bégaudeau2016-09-21 15:13:46 +0000
committerPierre-Charles David2016-09-22 12:43:08 +0000
commitd1b8f369e2b34d5a3a969095f23395db7d5d5f7e (patch)
tree465bbf3aeb9fcf5594a138a258c18c936d31f92f
parent23268624e7ae321d80d566dbd776f333ef5b15cd (diff)
downloadorg.eclipse.sirius-d1b8f369e2b34d5a3a969095f23395db7d5d5f7e.tar.gz
org.eclipse.sirius-d1b8f369e2b34d5a3a969095f23395db7d5d5f7e.tar.xz
org.eclipse.sirius-d1b8f369e2b34d5a3a969095f23395db7d5d5f7e.zip
[496057] Add documentation for the creation of custom widgets
Change-Id: Ic70c2008950829adf0793771e1630fc630f8dcfb Signed-off-by: Stéphane Bégaudeau <stephane.begaudeau@obeo.fr>
-rw-r--r--plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget.html41
-rw-r--r--plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget.textile16
-rw-r--r--plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget_advanced.html559
-rw-r--r--plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget_advanced.textile512
-rw-r--r--plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget_basic.html326
-rw-r--r--plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget_basic.textile312
-rw-r--r--plugins/org.eclipse.sirius.doc/doc/toc.xml4
7 files changed, 1770 insertions, 0 deletions
diff --git a/plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget.html b/plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget.html
new file mode 100644
index 0000000000..3983cac98d
--- /dev/null
+++ b/plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget.html
@@ -0,0 +1,41 @@
+<?xml version='1.0' encoding='utf-8' ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <title>extensions-properties_provide_custom_widget</title>
+ <link type="text/css" rel="stylesheet" href="../resources/bootstrap.css"/>
+ <link type="text/css" rel="stylesheet" href="../resources/custom.css"/>
+ </head>
+ <body>
+ <h1 id="SiriusProvideCustomWidgetForThePropertiesView">Sirius &#8211; Provide Custom Widget For The Properties View</h1>
+ <h2 id="Goal">Goal</h2>
+ <p>Using the Properties DSL in the odesign, a specifier can create the content of the Properties view for her/his Sirius-based designers. In some projects, a specifier may require the use of additional widgets not provided by the Sirius Properties runtime. In order to fulfill this need, the Sirius Properties runtime provide two ways for a developer to contribute additional widgets for specifiers.</p>
+ <h2 id="Strategies">Strategies</h2>
+ <p>Two strategies,
+ <a href="extensions-properties_provide_custom_widget_basic.html">
+ <strong>Basic Custom Widget</strong>
+ </a> and
+ <a href="extensions-properties_provide_custom_widget_advanced.html">
+ <strong>Advanced Custom Widget</strong>
+ </a>, exist to create a custom widget depending on the quality of the integration of the custom widget that you want to create.
+ </p>
+ <table class="table table-bordered" style="margin-left:0">
+ <tr>
+ <td></td>
+ <th>Basic Custom Widget</th>
+ <th>Advanced Custom Widget</th>
+ </tr>
+ <tr>
+ <td>Advantages</td>
+ <td>Easier to implement by the developer</td>
+ <td>Exactly the same integration in Sirius as any other concepts</td>
+ </tr>
+ <tr>
+ <td>Drawbacks</td>
+ <td>Poor user interface for the specifier to manipulate</td>
+ <td>Complex to implement by the developer</td>
+ </tr>
+ </table>
+ <p>Those two strategies have their own specific advantages and drawbacks but the &#8220;Advanced Custom Widget&#8221; strategy can developed as a follow-up of a &#8220;Basic Custom Widget&#8221; while keeping most of the code developed for the &#8220;Basic Custom Widget&#8221; approach.</p>
+ </body>
+</html> \ No newline at end of file
diff --git a/plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget.textile b/plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget.textile
new file mode 100644
index 0000000000..8cf80b270e
--- /dev/null
+++ b/plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget.textile
@@ -0,0 +1,16 @@
+h1. Sirius - Provide Custom Widget For The Properties View
+
+h2. Goal
+
+Using the Properties DSL in the odesign, a specifier can create the content of the Properties view for her/his Sirius-based designers. In some projects, a specifier may require the use of additional widgets not provided by the Sirius Properties runtime. In order to fulfill this need, the Sirius Properties runtime provide two ways for a developer to contribute additional widgets for specifiers.
+
+h2. Strategies
+
+Two strategies, "*Basic Custom Widget*":extensions-properties_provide_custom_widget_basic.html and "*Advanced Custom Widget*":extensions-properties_provide_custom_widget_advanced.html, exist to create a custom widget depending on the quality of the integration of the custom widget that you want to create.
+
+table(table table-bordered){margin-left:0}.
+| |_. Basic Custom Widget |_. Advanced Custom Widget |
+| Advantages | Easier to implement by the developer | Exactly the same integration in Sirius as any other concepts |
+| Drawbacks| Poor user interface for the specifier to manipulate | Complex to implement by the developer |
+
+Those two strategies have their own specific advantages and drawbacks but the "Advanced Custom Widget" strategy can developed as a follow-up of a "Basic Custom Widget" while keeping most of the code developed for the "Basic Custom Widget" approach.
diff --git a/plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget_advanced.html b/plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget_advanced.html
new file mode 100644
index 0000000000..1027fe4625
--- /dev/null
+++ b/plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget_advanced.html
@@ -0,0 +1,559 @@
+<?xml version='1.0' encoding='utf-8' ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <title>extensions-properties_provide_custom_widget_advanced</title>
+ <link type="text/css" rel="stylesheet" href="../resources/bootstrap.css"/>
+ <link type="text/css" rel="stylesheet" href="../resources/custom.css"/>
+ </head>
+ <body>
+ <h1 id="SiriusPropertiesAdvancedCustomWidget">Sirius Properties &#8211; Advanced Custom Widget</h1>
+ <h2 id="Goal">Goal</h2>
+ <p>Using the Advanced Custom Widget approach, our goal aims at the creation of a custom widget with a great user experience for the Sirius specifier. This custom widget will be created after the development of the basic custom widget. You should be familiar with this approach first.</p>
+ <h2 id="Strategy">Strategy</h2>
+ <p>In order to create a custom widget, we will have to think about the two kind of users that will interact with our work, the Sirius specifier and the end-user. With this advanced approach, we will create a table widget for the end-user with a great experience for the Sirius specifier who will manipulate it.</p>
+ <h3 id="SpecificationofthecustomwidgetinEclipseSirius">Specification of the custom widget in Eclipse Sirius</h3>
+ <p>The specification of the advanced custom widget will be the main difference with the basic custom widget. While with the basic approach, the Sirius specifier had to use a generic and bland custom widget description, with the advanced approach we will create a real definition for the Sirius specifier to manipulate.</p>
+ <p>As a first step, we want to contribute a piece of model to be displayed in the odesign file for our Sirius specifiers. For that, you will have to create a plugin named
+ <code>com.example.awesomeproject.sirius.properties.ext.widgets.table</code> containing, in a folder named model, an Ecore model named
+ <code>properties-ext-widgets-table.ecore</code>. In this Ecore model, you will have to load the resource
+ <code>properties.ecore</code> used to define the Properties view description in the odesign. This meta-model can be found in the EPackage registry using its NsURI
+ <code>http://www.eclipse.org/sirius/properties/1.0.0</code>.
+ </p>
+ <p>Once this resource has been loaded, give a name, NsPrefix and NsURI to your EPackage (the root element), for example:</p>
+ <ul>
+ <li>Name:
+ <code>propertiesextwidgetstable</code>
+ </li>
+ <li>NsPrefix:
+ <code>properties-ext-widgets-table</code>
+ </li>
+ <li>NsURI:
+ <code>http://www.example.com/awesomeproject/properties/ext/widgets/table</code>
+ </li>
+ </ul>
+ <p>After that, you can create an EClass under the EPackage with the named
+ <code>ExtTableDescription</code> and with the EClass
+ <code>WidgetDescription</code> as a supertype. This EClass should contain for our example two EAttributes named
+ <code>onClickExpression</code> and
+ <code>valueExpression</code> and both should have the type
+ <code>InterpretedExpresion</code>.
+ </p>
+ <p>You can then create a genmodel file for your Ecore model in the same repository and launch the generation of the model and its edit support. You will thus have two plugins:</p>
+ <ul>
+ <li>Sirius Properties model extension:
+ <code>com.example.awesomeproject.sirius.properties.ext.widgets.table</code>
+ </li>
+ <li>Sirius Properties model extension Edit support:
+ <code>com.example.awesomeproject.sirius.properties.ext.widgets.table.edit</code>
+ </li>
+ </ul>
+ <h3 id="SpecificationofthecustomwidgetinEclipseEEF">Specification of the custom widget in Eclipse EEF</h3>
+ <p>Eclipse EEF has a runtime independent of Eclipse Sirius and as such the definition of the widget that exist in the odesign file has to be transformed into a definition that can be maintained by Eclipse EEF. For most of the existing concepts this transformation is very basic since it only involves the transformation of the Sirius concepts directly into similar EEF concepts but both domain specific languages have very different roles.</p>
+ <p>The Eclipse Sirius Properties DSL is used as the user interface for the Eclipse Sirius specifier while the Eclipse EEF DSL is the model interpreted by the runtime. It is perfectly possible, and even recommended, to define, in the extension of the Sirius Properties DSL, high level concepts that should be user-friendly for the Sirius specifier and to keep in the extension of the EEF DSL raw concepts transformed from the extension of the Sirius Properties DSL.</p>
+ <p>You will now have to create a third plugin named
+ <code>com.example.awesomeproject.eef.ext.widgets.table</code> with a model folder and an Ecore model inside named
+ <code>eef-ext-widgets-table.ecore</code>. In this model you should load the resource
+ <code>eef.ecore</code> containing the EEF DSL that you will have to extend. You can find it in the EPackage registry using its NsURI
+ <code>http://www.eclipse.org/eef</code>. Once the EEF DSL is loaded, you can set the properties of your root EPackage:
+ </p>
+ <ul>
+ <li>Name:
+ <code>eefextwidgetstable</code>
+ </li>
+ <li>NsPrefix:
+ <code>eef-ext-widgets-table</code>
+ </li>
+ <li>NsURI:
+ <code>http://www.example.com/awesomeproject/eef/ext/widgets/table</code>
+ </li>
+ </ul>
+ <p>Under this root EPackage, you can now create your EClass named
+ <code>EEFExtTableDescription</code> which should have
+ <code>EEFWidgetDescription</code> as a supertype. This EClass should contain for our example two EAttributes named
+ <code>onClickExpression</code> and
+ <code>valueExpression</code> and both should have the type
+ <code>EString</code>. In our example, both the extension of the Sirius DSL and the EEF DSL have the same properties since we are using a very simple example.
+ </p>
+ <p>You can now create a genmodel for your extension of the EEF DSL and generate the code for the model (the Edit support is not necessary here). You will now have three plugins:</p>
+ <ul>
+ <li>Sirius Properties model extension:
+ <code>com.example.awesomeproject.sirius.properties.ext.widgets.table</code>
+ </li>
+ <li>Sirius Properties model extension Edit support:
+ <code>com.example.awesomeproject.sirius.properties.ext.widgets.table.edit</code>
+ </li>
+ <li>EEF model extension:
+ <code>com.example.awesomeproject.eef.ext.widgets.table</code>
+ </li>
+ </ul>
+ <h3 id="ContributionoftheconverterfromSiriustoEEF">Contribution of the converter from Sirius to EEF</h3>
+ <p>Now that you have your extension to the Sirius Properties DSL and the EEF DSL, you need to inform the Sirius bridge how to transform the concepts from your extension to the Sirius DSL into EEF concepts. For that an extension point is available in order to contribute an IDescriptionConverter. This converter will have to depend on the Sirius bridge for Eclipse EEF and at least the extension to both the Sirius Properties DSL and the EEF DSL. As a result, this code will have to go into another plugin because having those dependencies on an existing plugins would be against the best practices. We will thus create a fourth plugin named
+ <code>com.example.awesomeproject.sirius.ui.properties.ext.widgets.table</code>. In this plugin, we will declare our description converter using the following extension.
+ </p>
+ <pre>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
+&lt;?eclipse version="3.4"?&gt;
+&lt;plugin&gt;
+ &lt;extension
+ point="org.eclipse.sirius.ui.properties.descriptionConverter"&gt;
+ &lt;descriptor
+ class="com.example.awesomeproject.sirius.ui.properties.ext.widgets.table.internal.ExtTableDescriptionConverter"
+ description="%tableDescriptionConverter.Description"
+ id="com.example.awesomeproject.sirius.ui.properties.ext.widgets.table.descriptionConverter"
+ label="%tableDescriptionConverter.Label"&gt;
+ &lt;/descriptor&gt;
+ &lt;/extension&gt;
+&lt;/plugin&gt;
+
+</pre>
+ <p>
+ <br/>The transformation from the Sirius DSL to the EEF DSL occur in two parts, first the description are converted using an IDescriptionConverter and after an IDescriptionLinkResolver may be used to resolve some links. In our case, the description converter will simply be used to transform a instance of
+ <code>ExtTableDescription</code> into an
+ <code>EEFExtTableDescription</code> and we won&#8217;t use a description link resolver since it is not useful here.
+ </p>
+ <pre>package com.example.awesomeproject.sirius.ui.properties.ext.widgets.table.internal;
+
+import java.util.Map;
+
+import com.example.awesomeproject.eef.ext.widgets.table.EEFExtTableDescription;
+import com.example.awesomeproject.eef.ext.widgets.table.EefExtWidgetsTableFactory;
+import org.eclipse.emf.ecore.EObject;
+import com.example.awesomeproject.sirius.properties.ext.widgets.table.ExtTableDescription;
+import org.eclipse.sirius.ui.properties.api.DescriptionCache;
+import org.eclipse.sirius.ui.properties.api.IDescriptionConverter;
+
+public class ExtTableDescriptionConverter implements IDescriptionConverter {
+
+ @Override
+ public boolean canHandle(EObject description) {
+ return description instanceof ExtTableDescription;
+ }
+
+ @Override
+ public EObject convert(EObject description, Map&lt;String, Object&gt; parameters, DescriptionCache cache) {
+ if (description instanceof ExtTableDescription) {
+ ExtTableDescription extTableDescription = (ExtTableDescription) description;
+
+ EEFExtTableDescription eefExtTableDescription = EefExtWidgetsTableFactory.eINSTANCE.createEEFExtTableDescription();
+ eefExtTableDescription.setIdentifier(extTableDescription.getIdentifier());
+ eefExtTableDescription.setHelpExpression(extTableDescription.getHelpExpression());
+ eefExtTableDescription.setIsEnabledExpression(extTableDescription.getIsEnabledExpression());
+ eefExtTableDescription.setLabelExpression(extTableDescription.getLabelExpression());
+
+ eefExtTableDescription.setValueExpression(extTableDescription.getValueExpression());
+ eefExtTableDescription.setOnClickExpression(extTableDescription.getOnClickExpression());
+
+ // Let's not forget to populate the cache for the other converters or link resolvers
+ cache.put(extTableDescription, eefExtTableDescription);
+
+ return eefExtTableDescription;
+ }
+ return null;
+ }
+}
+
+</pre>
+ <p>
+ <br/>An IDescriptionConverter can be used to convert any element from the Sirius Properties DSL into an EEF DSL element. In order to support custom widgets, you only have to handle your own objects but you could still modify other kind of objects from the Sirius Properties DSL. As a result, this mechanism can be used to dynamically modify anything in the description of the Properties view at runtime. This usage of the IDescriptionConverter should almost never be used, it&#8217;s a very powerful mechanism for extremely advanced users only.
+ </p>
+ <p>In order to help developer transform their Sirius entities into EEF entities, multiple possible super-classes are available:</p>
+ <ul>
+ <li>org.eclipse.sirius.ui.properties.api.AbstractDescriptionConverter &#8211; Common superclass with utility methods</li>
+ <li>org.eclipse.sirius.ui.properties.api.DefaultDescriptionConverter &#8211; Converter for basic transformations</li>
+ <li>org.eclipse.sirius.ui.properties.api.DefaultDescriptionWithInitialOperationConverter &#8211; Converter with support for the transformation of a basic operation into a regular expression</li>
+ <li>org.eclipse.sirius.ui.properties.api.DefaultStyleDescriptionConverter &#8211; Converter with support for styling</li>
+ </ul>
+ <h3 id="ContributionofthePropertiessectionfortheodesign">Contribution of the Properties section for the odesign</h3>
+ <p>If you try to edit your extension of the Sirius DSL in the odesign editor, you will find out that it does not work. In order to be able to view the properties of your widget, in our case the
+ <code>onClickExpression</code> and the
+ <code>valueExpression</code>, you will need to contribute to the Properties view of the odesign editor. Since this contribution will require a dependency with the framework used for the Properties view of the odesign editor, it will require another plugin named
+ <code>com.example.awesomeproject.sirius.editor.properties.ext.widgets.table</code> with a dependency to at least:
+ </p>
+ <ul>
+ <li>com.example.awesomeproject.sirius.properties.ext.widgets.table</li>
+ <li>com.example.awesomeproject.sirius.properties.ext.widgets.table.edit</li>
+ <li>org.eclipse.ui.views.properties.tabbed</li>
+ <li>org.eclipse.sirius.editor</li>
+ </ul>
+ <pre>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
+&lt;?eclipse version="3.4"?&gt;
+&lt;plugin&gt;
+ &lt;extension
+ point="org.eclipse.ui.views.properties.tabbed.propertySections"&gt;
+ &lt;propertySections
+ contributorId="org.eclipse.sirius.editor.editorPlugin.SiriusEditorContributor"&gt;
+ &lt;propertySection
+ afterSection="properties.section.widgetDescription.IsEnabledExpression"
+ class="com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.internal.ExtTableDescriptionValueExpressionPropertySection"
+ filter="com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.internal.ExtTableDescriptionValueExpressionFilter"
+ id="properties.section.extTableDescription.valueExpression"
+ tab="viewpoint.tab.general"&gt;
+ &lt;input
+ type="com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.propertiesextwidgetstablee.ExtTableDescription"&gt;
+ &lt;/input&gt;
+ &lt;/propertySection&gt;
+ &lt;propertySection
+ afterSection="properties.section.extTableDescription.valueExpression"
+ class="com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.internal.ExtTableDescriptionOnClickExpressionPropertySection"
+ filter="com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.internal.ExtTableDescriptionOnClickExpressionFilter"
+ id="properties.section.extTableDescription.valueExpression"
+ tab="viewpoint.tab.general"&gt;
+ &lt;input
+ type="com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.propertiesextwidgetstablee.ExtTableDescription"&gt;
+ &lt;/input&gt;
+ &lt;/propertySection&gt;
+ &lt;/propertySections&gt;
+ &lt;/extension&gt;
+&lt;/plugin&gt;
+
+
+</pre>
+ <p>
+ <br/>Two property sections will be necessary in order to display a text field for the value expression and the on click expression.
+ </p>
+ <pre>package com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.internal;
+
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.sirius.editor.properties.filters.common.ViewpointPropertyFilter;
+import com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.propertiesextwidgetstablee.PropertiesExtTablePackage;
+
+public class ExtTableDescriptionOnClickExpressionFilter extends ViewpointPropertyFilter {
+
+ @Override
+ protected EStructuralFeature getFeature() {
+ return PropertiesExtTablePackage.eINSTANCE.getExtTableDescription_OnClickExpression();
+ }
+
+ @Override
+ protected boolean isRightInputType(Object arg0) {
+ return arg0 instanceof com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.propertiesextwidgetstablee.ExtTableDescription;
+ }
+
+}
+
+</pre>
+ <p>
+ <br/>Both filter will look almost the same, the only difference will be the structural feature that they will return.
+ </p>
+ <pre>package com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.internal;
+
+import org.eclipse.emf.ecore.EAttribute;
+import org.eclipse.sirius.editor.editorPlugin.SiriusEditor;
+import org.eclipse.sirius.editor.properties.sections.common.AbstractTextWithButtonPropertySection;
+import org.eclipse.sirius.editor.tools.api.assist.TypeContentProposalProvider;
+import org.eclipse.sirius.editor.tools.internal.presentation.TextWithContentProposalDialog;
+import com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.propertiesextwidgetstablee.PropertiesExtTablePackage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
+
+@SuppressWarnings("restriction")
+public class ExtTableDescriptionOnClickExpressionPropertySection extends AbstractTextWithButtonPropertySection {
+
+ @Override
+ protected String getDefaultLabelText() {
+ return "On Click Expression"; //$NON-NLS-1$
+ }
+
+ @Override
+ protected String getLabelText() {
+ String labelText;
+ labelText = super.getLabelText() + "*:"; //$NON-NLS-1$
+ return labelText;
+ }
+
+ @Override
+ public EAttribute getFeature() {
+ return PropertiesExtTablePackage.eINSTANCE.getExtTableDescription_OnClickExpression();
+ }
+
+ @Override
+ protected Object getFeatureValue(String newText) {
+ return newText;
+ }
+
+ @Override
+ protected boolean isEqual(String newText) {
+ return this.getFeatureAsText().equals(newText);
+ }
+
+ @Override
+ public void createControls(Composite parent, TabbedPropertySheetPage tabbedPropertySheetPage) {
+ super.createControls(parent, tabbedPropertySheetPage);
+
+ text.setToolTipText(getToolTipText());
+ /*
+ * We set the color as it's a InterpretedExpression
+ */
+ text.setBackground(SiriusEditor.getColorRegistry().get("yellow")); //$NON-NLS-1$
+
+ TypeContentProposalProvider.bindPluginsCompletionProcessors(this, text);
+
+ FormData data = new FormData();
+ data.top = new FormAttachment(text, 0, SWT.TOP);
+ data.left = new FormAttachment(nameLabel);
+
+ nameLabel.setFont(SiriusEditor.getFontRegistry().get("required")); //$NON-NLS-1$
+ }
+
+ @Override
+ protected SelectionListener createButtonListener() {
+ return new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ TextWithContentProposalDialog dialog = new TextWithContentProposalDialog(composite.getShell(), ExtTableDescriptionOnClickExpressionPropertySection.this, text.getText());
+ dialog.open();
+ text.setText(dialog.getResult());
+ handleTextModified();
+ }
+ };
+ }
+
+ @Override
+ protected String getPropertyDescription() {
+ return ""; //$NON-NLS-1$
+ }
+
+}
+
+
+</pre>
+ <p>
+ <br/>This property section will create a text field and label in the Properties view of the odesign editor in order to let the Sirius specifier edit the
+ <code>onClickExpression</code> of the table. Our text field will have the same appear and feature as the regular yellow text fields used by Sirius.
+ </p>
+ <h3 id="Updatetothepluginsofourbasicwidgetapproach">Update to the plugins of our basic widget approach</h3>
+ <p>Now we can reuse our work in the basic approach but this time with our own specific DSL instead of reusing the CustomElements from the EEF DSL. First we will have to modify the lifecycle manager provider to support our EEFExtTableDescription.</p>
+ <pre>package com.example.awesomeproject.eef.ide.ui.ext.widgets.table.internal;
+
+import com.example.awesomeproject.eef.ext.widgets.table.EEFExtTableDescription;
+
+import org.eclipse.eef.EEFControlDescription;
+import org.eclipse.eef.core.api.EditingContextAdapter;
+import org.eclipse.eef.ide.ui.api.widgets.IEEFLifecycleManager;
+import org.eclipse.eef.ide.ui.api.widgets.IEEFLifecycleManagerProvider;
+import org.eclipse.sirius.common.interpreter.api.IInterpreter;
+import org.eclipse.sirius.common.interpreter.api.IVariableManager;
+
+public class TableLifecycleManagerProvider implements IEEFLifecycleManagerProvider {
+
+ @Override
+ public boolean canHandle(EEFControlDescription controlDescription) {
+ return controlDescription instanceof EEFExtTableDescription;
+ }
+
+ @Override
+ public IEEFLifecycleManager getLifecycleManager(EEFControlDescription controlDescription, IVariableManager variableManager,
+ IInterpreter interpreter, EditingContextAdapter contextAdapter) {
+ if (controlDescription instanceof EEFExtTableDescription) {
+ return new TableLifecycleManager((EEFExtTableDescription) controlDescription, variableManager, interpreter, contextAdapter);
+ }
+ throw new IllegalArgumentException();
+ }
+}
+
+</pre>
+ <p>
+ <br/>Now we can just modify our lifecycle manager to support our EEFExtTableDescription.
+ </p>
+ <pre>package com.example.awesomeproject.eef.ide.ui.ext.widgets.table.internal;
+
+import com.example.awesomeproject.eef.ext.widgets.table.EEFExtTableDescription;
+
+import com.example.awesomeproject.eef.core.ext.widgets.table.TableController;
+import org.eclipse.eef.EEFWidgetDescription;
+import org.eclipse.eef.common.ui.api.IEEFFormContainer;
+import org.eclipse.eef.core.api.EditingContextAdapter;
+import org.eclipse.eef.core.api.controllers.IConsumer;
+import org.eclipse.eef.core.api.controllers.IEEFWidgetController;
+import org.eclipse.eef.ide.ui.api.widgets.AbstractEEFWidgetLifecycleManager;
+import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
+import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.sirius.common.interpreter.api.IInterpreter;
+import org.eclipse.sirius.common.interpreter.api.IVariableManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Table;
+
+public class TableLifecycleManager extends AbstractEEFWidgetLifecycleManager {
+
+ private EEFExtTableDescription description;
+
+ private TableViewer tableViewer;
+
+ private ComposedAdapterFactory composedAdapterFactory;
+
+ private SelectionListener onClickListener;
+
+ private TableController controller;
+
+ private IConsumer&lt;Object&gt; newValueConsumer;
+
+ public TableLifecycleManager(EEFExtTableDescription description, IVariableManager variableManager, IInterpreter interpreter,
+ EditingContextAdapter contextAdapter) {
+ super(variableManager, interpreter, contextAdapter);
+ this.description = description;
+ }
+
+ @Override
+ protected void createMainControl(Composite parent, IEEFFormContainer formContainer) {
+ Table table = formContainer.getWidgetFactory().createTable(parent,
+ SWT.READ_ONLY | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER | SWT.SINGLE);
+ this.tableViewer = new TableViewer(table);
+ this.composedAdapterFactory = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
+
+ this.tableViewer.setContentProvider(ArrayContentProvider.getInstance());
+ this.tableViewer.setLabelProvider(new DelegatingStyledCellLabelProvider(new AdapterFactoryLabelProvider.StyledLabelProvider(
+ this.composedAdapterFactory, this.tableViewer)));
+
+ this.controller = new TableController(description, variableManager, interpreter, contextAdapter);
+ }
+
+ @Override
+ public void aboutToBeShown() {
+ super.aboutToBeShown();
+
+ this.newValueConsumer = (newValue) -&gt; this.tableViewer.setInput(newValue);
+ this.controller.onNewValue(this.newValueConsumer);
+
+ this.onClickListener = new SelectionListener() {
+ @Override
+ public void widgetSelected(SelectionEvent event) {
+ Object selection = ((IStructuredSelection) TableLifecycleManager.this.tableViewer.getSelection()).getFirstElement();
+ TableLifecycleManager.this.controller.handleClick(selection);
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent event) {
+ Object selection = ((IStructuredSelection) TableLifecycleManager.this.tableViewer.getSelection()).getFirstElement();
+ TableLifecycleManager.this.controller.handleClick(selection);
+ }
+ };
+ this.tableViewer.getTable().addSelectionListener(this.onClickListener);
+ }
+
+ @Override
+ public void refresh() {
+ super.refresh();
+
+ this.controller.refresh();
+ }
+
+ @Override
+ public void aboutToBeHidden() {
+ super.aboutToBeHidden();
+ this.controller.removeValueConsumer();
+ this.newValueConsumer = null;
+
+ this.tableViewer.getTable().removeSelectionListener(this.onClickListener);
+ this.onClickListener = null;
+ }
+
+ @Override
+ protected IEEFWidgetController getController() {
+ return this.controller;
+ }
+
+ @Override
+ protected EEFWidgetDescription getWidgetDescription() {
+ return this.description;
+ }
+
+ @Override
+ protected Control getValidationControl() {
+ return this.tableViewer.getTable();
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+
+ this.composedAdapterFactory.dispose();
+ }
+}
+
+
+</pre>
+ <p>
+ <br/>And finally we can easily adapter our controller.
+ </p>
+ <pre>package com.example.awesomeproject.eef.core.ext.widgets.table.internal;
+
+import com.example.awesomeproject.eef.ext.widgets.table.EEFExtTableDescription;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.eef.EEFCustomWidgetDescription;
+import org.eclipse.eef.core.api.EditingContextAdapter;
+import org.eclipse.eef.core.api.controllers.AbstractEEFCustomWidgetController;
+import org.eclipse.eef.core.api.controllers.IConsumer;
+import org.eclipse.eef.core.api.utils.EvalFactory;
+import org.eclipse.sirius.common.interpreter.api.IInterpreter;
+import org.eclipse.sirius.common.interpreter.api.IVariableManager;
+
+public class TableController extends AbstractEEFWidgetController {
+
+ private static final String VALUE_EXPRESSION_ID = "valueExpression"; //$NON-NLS-1$
+
+ private static final String ON_CLICK_EXPRESSION_ID = "onClickExpression"; //$NON-NLS-1$
+
+ private static final String SELECTION_VARIABLE_NAME = "selection"; //$NON-NLS-1$
+
+ private IConsumer&lt;Object&gt; newValueConsumer;
+
+ public TableController(EEFExtTableDescription description, IVariableManager variableManager, IInterpreter interpreter,
+ EditingContextAdapter contextAdapter) {
+ super(description, variableManager, interpreter, contextAdapter);
+ }
+
+ @Override
+ protected EEFExtTableDescription getDescription() {
+ return this.description;
+ }
+
+ @Override
+ public void refresh() {
+ super.refresh();
+ this.newEval().call(this.description.getValueExpression(), this.newValueConsumer);
+ }
+
+ public void handleClick(Object object) {
+ this.contextAdapter.performModelChange(() -&gt; {
+ String onClickExpression = this.description.getOnClickExpression();
+
+ Map&lt;String, Object&gt; variables = new HashMap&lt;String, Object&gt;();
+ variables.putAll(this.variableManager.getVariables());
+ variables.put(SELECTION_VARIABLE_NAME, object);
+
+ EvalFactory.of(this.interpreter, variables).call(onClickExpression);
+ });
+ }
+
+ public void onNewValue(IConsumer&lt;Object&gt; consumer) {
+ this.newValueConsumer = consumer;
+ }
+
+ public void removeValueConsumer() {
+ this.newValueConsumer = null;
+ }
+}
+
+</pre>
+ <p>
+ <br/>With minimal changes, we can reuse the code of the basic approach in this advanced approach. But now we have the ability to provide the Sirius specifiers with a proper user interface for the manipulation of our widget and we can manipulate our own concepts in the EEF runtime.
+ </p>
+ </body>
+</html> \ No newline at end of file
diff --git a/plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget_advanced.textile b/plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget_advanced.textile
new file mode 100644
index 0000000000..54aa56bd50
--- /dev/null
+++ b/plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget_advanced.textile
@@ -0,0 +1,512 @@
+h1. Sirius Properties - Advanced Custom Widget
+
+h2. Goal
+
+Using the Advanced Custom Widget approach, our goal aims at the creation of a custom widget with a great user experience for the Sirius specifier. This custom widget will be created after the development of the basic custom widget. You should be familiar with this approach first.
+
+h2. Strategy
+
+In order to create a custom widget, we will have to think about the two kind of users that will interact with our work, the Sirius specifier and the end-user. With this advanced approach, we will create a table widget for the end-user with a great experience for the Sirius specifier who will manipulate it.
+
+h3. Specification of the custom widget in Eclipse Sirius
+
+The specification of the advanced custom widget will be the main difference with the basic custom widget. While with the basic approach, the Sirius specifier had to use a generic and bland custom widget description, with the advanced approach we will create a real definition for the Sirius specifier to manipulate.
+
+As a first step, we want to contribute a piece of model to be displayed in the odesign file for our Sirius specifiers. For that, you will have to create a plugin named @com.example.awesomeproject.sirius.properties.ext.widgets.table@ containing, in a folder named model, an Ecore model named @properties-ext-widgets-table.ecore@. In this Ecore model, you will have to load the resource @properties.ecore@ used to define the Properties view description in the odesign. This meta-model can be found in the EPackage registry using its NsURI @http://www.eclipse.org/sirius/properties/1.0.0@.
+
+Once this resource has been loaded, give a name, NsPrefix and NsURI to your EPackage (the root element), for example:
+* Name: @propertiesextwidgetstable@
+* NsPrefix: @properties-ext-widgets-table@
+* NsURI: @http://www.example.com/awesomeproject/properties/ext/widgets/table@
+
+After that, you can create an EClass under the EPackage with the named @ExtTableDescription@ and with the EClass @WidgetDescription@ as a supertype. This EClass should contain for our example two EAttributes named @onClickExpression@ and @valueExpression@ and both should have the type @InterpretedExpresion@.
+
+
+You can then create a genmodel file for your Ecore model in the same repository and launch the generation of the model and its edit support. You will thus have two plugins:
+* Sirius Properties model extension: @com.example.awesomeproject.sirius.properties.ext.widgets.table@
+* Sirius Properties model extension Edit support: @com.example.awesomeproject.sirius.properties.ext.widgets.table.edit@
+
+h3. Specification of the custom widget in Eclipse EEF
+
+Eclipse EEF has a runtime independent of Eclipse Sirius and as such the definition of the widget that exist in the odesign file has to be transformed into a definition that can be maintained by Eclipse EEF. For most of the existing concepts this transformation is very basic since it only involves the transformation of the Sirius concepts directly into similar EEF concepts but both domain specific languages have very different roles.
+
+The Eclipse Sirius Properties DSL is used as the user interface for the Eclipse Sirius specifier while the Eclipse EEF DSL is the model interpreted by the runtime. It is perfectly possible, and even recommended, to define, in the extension of the Sirius Properties DSL, high level concepts that should be user-friendly for the Sirius specifier and to keep in the extension of the EEF DSL raw concepts transformed from the extension of the Sirius Properties DSL.
+
+You will now have to create a third plugin named @com.example.awesomeproject.eef.ext.widgets.table@ with a model folder and an Ecore model inside named @eef-ext-widgets-table.ecore@. In this model you should load the resource @eef.ecore@ containing the EEF DSL that you will have to extend. You can find it in the EPackage registry using its NsURI @http://www.eclipse.org/eef@. Once the EEF DSL is loaded, you can set the properties of your root EPackage:
+* Name: @eefextwidgetstable@
+* NsPrefix: @eef-ext-widgets-table@
+* NsURI: @http://www.example.com/awesomeproject/eef/ext/widgets/table@
+
+Under this root EPackage, you can now create your EClass named @EEFExtTableDescription@ which should have @EEFWidgetDescription@ as a supertype. This EClass should contain for our example two EAttributes named @onClickExpression@ and @valueExpression@ and both should have the type @EString@. In our example, both the extension of the Sirius DSL and the EEF DSL have the same properties since we are using a very simple example.
+
+You can now create a genmodel for your extension of the EEF DSL and generate the code for the model (the Edit support is not necessary here). You will now have three plugins:
+* Sirius Properties model extension: @com.example.awesomeproject.sirius.properties.ext.widgets.table@
+* Sirius Properties model extension Edit support: @com.example.awesomeproject.sirius.properties.ext.widgets.table.edit@
+* EEF model extension: @com.example.awesomeproject.eef.ext.widgets.table@
+
+h3. Contribution of the converter from Sirius to EEF
+
+Now that you have your extension to the Sirius Properties DSL and the EEF DSL, you need to inform the Sirius bridge how to transform the concepts from your extension to the Sirius DSL into EEF concepts. For that an extension point is available in order to contribute an IDescriptionConverter. This converter will have to depend on the Sirius bridge for Eclipse EEF and at least the extension to both the Sirius Properties DSL and the EEF DSL. As a result, this code will have to go into another plugin because having those dependencies on an existing plugins would be against the best practices. We will thus create a fourth plugin named @com.example.awesomeproject.sirius.ui.properties.ext.widgets.table@. In this plugin, we will declare our description converter using the following extension.
+
+
+pre..
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension
+ point="org.eclipse.sirius.ui.properties.descriptionConverter">
+ <descriptor
+ class="com.example.awesomeproject.sirius.ui.properties.ext.widgets.table.internal.ExtTableDescriptionConverter"
+ description="%tableDescriptionConverter.Description"
+ id="com.example.awesomeproject.sirius.ui.properties.ext.widgets.table.descriptionConverter"
+ label="%tableDescriptionConverter.Label">
+ </descriptor>
+ </extension>
+</plugin>
+
+p.
+The transformation from the Sirius DSL to the EEF DSL occur in two parts, first the description are converted using an IDescriptionConverter and after an IDescriptionLinkResolver may be used to resolve some links. In our case, the description converter will simply be used to transform a instance of @ExtTableDescription@ into an @EEFExtTableDescription@ and we won't use a description link resolver since it is not useful here.
+
+
+pre..
+package com.example.awesomeproject.sirius.ui.properties.ext.widgets.table.internal;
+
+import java.util.Map;
+
+import com.example.awesomeproject.eef.ext.widgets.table.EEFExtTableDescription;
+import com.example.awesomeproject.eef.ext.widgets.table.EefExtWidgetsTableFactory;
+import org.eclipse.emf.ecore.EObject;
+import com.example.awesomeproject.sirius.properties.ext.widgets.table.ExtTableDescription;
+import org.eclipse.sirius.ui.properties.api.DescriptionCache;
+import org.eclipse.sirius.ui.properties.api.IDescriptionConverter;
+
+public class ExtTableDescriptionConverter implements IDescriptionConverter {
+
+ @Override
+ public boolean canHandle(EObject description) {
+ return description instanceof ExtTableDescription;
+ }
+
+ @Override
+ public EObject convert(EObject description, Map<String, Object> parameters, DescriptionCache cache) {
+ if (description instanceof ExtTableDescription) {
+ ExtTableDescription extTableDescription = (ExtTableDescription) description;
+
+ EEFExtTableDescription eefExtTableDescription = EefExtWidgetsTableFactory.eINSTANCE.createEEFExtTableDescription();
+ eefExtTableDescription.setIdentifier(extTableDescription.getIdentifier());
+ eefExtTableDescription.setHelpExpression(extTableDescription.getHelpExpression());
+ eefExtTableDescription.setIsEnabledExpression(extTableDescription.getIsEnabledExpression());
+ eefExtTableDescription.setLabelExpression(extTableDescription.getLabelExpression());
+
+ eefExtTableDescription.setValueExpression(extTableDescription.getValueExpression());
+ eefExtTableDescription.setOnClickExpression(extTableDescription.getOnClickExpression());
+
+ // Let's not forget to populate the cache for the other converters or link resolvers
+ cache.put(extTableDescription, eefExtTableDescription);
+
+ return eefExtTableDescription;
+ }
+ return null;
+ }
+}
+
+p.
+An IDescriptionConverter can be used to convert any element from the Sirius Properties DSL into an EEF DSL element. In order to support custom widgets, you only have to handle your own objects but you could still modify other kind of objects from the Sirius Properties DSL. As a result, this mechanism can be used to dynamically modify anything in the description of the Properties view at runtime. This usage of the IDescriptionConverter should almost never be used, it's a very powerful mechanism for extremely advanced users only.
+
+In order to help developer transform their Sirius entities into EEF entities, multiple possible super-classes are available:
+
+* org.eclipse.sirius.ui.properties.api.AbstractDescriptionConverter - Common superclass with utility methods
+* org.eclipse.sirius.ui.properties.api.DefaultDescriptionConverter - Converter for basic transformations
+* org.eclipse.sirius.ui.properties.api.DefaultDescriptionWithInitialOperationConverter - Converter with support for the transformation of a basic operation into a regular expression
+* org.eclipse.sirius.ui.properties.api.DefaultStyleDescriptionConverter - Converter with support for styling
+
+h3. Contribution of the Properties section for the odesign
+
+If you try to edit your extension of the Sirius DSL in the odesign editor, you will find out that it does not work. In order to be able to view the properties of your widget, in our case the @onClickExpression@ and the @valueExpression@, you will need to contribute to the Properties view of the odesign editor. Since this contribution will require a dependency with the framework used for the Properties view of the odesign editor, it will require another plugin named @com.example.awesomeproject.sirius.editor.properties.ext.widgets.table@ with a dependency to at least:
+
+* com.example.awesomeproject.sirius.properties.ext.widgets.table
+* com.example.awesomeproject.sirius.properties.ext.widgets.table.edit
+* org.eclipse.ui.views.properties.tabbed
+* org.eclipse.sirius.editor
+
+pre..
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension
+ point="org.eclipse.ui.views.properties.tabbed.propertySections">
+ <propertySections
+ contributorId="org.eclipse.sirius.editor.editorPlugin.SiriusEditorContributor">
+ <propertySection
+ afterSection="properties.section.widgetDescription.IsEnabledExpression"
+ class="com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.internal.ExtTableDescriptionValueExpressionPropertySection"
+ filter="com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.internal.ExtTableDescriptionValueExpressionFilter"
+ id="properties.section.extTableDescription.valueExpression"
+ tab="viewpoint.tab.general">
+ <input
+ type="com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.propertiesextwidgetstablee.ExtTableDescription">
+ </input>
+ </propertySection>
+ <propertySection
+ afterSection="properties.section.extTableDescription.valueExpression"
+ class="com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.internal.ExtTableDescriptionOnClickExpressionPropertySection"
+ filter="com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.internal.ExtTableDescriptionOnClickExpressionFilter"
+ id="properties.section.extTableDescription.valueExpression"
+ tab="viewpoint.tab.general">
+ <input
+ type="com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.propertiesextwidgetstablee.ExtTableDescription">
+ </input>
+ </propertySection>
+ </propertySections>
+ </extension>
+</plugin>
+
+
+p.
+Two property sections will be necessary in order to display a text field for the value expression and the on click expression.
+
+pre..
+package com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.internal;
+
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.sirius.editor.properties.filters.common.ViewpointPropertyFilter;
+import com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.propertiesextwidgetstablee.PropertiesExtTablePackage;
+
+public class ExtTableDescriptionOnClickExpressionFilter extends ViewpointPropertyFilter {
+
+ @Override
+ protected EStructuralFeature getFeature() {
+ return PropertiesExtTablePackage.eINSTANCE.getExtTableDescription_OnClickExpression();
+ }
+
+ @Override
+ protected boolean isRightInputType(Object arg0) {
+ return arg0 instanceof com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.propertiesextwidgetstablee.ExtTableDescription;
+ }
+
+}
+
+p.
+Both filter will look almost the same, the only difference will be the structural feature that they will return.
+
+pre..
+package com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.internal;
+
+import org.eclipse.emf.ecore.EAttribute;
+import org.eclipse.sirius.editor.editorPlugin.SiriusEditor;
+import org.eclipse.sirius.editor.properties.sections.common.AbstractTextWithButtonPropertySection;
+import org.eclipse.sirius.editor.tools.api.assist.TypeContentProposalProvider;
+import org.eclipse.sirius.editor.tools.internal.presentation.TextWithContentProposalDialog;
+import com.example.awesomeproject.sirius.editor.properties.ext.widgets.table.propertiesextwidgetstablee.PropertiesExtTablePackage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
+
+@SuppressWarnings("restriction")
+public class ExtTableDescriptionOnClickExpressionPropertySection extends AbstractTextWithButtonPropertySection {
+
+ @Override
+ protected String getDefaultLabelText() {
+ return "On Click Expression"; //$NON-NLS-1$
+ }
+
+ @Override
+ protected String getLabelText() {
+ String labelText;
+ labelText = super.getLabelText() + "*:"; //$NON-NLS-1$
+ return labelText;
+ }
+
+ @Override
+ public EAttribute getFeature() {
+ return PropertiesExtTablePackage.eINSTANCE.getExtTableDescription_OnClickExpression();
+ }
+
+ @Override
+ protected Object getFeatureValue(String newText) {
+ return newText;
+ }
+
+ @Override
+ protected boolean isEqual(String newText) {
+ return this.getFeatureAsText().equals(newText);
+ }
+
+ @Override
+ public void createControls(Composite parent, TabbedPropertySheetPage tabbedPropertySheetPage) {
+ super.createControls(parent, tabbedPropertySheetPage);
+
+ text.setToolTipText(getToolTipText());
+ /*
+ * We set the color as it's a InterpretedExpression
+ */
+ text.setBackground(SiriusEditor.getColorRegistry().get("yellow")); //$NON-NLS-1$
+
+ TypeContentProposalProvider.bindPluginsCompletionProcessors(this, text);
+
+ FormData data = new FormData();
+ data.top = new FormAttachment(text, 0, SWT.TOP);
+ data.left = new FormAttachment(nameLabel);
+
+ nameLabel.setFont(SiriusEditor.getFontRegistry().get("required")); //$NON-NLS-1$
+ }
+
+ @Override
+ protected SelectionListener createButtonListener() {
+ return new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ TextWithContentProposalDialog dialog = new TextWithContentProposalDialog(composite.getShell(), ExtTableDescriptionOnClickExpressionPropertySection.this, text.getText());
+ dialog.open();
+ text.setText(dialog.getResult());
+ handleTextModified();
+ }
+ };
+ }
+
+ @Override
+ protected String getPropertyDescription() {
+ return ""; //$NON-NLS-1$
+ }
+
+}
+
+
+p.
+This property section will create a text field and label in the Properties view of the odesign editor in order to let the Sirius specifier edit the @onClickExpression@ of the table. Our text field will have the same appear and feature as the regular yellow text fields used by Sirius.
+
+h3. Update to the plugins of our basic widget approach
+
+Now we can reuse our work in the basic approach but this time with our own specific DSL instead of reusing the CustomElements from the EEF DSL. First we will have to modify the lifecycle manager provider to support our EEFExtTableDescription.
+
+pre..
+package com.example.awesomeproject.eef.ide.ui.ext.widgets.table.internal;
+
+import com.example.awesomeproject.eef.ext.widgets.table.EEFExtTableDescription;
+
+import org.eclipse.eef.EEFControlDescription;
+import org.eclipse.eef.core.api.EditingContextAdapter;
+import org.eclipse.eef.ide.ui.api.widgets.IEEFLifecycleManager;
+import org.eclipse.eef.ide.ui.api.widgets.IEEFLifecycleManagerProvider;
+import org.eclipse.sirius.common.interpreter.api.IInterpreter;
+import org.eclipse.sirius.common.interpreter.api.IVariableManager;
+
+public class TableLifecycleManagerProvider implements IEEFLifecycleManagerProvider {
+
+ @Override
+ public boolean canHandle(EEFControlDescription controlDescription) {
+ return controlDescription instanceof EEFExtTableDescription;
+ }
+
+ @Override
+ public IEEFLifecycleManager getLifecycleManager(EEFControlDescription controlDescription, IVariableManager variableManager,
+ IInterpreter interpreter, EditingContextAdapter contextAdapter) {
+ if (controlDescription instanceof EEFExtTableDescription) {
+ return new TableLifecycleManager((EEFExtTableDescription) controlDescription, variableManager, interpreter, contextAdapter);
+ }
+ throw new IllegalArgumentException();
+ }
+}
+
+p.
+Now we can just modify our lifecycle manager to support our EEFExtTableDescription.
+
+pre..
+package com.example.awesomeproject.eef.ide.ui.ext.widgets.table.internal;
+
+import com.example.awesomeproject.eef.ext.widgets.table.EEFExtTableDescription;
+
+import com.example.awesomeproject.eef.core.ext.widgets.table.TableController;
+import org.eclipse.eef.EEFWidgetDescription;
+import org.eclipse.eef.common.ui.api.IEEFFormContainer;
+import org.eclipse.eef.core.api.EditingContextAdapter;
+import org.eclipse.eef.core.api.controllers.IConsumer;
+import org.eclipse.eef.core.api.controllers.IEEFWidgetController;
+import org.eclipse.eef.ide.ui.api.widgets.AbstractEEFWidgetLifecycleManager;
+import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
+import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.sirius.common.interpreter.api.IInterpreter;
+import org.eclipse.sirius.common.interpreter.api.IVariableManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Table;
+
+public class TableLifecycleManager extends AbstractEEFWidgetLifecycleManager {
+
+ private EEFExtTableDescription description;
+
+ private TableViewer tableViewer;
+
+ private ComposedAdapterFactory composedAdapterFactory;
+
+ private SelectionListener onClickListener;
+
+ private TableController controller;
+
+ private IConsumer<Object> newValueConsumer;
+
+ public TableLifecycleManager(EEFExtTableDescription description, IVariableManager variableManager, IInterpreter interpreter,
+ EditingContextAdapter contextAdapter) {
+ super(variableManager, interpreter, contextAdapter);
+ this.description = description;
+ }
+
+ @Override
+ protected void createMainControl(Composite parent, IEEFFormContainer formContainer) {
+ Table table = formContainer.getWidgetFactory().createTable(parent,
+ SWT.READ_ONLY | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER | SWT.SINGLE);
+ this.tableViewer = new TableViewer(table);
+ this.composedAdapterFactory = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
+
+ this.tableViewer.setContentProvider(ArrayContentProvider.getInstance());
+ this.tableViewer.setLabelProvider(new DelegatingStyledCellLabelProvider(new AdapterFactoryLabelProvider.StyledLabelProvider(
+ this.composedAdapterFactory, this.tableViewer)));
+
+ this.controller = new TableController(description, variableManager, interpreter, contextAdapter);
+ }
+
+ @Override
+ public void aboutToBeShown() {
+ super.aboutToBeShown();
+
+ this.newValueConsumer = (newValue) -> this.tableViewer.setInput(newValue);
+ this.controller.onNewValue(this.newValueConsumer);
+
+ this.onClickListener = new SelectionListener() {
+ @Override
+ public void widgetSelected(SelectionEvent event) {
+ Object selection = ((IStructuredSelection) TableLifecycleManager.this.tableViewer.getSelection()).getFirstElement();
+ TableLifecycleManager.this.controller.handleClick(selection);
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent event) {
+ Object selection = ((IStructuredSelection) TableLifecycleManager.this.tableViewer.getSelection()).getFirstElement();
+ TableLifecycleManager.this.controller.handleClick(selection);
+ }
+ };
+ this.tableViewer.getTable().addSelectionListener(this.onClickListener);
+ }
+
+ @Override
+ public void refresh() {
+ super.refresh();
+
+ this.controller.refresh();
+ }
+
+ @Override
+ public void aboutToBeHidden() {
+ super.aboutToBeHidden();
+ this.controller.removeValueConsumer();
+ this.newValueConsumer = null;
+
+ this.tableViewer.getTable().removeSelectionListener(this.onClickListener);
+ this.onClickListener = null;
+ }
+
+ @Override
+ protected IEEFWidgetController getController() {
+ return this.controller;
+ }
+
+ @Override
+ protected EEFWidgetDescription getWidgetDescription() {
+ return this.description;
+ }
+
+ @Override
+ protected Control getValidationControl() {
+ return this.tableViewer.getTable();
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+
+ this.composedAdapterFactory.dispose();
+ }
+}
+
+
+p.
+And finally we can easily adapter our controller.
+
+
+pre..
+package com.example.awesomeproject.eef.core.ext.widgets.table.internal;
+
+import com.example.awesomeproject.eef.ext.widgets.table.EEFExtTableDescription;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.eef.EEFCustomWidgetDescription;
+import org.eclipse.eef.core.api.EditingContextAdapter;
+import org.eclipse.eef.core.api.controllers.AbstractEEFCustomWidgetController;
+import org.eclipse.eef.core.api.controllers.IConsumer;
+import org.eclipse.eef.core.api.utils.EvalFactory;
+import org.eclipse.sirius.common.interpreter.api.IInterpreter;
+import org.eclipse.sirius.common.interpreter.api.IVariableManager;
+
+public class TableController extends AbstractEEFWidgetController {
+
+ private static final String VALUE_EXPRESSION_ID = "valueExpression"; //$NON-NLS-1$
+
+ private static final String ON_CLICK_EXPRESSION_ID = "onClickExpression"; //$NON-NLS-1$
+
+ private static final String SELECTION_VARIABLE_NAME = "selection"; //$NON-NLS-1$
+
+ private IConsumer<Object> newValueConsumer;
+
+ public TableController(EEFExtTableDescription description, IVariableManager variableManager, IInterpreter interpreter,
+ EditingContextAdapter contextAdapter) {
+ super(description, variableManager, interpreter, contextAdapter);
+ }
+
+ @Override
+ protected EEFExtTableDescription getDescription() {
+ return this.description;
+ }
+
+ @Override
+ public void refresh() {
+ super.refresh();
+ this.newEval().call(this.description.getValueExpression(), this.newValueConsumer);
+ }
+
+ public void handleClick(Object object) {
+ this.contextAdapter.performModelChange(() -> {
+ String onClickExpression = this.description.getOnClickExpression();
+
+ Map<String, Object> variables = new HashMap<String, Object>();
+ variables.putAll(this.variableManager.getVariables());
+ variables.put(SELECTION_VARIABLE_NAME, object);
+
+ EvalFactory.of(this.interpreter, variables).call(onClickExpression);
+ });
+ }
+
+ public void onNewValue(IConsumer<Object> consumer) {
+ this.newValueConsumer = consumer;
+ }
+
+ public void removeValueConsumer() {
+ this.newValueConsumer = null;
+ }
+}
+
+p.
+With minimal changes, we can reuse the code of the basic approach in this advanced approach. But now we have the ability to provide the Sirius specifiers with a proper user interface for the manipulation of our widget and we can manipulate our own concepts in the EEF runtime. \ No newline at end of file
diff --git a/plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget_basic.html b/plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget_basic.html
new file mode 100644
index 0000000000..b3bfa6fb50
--- /dev/null
+++ b/plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget_basic.html
@@ -0,0 +1,326 @@
+<?xml version='1.0' encoding='utf-8' ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <title>extensions-properties_provide_custom_widget_basic</title>
+ <link type="text/css" rel="stylesheet" href="../resources/bootstrap.css"/>
+ <link type="text/css" rel="stylesheet" href="../resources/custom.css"/>
+ </head>
+ <body>
+ <h1 id="SiriusPropertiesBasicCustomWidget">Sirius Properties &#8211; Basic Custom Widget</h1>
+ <h2 id="Goal">Goal</h2>
+ <p>Using the Basic Custom Widget approach, our goal aims at the quick creation of a custom widget with a minimal amount of code even if it creates only a basic user interface for the specifier to use our widget. In this example, we will try to create a simple table widget.</p>
+ <h2 id="Strategy">Strategy</h2>
+ <p>In order to create a custom widget, we will have to think about the two kind of users that will interact with our work, the Sirius specifier and the end-user. With this basic approach, we will create a nice table widget for the end-user but we will be a bit limited regarding what we can offer to the specifier.</p>
+ <h3 id="Specificationofthecustomwidget">Specification of the custom widget</h3>
+ <p>The implementation of the Properties view support in Eclipse Sirius is based on the Eclipse EEF project. While both projects are closely related, EEF can be used without Eclipse Sirius and in order to contribute a basic custom widget, we will only have to contribute to the EEF runtime. Both Eclipse EEF and Eclipse Sirius have the ability to let the specifier define a custom widget with a custom widget description.</p>
+ <p>This custom widget description in Eclipse Sirius is manipulated, like any other widget, in the odesign file. In this widget, you can specify the following properties of your widget:</p>
+ <ul>
+ <li>identifier</li>
+ <li>labelExpression</li>
+ <li>helpExpression</li>
+ <li>isEnabledExpression</li>
+ <li>customExpressions</li>
+ <li>customOperations</li>
+ <li>style</li>
+ <li>conditionalStyles</li>
+ </ul>
+ <p>Most of those properties are inherited from the description of the widget (identifier, labelExpression, helpExpression, isEnabledExpression) and the others are specific to the custom widget description (customExpressions, customOperations, style, conditionalStyles). In order to provide a basic custom widget to a Sirius specifier, you will have to indicate her/him the proper identifier to use for the widget, its expressions and its operations. For example, we will specify that in order to make our custom table widget work, the specifier will have to use a custom widget description with the identifier
+ <code>com.example.awesomeproject.sirius.properties.ext.widgets.table</code>.
+ </p>
+ <p>In order to provide the implementation of our table, we will need to use Eclipse EEF&#8217;s lifecycle manager provider extension point. This extension point let you define the appearance and behavior of a custom widget using a lifecycle manager.</p>
+ <h3 id="Extensioncontribution">Extension contribution</h3>
+ <p>You will have to start by creating an Eclipse plugin named
+ <code>com.example.awesomeproject.eef.ide.ui.ext.widgets.table</code> which will contain the provider of the lifecycle manager. In Eclipse EEF, a lifecycle manager is used to create the user interface of a widget and manage its lifecycle (add listeners, refresh, remove listeners, dispose etc) while a controller is used to define its behavior. The controller is not compulsary but it is highly recommended. In order to create the lifecycle manager provider, you will have to define the following extension.
+ </p>
+ <pre>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
+&lt;?eclipse version="3.4"?&gt;
+&lt;plugin&gt;
+ &lt;extension
+ point="org.eclipse.eef.ide.ui.eefLifecycleManagerProvider"&gt;
+ &lt;descriptor
+ class="com.example.awesomeproject.eef.ide.ui.ext.widgets.table.internal.TableLifecycleManagerProvider"
+ description="%tableLifecycleManagerProvider.Description"
+ id="com.example.awesomeproject.eef.ide.ui.ext.widgets.table"
+ label="%tableLifecycleManagerProvider.Label"&gt;
+ &lt;/descriptor&gt;
+ &lt;/extension&gt;
+&lt;/plugin&gt;
+
+</pre>
+ <p>
+ <br/>To make your code compile, you will have to add at least some dependencies (Required Bundle) to the following plugins:
+ </p>
+ <ul>
+ <li>org.eclipse.eef</li>
+ <li>org.eclipse.eef.core</li>
+ <li>org.eclipse.eef.ide.ui</li>
+ <li>org.eclipse.sirius.common.interpreter</li>
+ </ul>
+ <h3 id="EEFlifecyclemanagerprovider">EEF lifecycle manager provider</h3>
+ <p>
+ <br/>The properties label and description should both be internationalized and the class should reference a Java class implementing IEEFLifecycleManagerProvider. Our lifecycle manager provider will have two methods, one to indicate if it can handle the description given by the EEF runtime and one to return a proper lifecycle manager for this description. In our case, the only description that we want to support is a custom widget with the identifier
+ <code>com.example.awesomeproject.sirius.properties.ext.widgets.table</code>.
+ </p>
+ <pre>package com.example.awesomeproject.eef.ide.ui.ext.widgets.table.internal;
+
+import org.eclipse.eef.EEFControlDescription;
+import org.eclipse.eef.EEFCustomWidgetDescription;
+import org.eclipse.eef.core.api.EditingContextAdapter;
+import org.eclipse.eef.ide.ui.api.widgets.IEEFLifecycleManager;
+import org.eclipse.eef.ide.ui.api.widgets.IEEFLifecycleManagerProvider;
+import org.eclipse.sirius.common.interpreter.api.IInterpreter;
+import org.eclipse.sirius.common.interpreter.api.IVariableManager;
+
+public class TableLifecycleManagerProvider implements IEEFLifecycleManagerProvider {
+ /**
+ * The identifier of the control description supported.
+ */
+ private static final String SUPPORTED_ID = "com.example.awesomeproject.sirius.properties.ext.widgets.table"; //$NON-NLS-1$
+
+ @Override
+ public boolean canHandle(EEFControlDescription controlDescription) {
+ // only support custom widgets with the proper identifier
+ return SUPPORTED_ID.equals(controlDescription.getIdentifier()) &amp;&amp; controlDescription instanceof EEFCustomWidgetDescription;
+ }
+
+ @Override
+ public IEEFLifecycleManager getLifecycleManager(EEFControlDescription controlDescription, IVariableManager variableManager,
+ IInterpreter interpreter, EditingContextAdapter contextAdapter) {
+ if (controlDescription instanceof EEFCustomWidgetDescription) {
+ return new TableLifecycleManager((EEFCustomWidgetDescription) controlDescription, variableManager, interpreter, contextAdapter);
+ }
+ throw new IllegalArgumentException();
+ }
+}
+
+
+</pre>
+ <p>
+ <br/>To create the lifecycle manager, the EEF runtime gives us access to several variables:
+ </p>
+ <ul>
+ <li>controlDescription: The description of the control that we support</li>
+ <li>variableManager: The variables available for this control, we can use it to add new variables and even create child contexts with new variables</li>
+ <li>interpreter: The interpreter used to run expression given by the Sirius specifier</li>
+ <li>contextAdapter: The wrapper used to run modifications of the model with optional support for transactions</li>
+ </ul>
+ <h3 id="EEFlifecyclemanager">EEF lifecycle manager</h3>
+ <p>The lifecycle manager must implements the interface
+ <code>IEEFLifecycleManager</code> but for a custom widget it is easier to extends on of its default abstract implementation, in our case
+ <code>org.eclipse.eef.ide.ui.api.widgets.AbstractEEFWidgetLifecycleManager</code>.
+ </p>
+ <pre>package com.example.awesomeproject.eef.ide.ui.ext.widgets.table.internal;
+
+import com.example.awesomeproject.eef.core.ext.widgets.table.TableController;
+import org.eclipse.eef.EEFCustomWidgetDescription;
+import org.eclipse.eef.EEFWidgetDescription;
+import org.eclipse.eef.common.ui.api.IEEFFormContainer;
+import org.eclipse.eef.core.api.EditingContextAdapter;
+import org.eclipse.eef.core.api.controllers.IConsumer;
+import org.eclipse.eef.core.api.controllers.IEEFWidgetController;
+import org.eclipse.eef.ide.ui.api.widgets.AbstractEEFWidgetLifecycleManager;
+import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
+import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.sirius.common.interpreter.api.IInterpreter;
+import org.eclipse.sirius.common.interpreter.api.IVariableManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Table;
+
+public class TableLifecycleManager extends AbstractEEFWidgetLifecycleManager {
+
+ private EEFCustomWidgetDescription description;
+
+ private TableViewer tableViewer;
+
+ private ComposedAdapterFactory composedAdapterFactory;
+
+ private SelectionListener onClickListener;
+
+ private TableController controller;
+
+ private IConsumer&lt;Object&gt; newValueConsumer;
+
+ public TableLifecycleManager(EEFCustomWidgetDescription description, IVariableManager variableManager, IInterpreter interpreter,
+ EditingContextAdapter contextAdapter) {
+ super(variableManager, interpreter, contextAdapter);
+ this.description = description;
+ }
+
+ @Override
+ protected void createMainControl(Composite parent, IEEFFormContainer formContainer) {
+ Table table = formContainer.getWidgetFactory().createTable(parent,
+ SWT.READ_ONLY | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER | SWT.SINGLE);
+ this.tableViewer = new TableViewer(table);
+ this.composedAdapterFactory = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
+
+ this.tableViewer.setContentProvider(ArrayContentProvider.getInstance());
+ this.tableViewer.setLabelProvider(new DelegatingStyledCellLabelProvider(new AdapterFactoryLabelProvider.StyledLabelProvider(
+ this.composedAdapterFactory, this.tableViewer)));
+
+ this.controller = new TableController(description, variableManager, interpreter, contextAdapter);
+ }
+
+ @Override
+ public void aboutToBeShown() {
+ super.aboutToBeShown();
+
+ this.newValueConsumer = (newValue) -&gt; this.tableViewer.setInput(newValue);
+ this.controller.onNewValue(this.newValueConsumer);
+
+ this.onClickListener = new SelectionListener() {
+ @Override
+ public void widgetSelected(SelectionEvent event) {
+ Object selection = ((IStructuredSelection) TableLifecycleManager.this.tableViewer.getSelection()).getFirstElement();
+ TableLifecycleManager.this.controller.handleClick(selection);
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent event) {
+ Object selection = ((IStructuredSelection) TableLifecycleManager.this.tableViewer.getSelection()).getFirstElement();
+ TableLifecycleManager.this.controller.handleClick(selection);
+ }
+ };
+ this.tableViewer.getTable().addSelectionListener(this.onClickListener);
+ }
+
+ @Override
+ public void refresh() {
+ super.refresh();
+
+ this.controller.refresh();
+ }
+
+ @Override
+ public void aboutToBeHidden() {
+ super.aboutToBeHidden();
+ this.controller.removeValueConsumer();
+ this.newValueConsumer = null;
+
+ this.tableViewer.getTable().removeSelectionListener(this.onClickListener);
+ this.onClickListener = null;
+ }
+
+ @Override
+ protected IEEFWidgetController getController() {
+ return this.controller;
+ }
+
+ @Override
+ protected EEFWidgetDescription getWidgetDescription() {
+ return this.description;
+ }
+
+ @Override
+ protected Control getValidationControl() {
+ return this.tableViewer.getTable();
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+
+ this.composedAdapterFactory.dispose();
+ }
+}
+
+
+</pre>
+ <p>
+ <br/>The lifecycle manager will first create the controls of the table widget and it will initialized a controller for the table.
+ </p>
+ <h3 id="EEFcontroller">EEF controller</h3>
+ <p>The controller is not required but it will handle several keys problems for us, for example by extending the proper abstract class it can handle the validation rules of your widget. For a basic custom widget, it is highly recommended to extend
+ <code>org.eclipse.eef.core.api.controllers.AbstractEEFCustomWidgetController</code>. The controller does not have any reason to depend on the user interface, thus it will be created in another Eclipse plugin which will be usable without any dependency to an user interface specific plugins. This plugin will be named
+ <code>com.example.awesomeproject.eef.core.ext.widgets.table</code>. It will depend on at least the following plugins:
+ </p>
+ <ul>
+ <li>org.eclipse.eef</li>
+ <li>org.eclipse.eef.core</li>
+ <li>org.eclipse.sirius.common.interpreter</li>
+ </ul>
+ <pre>package com.example.awesomeproject.eef.core.ext.widgets.table.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.eef.EEFCustomWidgetDescription;
+import org.eclipse.eef.core.api.EditingContextAdapter;
+import org.eclipse.eef.core.api.controllers.AbstractEEFCustomWidgetController;
+import org.eclipse.eef.core.api.controllers.IConsumer;
+import org.eclipse.eef.core.api.utils.EvalFactory;
+import org.eclipse.sirius.common.interpreter.api.IInterpreter;
+import org.eclipse.sirius.common.interpreter.api.IVariableManager;
+
+public class TableController extends AbstractEEFCustomWidgetController {
+
+ private static final String VALUE_EXPRESSION_ID = "valueExpression"; //$NON-NLS-1$
+
+ private static final String ON_CLICK_EXPRESSION_ID = "onClickExpression"; //$NON-NLS-1$
+
+ private static final String SELECTION_VARIABLE_NAME = "selection"; //$NON-NLS-1$
+
+ private IConsumer&lt;Object&gt; newValueConsumer;
+
+ public TableController(EEFCustomWidgetDescription description, IVariableManager variableManager, IInterpreter interpreter,
+ EditingContextAdapter contextAdapter) {
+ super(description, variableManager, interpreter, contextAdapter);
+ }
+
+ @Override
+ protected EEFCustomWidgetDescription getDescription() {
+ return this.description;
+ }
+
+ @Override
+ public void refresh() {
+ super.refresh();
+ this.newEval().call(this.getCustomExpression(VALUE_EXPRESSION_ID), this.newValueConsumer);
+ }
+
+ public void handleClick(Object object) {
+ this.contextAdapter.performModelChange(() -&gt; {
+ String onClickExpression = this.getCustomExpression(ON_CLICK_EXPRESSION_ID);
+
+ Map&lt;String, Object&gt; variables = new HashMap&lt;String, Object&gt;();
+ variables.putAll(this.variableManager.getVariables());
+ variables.put(SELECTION_VARIABLE_NAME, object);
+
+ EvalFactory.of(this.interpreter, variables).call(onClickExpression);
+ });
+ }
+
+ public void onNewValue(IConsumer&lt;Object&gt; consumer) {
+ this.newValueConsumer = consumer;
+ }
+
+ public void removeValueConsumer() {
+ this.newValueConsumer = null;
+ }
+}
+
+</pre>
+ <p>
+ <br/>The controller here will handle two different situations. Firstly, it will react to the refresh in order to compute the new value of the table widget. For that, it will look for an expression with the identifier
+ <code>valueExpression</code> and it will execute it. Once executed, it will give its result to the
+ <code>newValueConsumer</code>. In the table lifecycle manager, we have set, in the method
+ <code>aboutToBeShown</code>, this new value consumer to a lambda which will set a new input for the table. Each time that the lifecycle manager will be refreshed by the framework, its call to
+ <code>super.refresh()</code> will trigger the refresh of the controller (among other things) which will compute the new value to display and given this value to the callback (consumer) registered by the lifecycle manager.
+ </p>
+ <p>Secondly, the controller will be used to handle the click in the table. For that it has a method named
+ <code>handleClick</code> which will be called with the selected object in the table. The controller will then evaluate an expression with the identifier
+ <code>onClickExpression</code> with the variables available along with an additional variable specifically for this use case. The additional variable named
+ <code>selection</code> will here be used to give access to the current selection to the
+ <code>onClickExpression</code>.
+ </p>
+ <p>Now your end users can use your two plugins to create tables in their Properties view using the custom widget description.</p>
+ </body>
+</html> \ No newline at end of file
diff --git a/plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget_basic.textile b/plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget_basic.textile
new file mode 100644
index 0000000000..d98e1eeed9
--- /dev/null
+++ b/plugins/org.eclipse.sirius.doc/doc/developer/extensions-properties_provide_custom_widget_basic.textile
@@ -0,0 +1,312 @@
+h1. Sirius Properties - Basic Custom Widget
+
+h2. Goal
+
+Using the Basic Custom Widget approach, our goal aims at the quick creation of a custom widget with a minimal amount of code even if it creates only a basic user interface for the specifier to use our widget. In this example, we will try to create a simple table widget.
+
+h2. Strategy
+
+In order to create a custom widget, we will have to think about the two kind of users that will interact with our work, the Sirius specifier and the end-user. With this basic approach, we will create a nice table widget for the end-user but we will be a bit limited regarding what we can offer to the specifier.
+
+h3. Specification of the custom widget
+
+The implementation of the Properties view support in Eclipse Sirius is based on the Eclipse EEF project. While both projects are closely related, EEF can be used without Eclipse Sirius and in order to contribute a basic custom widget, we will only have to contribute to the EEF runtime. Both Eclipse EEF and Eclipse Sirius have the ability to let the specifier define a custom widget with a custom widget description.
+
+This custom widget description in Eclipse Sirius is manipulated, like any other widget, in the odesign file. In this widget, you can specify the following properties of your widget:
+
+* identifier
+* labelExpression
+* helpExpression
+* isEnabledExpression
+* customExpressions
+* customOperations
+* style
+* conditionalStyles
+
+Most of those properties are inherited from the description of the widget (identifier, labelExpression, helpExpression, isEnabledExpression) and the others are specific to the custom widget description (customExpressions, customOperations, style, conditionalStyles). In order to provide a basic custom widget to a Sirius specifier, you will have to indicate her/him the proper identifier to use for the widget, its expressions and its operations. For example, we will specify that in order to make our custom table widget work, the specifier will have to use a custom widget description with the identifier @com.example.awesomeproject.sirius.properties.ext.widgets.table@.
+
+In order to provide the implementation of our table, we will need to use Eclipse EEF's lifecycle manager provider extension point. This extension point let you define the appearance and behavior of a custom widget using a lifecycle manager.
+
+h3. Extension contribution
+
+You will have to start by creating an Eclipse plugin named @com.example.awesomeproject.eef.ide.ui.ext.widgets.table@ which will contain the provider of the lifecycle manager. In Eclipse EEF, a lifecycle manager is used to create the user interface of a widget and manage its lifecycle (add listeners, refresh, remove listeners, dispose etc) while a controller is used to define its behavior. The controller is not compulsary but it is highly recommended. In order to create the lifecycle manager provider, you will have to define the following extension.
+
+pre..
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension
+ point="org.eclipse.eef.ide.ui.eefLifecycleManagerProvider">
+ <descriptor
+ class="com.example.awesomeproject.eef.ide.ui.ext.widgets.table.internal.TableLifecycleManagerProvider"
+ description="%tableLifecycleManagerProvider.Description"
+ id="com.example.awesomeproject.eef.ide.ui.ext.widgets.table"
+ label="%tableLifecycleManagerProvider.Label">
+ </descriptor>
+ </extension>
+</plugin>
+
+p.
+To make your code compile, you will have to add at least some dependencies (Required Bundle) to the following plugins:
+
+* org.eclipse.eef
+* org.eclipse.eef.core
+* org.eclipse.eef.ide.ui
+* org.eclipse.sirius.common.interpreter
+
+h3. EEF lifecycle manager provider
+
+
+p.
+The properties label and description should both be internationalized and the class should reference a Java class implementing IEEFLifecycleManagerProvider. Our lifecycle manager provider will have two methods, one to indicate if it can handle the description given by the EEF runtime and one to return a proper lifecycle manager for this description. In our case, the only description that we want to support is a custom widget with the identifier @com.example.awesomeproject.sirius.properties.ext.widgets.table@.
+
+pre..
+package com.example.awesomeproject.eef.ide.ui.ext.widgets.table.internal;
+
+import org.eclipse.eef.EEFControlDescription;
+import org.eclipse.eef.EEFCustomWidgetDescription;
+import org.eclipse.eef.core.api.EditingContextAdapter;
+import org.eclipse.eef.ide.ui.api.widgets.IEEFLifecycleManager;
+import org.eclipse.eef.ide.ui.api.widgets.IEEFLifecycleManagerProvider;
+import org.eclipse.sirius.common.interpreter.api.IInterpreter;
+import org.eclipse.sirius.common.interpreter.api.IVariableManager;
+
+public class TableLifecycleManagerProvider implements IEEFLifecycleManagerProvider {
+ /**
+ * The identifier of the control description supported.
+ */
+ private static final String SUPPORTED_ID = "com.example.awesomeproject.sirius.properties.ext.widgets.table"; //$NON-NLS-1$
+
+ @Override
+ public boolean canHandle(EEFControlDescription controlDescription) {
+ // only support custom widgets with the proper identifier
+ return SUPPORTED_ID.equals(controlDescription.getIdentifier()) && controlDescription instanceof EEFCustomWidgetDescription;
+ }
+
+ @Override
+ public IEEFLifecycleManager getLifecycleManager(EEFControlDescription controlDescription, IVariableManager variableManager,
+ IInterpreter interpreter, EditingContextAdapter contextAdapter) {
+ if (controlDescription instanceof EEFCustomWidgetDescription) {
+ return new TableLifecycleManager((EEFCustomWidgetDescription) controlDescription, variableManager, interpreter, contextAdapter);
+ }
+ throw new IllegalArgumentException();
+ }
+}
+
+
+p.
+To create the lifecycle manager, the EEF runtime gives us access to several variables:
+
+* controlDescription: The description of the control that we support
+* variableManager: The variables available for this control, we can use it to add new variables and even create child contexts with new variables
+* interpreter: The interpreter used to run expression given by the Sirius specifier
+* contextAdapter: The wrapper used to run modifications of the model with optional support for transactions
+
+
+h3. EEF lifecycle manager
+
+The lifecycle manager must implements the interface @IEEFLifecycleManager@ but for a custom widget it is easier to extends on of its default abstract implementation, in our case @org.eclipse.eef.ide.ui.api.widgets.AbstractEEFWidgetLifecycleManager@.
+
+pre..
+package com.example.awesomeproject.eef.ide.ui.ext.widgets.table.internal;
+
+import com.example.awesomeproject.eef.core.ext.widgets.table.TableController;
+import org.eclipse.eef.EEFCustomWidgetDescription;
+import org.eclipse.eef.EEFWidgetDescription;
+import org.eclipse.eef.common.ui.api.IEEFFormContainer;
+import org.eclipse.eef.core.api.EditingContextAdapter;
+import org.eclipse.eef.core.api.controllers.IConsumer;
+import org.eclipse.eef.core.api.controllers.IEEFWidgetController;
+import org.eclipse.eef.ide.ui.api.widgets.AbstractEEFWidgetLifecycleManager;
+import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
+import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.sirius.common.interpreter.api.IInterpreter;
+import org.eclipse.sirius.common.interpreter.api.IVariableManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Table;
+
+public class TableLifecycleManager extends AbstractEEFWidgetLifecycleManager {
+
+ private EEFCustomWidgetDescription description;
+
+ private TableViewer tableViewer;
+
+ private ComposedAdapterFactory composedAdapterFactory;
+
+ private SelectionListener onClickListener;
+
+ private TableController controller;
+
+ private IConsumer<Object> newValueConsumer;
+
+ public TableLifecycleManager(EEFCustomWidgetDescription description, IVariableManager variableManager, IInterpreter interpreter,
+ EditingContextAdapter contextAdapter) {
+ super(variableManager, interpreter, contextAdapter);
+ this.description = description;
+ }
+
+ @Override
+ protected void createMainControl(Composite parent, IEEFFormContainer formContainer) {
+ Table table = formContainer.getWidgetFactory().createTable(parent,
+ SWT.READ_ONLY | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER | SWT.SINGLE);
+ this.tableViewer = new TableViewer(table);
+ this.composedAdapterFactory = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
+
+ this.tableViewer.setContentProvider(ArrayContentProvider.getInstance());
+ this.tableViewer.setLabelProvider(new DelegatingStyledCellLabelProvider(new AdapterFactoryLabelProvider.StyledLabelProvider(
+ this.composedAdapterFactory, this.tableViewer)));
+
+ this.controller = new TableController(description, variableManager, interpreter, contextAdapter);
+ }
+
+ @Override
+ public void aboutToBeShown() {
+ super.aboutToBeShown();
+
+ this.newValueConsumer = (newValue) -> this.tableViewer.setInput(newValue);
+ this.controller.onNewValue(this.newValueConsumer);
+
+ this.onClickListener = new SelectionListener() {
+ @Override
+ public void widgetSelected(SelectionEvent event) {
+ Object selection = ((IStructuredSelection) TableLifecycleManager.this.tableViewer.getSelection()).getFirstElement();
+ TableLifecycleManager.this.controller.handleClick(selection);
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent event) {
+ Object selection = ((IStructuredSelection) TableLifecycleManager.this.tableViewer.getSelection()).getFirstElement();
+ TableLifecycleManager.this.controller.handleClick(selection);
+ }
+ };
+ this.tableViewer.getTable().addSelectionListener(this.onClickListener);
+ }
+
+ @Override
+ public void refresh() {
+ super.refresh();
+
+ this.controller.refresh();
+ }
+
+ @Override
+ public void aboutToBeHidden() {
+ super.aboutToBeHidden();
+ this.controller.removeValueConsumer();
+ this.newValueConsumer = null;
+
+ this.tableViewer.getTable().removeSelectionListener(this.onClickListener);
+ this.onClickListener = null;
+ }
+
+ @Override
+ protected IEEFWidgetController getController() {
+ return this.controller;
+ }
+
+ @Override
+ protected EEFWidgetDescription getWidgetDescription() {
+ return this.description;
+ }
+
+ @Override
+ protected Control getValidationControl() {
+ return this.tableViewer.getTable();
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+
+ this.composedAdapterFactory.dispose();
+ }
+}
+
+
+p.
+The lifecycle manager will first create the controls of the table widget and it will initialized a controller for the table.
+
+
+h3. EEF controller
+
+The controller is not required but it will handle several keys problems for us, for example by extending the proper abstract class it can handle the validation rules of your widget. For a basic custom widget, it is highly recommended to extend @org.eclipse.eef.core.api.controllers.AbstractEEFCustomWidgetController@. The controller does not have any reason to depend on the user interface, thus it will be created in another Eclipse plugin which will be usable without any dependency to an user interface specific plugins. This plugin will be named @com.example.awesomeproject.eef.core.ext.widgets.table@. It will depend on at least the following plugins:
+
+* org.eclipse.eef
+* org.eclipse.eef.core
+* org.eclipse.sirius.common.interpreter
+
+pre..
+package com.example.awesomeproject.eef.core.ext.widgets.table.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.eef.EEFCustomWidgetDescription;
+import org.eclipse.eef.core.api.EditingContextAdapter;
+import org.eclipse.eef.core.api.controllers.AbstractEEFCustomWidgetController;
+import org.eclipse.eef.core.api.controllers.IConsumer;
+import org.eclipse.eef.core.api.utils.EvalFactory;
+import org.eclipse.sirius.common.interpreter.api.IInterpreter;
+import org.eclipse.sirius.common.interpreter.api.IVariableManager;
+
+public class TableController extends AbstractEEFCustomWidgetController {
+
+ private static final String VALUE_EXPRESSION_ID = "valueExpression"; //$NON-NLS-1$
+
+ private static final String ON_CLICK_EXPRESSION_ID = "onClickExpression"; //$NON-NLS-1$
+
+ private static final String SELECTION_VARIABLE_NAME = "selection"; //$NON-NLS-1$
+
+ private IConsumer<Object> newValueConsumer;
+
+ public TableController(EEFCustomWidgetDescription description, IVariableManager variableManager, IInterpreter interpreter,
+ EditingContextAdapter contextAdapter) {
+ super(description, variableManager, interpreter, contextAdapter);
+ }
+
+ @Override
+ protected EEFCustomWidgetDescription getDescription() {
+ return this.description;
+ }
+
+ @Override
+ public void refresh() {
+ super.refresh();
+ this.newEval().call(this.getCustomExpression(VALUE_EXPRESSION_ID), this.newValueConsumer);
+ }
+
+ public void handleClick(Object object) {
+ this.contextAdapter.performModelChange(() -> {
+ String onClickExpression = this.getCustomExpression(ON_CLICK_EXPRESSION_ID);
+
+ Map<String, Object> variables = new HashMap<String, Object>();
+ variables.putAll(this.variableManager.getVariables());
+ variables.put(SELECTION_VARIABLE_NAME, object);
+
+ EvalFactory.of(this.interpreter, variables).call(onClickExpression);
+ });
+ }
+
+ public void onNewValue(IConsumer<Object> consumer) {
+ this.newValueConsumer = consumer;
+ }
+
+ public void removeValueConsumer() {
+ this.newValueConsumer = null;
+ }
+}
+
+p.
+The controller here will handle two different situations. Firstly, it will react to the refresh in order to compute the new value of the table widget. For that, it will look for an expression with the identifier @valueExpression@ and it will execute it. Once executed, it will give its result to the @newValueConsumer@. In the table lifecycle manager, we have set, in the method @aboutToBeShown@, this new value consumer to a lambda which will set a new input for the table. Each time that the lifecycle manager will be refreshed by the framework, its call to @super.refresh()@ will trigger the refresh of the controller (among other things) which will compute the new value to display and given this value to the callback (consumer) registered by the lifecycle manager.
+
+Secondly, the controller will be used to handle the click in the table. For that it has a method named @handleClick@ which will be called with the selected object in the table. The controller will then evaluate an expression with the identifier @onClickExpression@ with the variables available along with an additional variable specifically for this use case. The additional variable named @selection@ will here be used to give access to the current selection to the @onClickExpression@.
+
+Now your end users can use your two plugins to create tables in their Properties view using the custom widget description.
diff --git a/plugins/org.eclipse.sirius.doc/doc/toc.xml b/plugins/org.eclipse.sirius.doc/doc/toc.xml
index e49409e1e5..352c1f808c 100644
--- a/plugins/org.eclipse.sirius.doc/doc/toc.xml
+++ b/plugins/org.eclipse.sirius.doc/doc/toc.xml
@@ -208,6 +208,10 @@
<topic href="doc/developer/extensions-provide_repair_contribution.html" label="Provide custom repair participant" />
<topic href="doc/developer/extensions-provide_celleditor.html" label="Provide custom cell editor" />
<topic href="doc/developer/extensions-provide_custom_bundled_image_shape.html" label="Provide custom bundled image shape" />
+ <topic href="doc/developer/extensions-properties_provide_custom_widget.html" label="Provide a custom widget in the Properties view">
+ <topic href="doc/developer/extensions-properties_provide_custom_widget_basic.html" label="Basic approach" />
+ <topic href="doc/developer/extensions-properties_provide_custom_widget_advanced.html" label="Advanced approach" />
+ </topic>
<anchor id="moreExtensionPointsRefs" />
</topic>
<anchor id="moreDeveloperRefs" />

Back to the top