{"id":386,"date":"2026-01-27T20:43:33","date_gmt":"2026-01-27T20:43:33","guid":{"rendered":"https:\/\/kinori.tech\/blog\/?p=386"},"modified":"2026-01-27T20:43:33","modified_gmt":"2026-01-27T20:43:33","slug":"using-the-eclipse-properties-view-with-a-glsp-diagram","status":"publish","type":"post","link":"https:\/\/kinori.tech\/blog\/en\/2026\/01\/27\/using-the-eclipse-properties-view-with-a-glsp-diagram\/","title":{"rendered":"Using the Eclipse Properties View with a GLSP Diagram"},"content":{"rendered":"\n<p>We are used to clicking things in Eclipse and the properties view getting updated to show us additional information about the thing we clicked. However, when clicking on elements in the Eclipse GLSP editor this is not the case. Next, I give some background on how the PropertiesView works and how it is related to the editor selection. Then, I show how we can modify the GLSP editor to make use of the properties view.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Current State<\/h2>\n\n\n\n<p>The GLSP tooling, I have found, is really complete out of the box. My issue is that most of the extra tools and things that are already available for you is poorly documented, so you either have to reinvent the wheel, or take some time to go over the code base and find what you need. This was the case with the properties view.  In this <a href=\"https:\/\/github.com\/eclipse-glsp\/glsp\/discussions\/806\">discussion<\/a>, one of the GLSP maintainers suggests to create a SelectAction handler and then use that to notify the <a href=\"https:\/\/www.eclipse.org\/articles\/Article-WorkbenchSelections\/article.html\">Eclipse Selection Service<\/a>. But taking a look at the code, the GLSP IDE editor plugin provides an <kbd>IdeSelectActionHandler<\/kbd>  and <kbd>IdeSelectAllActionHandler<\/kbd> that already does that, and that notifies the GLSP Editor about the selection changes. All you need to do, is bind them in your editor module:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; auto-links: false; title: ; quick-code: false; notranslate\" title=\"\">\n@Override\nprotected void configureActionHandlers(final MultiBinding&lt;ActionHandler&gt; bindings) {\n\tsuper.configureActionHandlers(bindings);\n...\n\tbindings.add(IdeSelectActionHandler.class);\n\tbindings.add(IdeSelectAllActionHandler.class);\n}\n<\/pre><\/div>\n\n\n<p>To be precise, the notification is received by the <kbd>GLSPDiagramComposite<\/kbd> (which is used by the <kbd>GLSPDiagramEditor<\/kbd> to provide the editor part control &#8211; an embedded browser if you need to know). The magic happens in the updateSelection method:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\npublic void updateSelection(final SelectAction selectAction) {\n      getModelStateOnceInitialized().thenAccept(modelState -&gt; {\n         Collection&lt;String&gt; selectedIds = selectAction.getSelectedElementsIDs();\n         Collection&lt;String&gt; deselectedIds = selectAction.isDeselectAll()\n            ? modelState.getIndex().allIds()\n            : selectAction.getDeselectedElementsIDs();\n         List&lt;GModelElement&gt; selectedGModelElements = toGModelElements(selectedIds, modelState);\n         List&lt;GModelElement&gt; deselectedGModelElements = toGModelElements(deselectedIds, modelState);\n         List&lt;GModelElement&gt; selection = toGModelElementStream(currentSelection).collect(toList());\n\n         selection.removeAll(deselectedGModelElements);\n         addUnique(selectedGModelElements, selection);\n\n         currentSelection = new StructuredSelection(selection);\n         selectionListener.selectionChanged(new SelectionChangedEvent(this, currentSelection));\n      });\n   }\n<\/pre><\/div>\n\n\n<p>In the method, the selected element IDs (<kbd>selectAction.getSelectedElementsIDs()<\/kbd>) are mapped to Graph Model Elements via the index. Then, the selectionListener is notified with the updated list of Graph Model Elements that are currently selected. In this case, the selection listener is just a wrapper to make sure that the notifications are run in the UI thread. Since the <kbd>GLSPDiagramComposite<\/kbd> was used as the selection provider for the <kbd>GLSPDiagramEditor<\/kbd>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; auto-links: false; highlight: [5]; title: ; quick-code: false; notranslate\" title=\"\">\n@Override\n public void createPartControl(final Composite parent) {\n      diagram.createPartControl(parent);\n      setPartName(generatePartName());\n      getSite().setSelectionProvider(diagram);\n      createBrowserMenu();\n   }\n<\/pre><\/div>\n\n\n<p>the PropertiesView will be notified that a Graph Model element was selected. Yet&#8230; nothing shows in the properties view. To understand why, we need to look at how the PropertiesView work.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Eclipse Properties View<\/h2>\n\n\n\n<p>There is an <a href=\"https:\/\/www.eclipse.org\/articles\/Article-Properties-View\/properties-view.html\">Eclipse Corner Article<\/a> that explains how to use the properties view. In a nutshell, there are two ways to provide a property source for a particular selection:<br><br>&#8211; The selected item can implement the\u00a0<kbd>IAdaptable<\/kbd>\u00a0interface and return an\u00a0<kbd>IPropertySource<\/kbd>\u00a0object when\u00a0<kbd>getAdapter<\/kbd>\u00a0is called.<br>&#8211; The selected item can implement the\u00a0<kbd>IPropertySource<\/kbd>\u00a0interface and provide the properties.<br><br>Neither of which <kbd>GModelElement<\/kbd> satisfies (the super type of selected elements). So in order to solve the issue, we would need to make sure that the <kbd>updateSelection<\/kbd> method that we discussed previously wraps the Graph Model Elements in a custom <kbd>IAdaptable<\/kbd>\u00a0or <kbd>IPropertySource<\/kbd>\u00a0implementation. Before you go and starting doing this, take a moment to understand the extend of the work needed to provide a working <kbd>IPropertySource<\/kbd>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">IPropertySource<\/h3>\n\n\n\n<p>The <kbd>IPropertySource<\/kbd> interface allows the Properties View to dynamically adjust itself to show the properties of any Object within eclipse. It does so, in its simplest way, by providing a set of <kbd>IPropertyDescriptor<\/kbd>. Each property descriptor will be visually represented by a row in the properties view (but technically provided by the <kbd>PropertySheetEntry<\/kbd>). Without dwelling into the details of the API, the critical bit is that <kbd>IPropertyDescriptor<\/kbd> must provide the editor for changing the property value. This can be a simple text editor for changing a text attribute or something like a color picker if the attribute represents a color. But most importantly you must react to the editor changes so you can update the object properties accordingly.<\/p>\n\n\n\n<p>I must confess I panicked a bit when I understood what had to be done. When I was about to tell my boss I needed another month to get this working, I remembered that EMF provides this functionality out of the box. Luckily for me, the domain model (i.e. the mode for which the graph is created) was an EMF model, so all I had to do was hook everything together. If your domain model is not an EMF model&#8230;. this is as far as I can help. You need to implement all the property descriptors&#8230; (or create an Ecore model of your domain and use the solution that I present next &#8211; which I would strongly suggest and <a href=\"https:\/\/kinori.tech\/#contact\">offer to help<\/a>).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Emf Edit and Editor<\/h2>\n\n\n\n<figure class=\"wp-block-pullquote\"><blockquote><p>If you haven&#8217;t done so, its a good time to open your Genmodel and generate your edit and editor plugins. If you have no idea about what I just said, then take some time to read about EMF code generation capabilities. Vogella has a nice <a href=\"https:\/\/www.vogella.com\/tutorials\/EclipseEMF\/article.html\">tutorial<\/a>.<\/p><\/blockquote><\/figure>\n\n\n\n<p>There are two main aspects that will help us tie all together. First, in your &#8216;edit&#8217; plugin, EMF must have generated an <kbd>XxxItemProviderAdapterFactory<\/kbd>. This class can be used to adapt your domain models and create the required property descriptors. As a result, EMF will provide the required editors for your object features and handle the model updating. Required implementation: 0! Well, unless you want to provide a custom editor&#8230; in which case I will recommend sticking to the generated code as much as possible so all the complicated bits are handled for you. Your properties view will behave the same as when using the generated editor.<\/p>\n\n\n\n<p>Second, understand how the editor uses the <kbd>XxxItemProviderAdapterFactory<\/kbd> to connect it all together. EMF does this by providing a custom <kbd>IPropertySheetPage<\/kbd> (a.k.a the properties view). This happens in two places in the XxxEditor (in the editor plugin):<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; auto-links: false; highlight: [6]; title: ; quick-code: false; notranslate\" title=\"\">\n@Override\npublic &lt;T&gt; T getAdapter(Class&lt;T&gt; key) {\n\tif (key.equals(IContentOutlinePage.class)) {\n\t\treturn showOutlineView() ? key.cast(getContentOutlinePage()) : null;\n\t} else if (key.equals(IPropertySheetPage.class)) {\n\t\treturn key.cast(getPropertySheetPage());\n\t} else if (key.equals(IGotoMarker.class)) {\n\t\treturn key.cast(this);\n\t} else {\n\t\treturn super.getAdapter(key);\n\t}\n}\n<\/pre><\/div>\n\n\n<p>that indicates to Eclipse that we want to provide custom outline, properties and goto marker implementations. And:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; auto-links: false; highlight: [15]; title: ; quick-code: false; notranslate\" title=\"\">\npublic IPropertySheetPage getPropertySheetPage() {\n\tPropertySheetPage propertySheetPage = new ExtendedPropertySheetPage(editingDomain,\n\t\t\tExtendedPropertySheetPage.Decoration.NONE, null, 0, false) {\n\t\t@Override\n\t\tpublic void setSelectionToViewer(List&lt;?&gt; selection) {\n\t\t\tWfpEditor.this.setSelectionToViewer(selection);\n\t\t\tWfpEditor.this.setFocus();\n\t\t}\n\t\t\t@Override\n\t\tpublic void setActionBars(IActionBars actionBars) {\n\t\t\tsuper.setActionBars(actionBars);\n\t\t\tgetActionBarContributor().shareGlobalActions(this, actionBars);\n\t\t}\n\t};\n\tpropertySheetPage.setPropertySourceProvider(new AdapterFactoryContentProvider(adapterFactory));\n\tpropertySheetPages.add(propertySheetPage);\n\treturn propertySheetPage;\n}\n<\/pre><\/div>\n\n\n<p>Of importance is line 14, in which we attach the <kbd>XxxItemProviderAdapterFactory<\/kbd> (adapterFactory) to the properties view. This part is the key, because in the <kbd>IPropertySheetEntry<\/kbd> implementation, the <kbd>propertySourceProvider<\/kbd> of the view is used to get the selected object&#8217;s IPropertySource.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; auto-links: false; highlight: [12]; title: ; quick-code: false; notranslate\" title=\"\">\nprotected IPropertySource getPropertySource(Object object) {\n\tif (sources.containsKey(object))\n\t\treturn sources.get(object);\n\n\tIPropertySource result = null;\n\tIPropertySourceProvider provider = propertySourceProvider;\n\tif (provider == null &amp;&amp; object != null) {\n\t\tprovider = Adapters.adapt(object, IPropertySourceProvider.class);\n\t}\n\n\tif (provider != null) {\n\t\tresult = provider.getPropertySource(object);\n\t} else {\n\t\tresult = Adapters.adapt(object, IPropertySource.class);\n\t}\n\n\tsources.put(object, result);\n\treturn result;\n}\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Hocking it up<\/h2>\n\n\n\n<p>So with the required information in place, we need to modify our GLSP editor to use our generated <kbd>XxxItemProviderAdapterFactory<\/kbd>. Our first modification would be to the composite&#8217;s update selection method. For this we need to extend the <kbd>GLSPDiagramComposite<\/kbd>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; auto-links: false; highlight: [5,44,47,48,49,50,51,52,53,54,55]; title: ; quick-code: false; notranslate\" title=\"\">\npublic class WfpGLSPDiagramComposite extends GLSPDiagramComposite {\n\n\tpublic WfpGLSPDiagramComposite(\n\t\tfinal String editorId,\n\t\tPropertySheetPage propertySheetPage) {\n\t\tsuper(editorId);\n\t\tthis.propertySheetPage = propertySheetPage;\n\t}\n\n\t\/**\n\t * Updates the currently selected elements.\n\t * &lt;p&gt;\n\t * This implementation mapes the selected Graph Model Elements to their semantic counterparts,\n\t * and notifies the selection listeners with a selection of semantic elements.\n\t * &lt;\/p&gt;\n\t *\n\t * @param selectAction the {@link SelectAction}\n\t *\/\n\t@Override\n\tpublic void updateSelection(final SelectAction selectAction) {\n\t\tgetModelStateOnceInitialized().thenAccept(modelState -&gt; {\n\t\t\tSet&lt;String&gt; selectedIds = new HashSet&lt;&gt;(selectAction.getSelectedElementsIDs());\n\t\t\tSet&lt;String&gt; deselectedIds = new HashSet&lt;&gt;(selectAction.isDeselectAll() ? \n\t\t\t\t\tmodelState.getIndex().allIds()\n\t\t\t\t\t: selectAction.getDeselectedElementsIDs());\n\t\t\tSet&lt;String&gt; selection = this.currentSelection.stream()\n\t\t\t\t\t.filter(String.class::isInstance)\n\t\t\t\t\t.map(String.class::cast)\n\t\t\t\t\t.collect(Collectors.toCollection(() -&gt; new HashSet&lt;&gt;()));\n\t\t\tselection.removeAll(deselectedIds);\n\t\t\tselection.addAll(selectedIds);\n\t\t\tcurrentSelection = new StructuredSelection(new ArrayList&lt;&gt;(selection));\n\t\t\tif (modelState instanceof SemanticModelState) {\n\t\t\t\tthis.getInstance(AdapterFactory.class)\n\t\t\t\t\t.thenAccept(af -&gt; {\n\t\t\t\t\t\tif (af instanceof IChangeNotifier) {\n\t\t\t\t\t\t\t\/\/ Remove and add so we don&#039;t get multiple registrations\n\t\t\t\t\t\t\t((IChangeNotifier) af).removeListener(gslpUpdateProvider);\n\t\t\t\t\t\t\t((IChangeNotifier) af).addListener(gslpUpdateProvider);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tvar provider = new AdapterFactoryContentProvider(af);\n\t\t\t\t\t\tDisplay.getDefault().syncExec(new Runnable() {\n\t\t\t\t\t\t    public void run() {\n\t\t\t\t\t\t    \tpropertySheetPage.setPropertySourceProvider(provider);\n\t\t\t\t\t\t    }\n\t\t\t\t\t\t});\n\t\t\t\t\t\tvar semSelection = selection.stream()\n\t\t\t\t\t\t\t\t.map(id -&gt; ((SemanticModelState) modelState).getIndex().get(id))\n\t\t\t\t\t\t\t\t.filter(Optional::isPresent)\n\t\t\t\t\t\t\t\t.map(Optional::get)\n\t\t\t\t\t\t\t\t.map(ge -&gt; ((SemanticModelState) modelState).getIndex().semanticElement(ge))\n\t\t\t\t\t\t\t\t.filter(Optional::isPresent)\n\t\t\t\t\t\t\t\t.map(Optional::get)\n\t\t\t\t\t\t\t\t.distinct()\n\t\t\t\t\t\t\t\t.toList();\n\t\t\t\t\t\tthis.selectionListener.selectionChanged(new SelectionChangedEvent(this, new StructuredSelection(semSelection)));\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tselectionListener.selectionChanged(new SelectionChangedEvent(this, currentSelection));\n\t\t\t}\n\n\t\t});\n\t}\n\t\n\tprivate PropertySheetPage propertySheetPage;\n\tprivate GslpUpdateProvider gslpUpdateProvider = new GslpUpdateProvider();\n\t\n\tprivate class GslpUpdateProvider implements INotifyChangedListener {\n\n\t\t@Override\n\t\tpublic void notifyChanged(Notification notification) {\n\t\t\tgetInstance(ModelSubmissionHandler.class)\n\t\t\t\t.thenAccept(handler -&gt; {\n\t\t\t\t\t\thandler.submitModel(&quot;PropertiesView change&quot;).forEach(a -&gt; dispatch(a));\n\t\t\t\t\t});\n\t\t}\n\t\t\n\t}\n}\n<\/pre><\/div>\n\n\n<p>The first modification is that we accept a PropertySheetPage in the constructor. This is to emulate the EMF editor implementation, without providing a custom property sheet page. The reference to the PropertySheetPage enables us to set our property source provider (line 44). The other deviation from the original implementation is that we keep the current selection as a list of element IDs (this is in order to avoid duplicate Graph Model elements in the selection). In lines 47-55 we use the selected element IDs to get the GraphElements from the index, and from their resolve their respective semantic (domain) elements. Since these will be instances of our Ecore metamodel, the <kbd>AdapterFactoryContentProvider<\/kbd> will resolve their IPropertySources. Finally, notice that the <kbd>AdapterFactory<\/kbd> is injected. This allows this custom Diagram composite to be used with any Ecore metamodel. The adapter factory is injected in the editor module:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; auto-links: false; title: ; quick-code: false; notranslate\" title=\"\">\npublic class EclipseDiagramModule extends DiagramModule {\n\n\t@Override\n\tpublic void configure() {\n\t\tsuper.configure();\n\t\tbind(ModelInitializationConstraint.class).to(DefaultModelInitializationConstraint.class).in(Scopes.SINGLETON);\n\t\tbind(AdapterFactory.class).to(XxxItemProviderAdapterFactory.class).in(Scopes.SINGLETON);\n\t}\n\n...\n\n<\/pre><\/div>\n\n\n<p>The other piece of additional code is the <kbd>GslpUpdateProvider<\/kbd> class. This is a simple EMF notification listener that we will attach the AdapterFactory in order to be informed when the domain elements are nodified via the PropertiesView. In this case, we use the <kbd>ModelSubmissionHandler<\/kbd> to notify the client that the model has been modified and the graph should be redrawn. Further filtering could be done to determine if the change merits a graph redraw (e.g. a non-visual) property change can be skipped.<\/p>\n\n\n\n<p>Next, we extend the GLSPDiagramEditor to provide our custom composite. It also makes sure we control the PropertySheetPage used for the editor.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; auto-links: false; title: ; quick-code: false; notranslate\" title=\"\">\npublic class XxxGLSPDiagramEditor extends GLSPDiagramEditor {\n\n\t@Override\n\tpublic &lt;T&gt; T getAdapter(Class&lt;T&gt; key) {\n\t\tif (key.equals(IPropertySheetPage.class)) {\n\t\t\treturn key.cast(this.propertySheetPage);\n\t\t}\n\t\treturn super.getAdapter(key);\n\t}\n\t\n\t@Override\n\tprotected GLSPDiagramComposite createGLSPDiagramComposite() {\n\t\treturn new WfpGLSPDiagramComposite(getEditorId(), this.propertySheetPage);\n\t}\n\t\n\tprivate PropertySheetPage propertySheetPage = new PropertySheetPage(); \n\n}\n<\/pre><\/div>\n\n\n<p>Finally, we need to update our <kbd>org.eclipse.ui.editors<\/kbd> extension to use the new Editor:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; auto-links: false; title: ; quick-code: false; notranslate\" title=\"\">\n&lt;extension\n         point=&quot;org.eclipse.ui.editors&quot;&gt;\n      &lt;editor\n            class=&quot;my.project.glsp.ide.editor.ui.XxxGLSPDiagramEditor&quot;\n            contributorClass=&quot;org.eclipse.glsp.ide.editor.ui.GLSPDiagramEditorActionContributor&quot;\n...\n<\/pre><\/div>\n\n\n<p>And that&#8217;s it. Now, when you click on an element in your diagram, your properties editor should show you all the properties, let you modify them and refresh the graph when changes occur. If not working, the best place to start is the <kbd>PropertySheetEntry<\/kbd> class, in the <kbd>getPropertySource<\/kbd> method. There, you can check that thar the property source provider is set, and how it is working.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We are used to clicking things in Eclipse and the properties view getting updated to show us additional information about the thing we clicked. However, when clicking on elements in the Eclipse GLSP editor this is not the case. Next, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"series":[],"class_list":["post-386","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/kinori.tech\/blog\/wp-json\/wp\/v2\/posts\/386","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kinori.tech\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kinori.tech\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kinori.tech\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/kinori.tech\/blog\/wp-json\/wp\/v2\/comments?post=386"}],"version-history":[{"count":19,"href":"https:\/\/kinori.tech\/blog\/wp-json\/wp\/v2\/posts\/386\/revisions"}],"predecessor-version":[{"id":405,"href":"https:\/\/kinori.tech\/blog\/wp-json\/wp\/v2\/posts\/386\/revisions\/405"}],"wp:attachment":[{"href":"https:\/\/kinori.tech\/blog\/wp-json\/wp\/v2\/media?parent=386"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/kinori.tech\/blog\/wp-json\/wp\/v2\/categories?post=386"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kinori.tech\/blog\/wp-json\/wp\/v2\/tags?post=386"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/kinori.tech\/blog\/wp-json\/wp\/v2\/series?post=386"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}