Skip to content

Commit

Permalink
[1914] Add support for custom widgets
Browse files Browse the repository at this point in the history
Bug: #1914
Signed-off-by: Pierre-Charles David <pierre-charles.david@obeo.fr>
  • Loading branch information
pcdavid committed May 10, 2023
1 parent 4ccf0e6 commit 5f7a5f9
Show file tree
Hide file tree
Showing 72 changed files with 1,042 additions and 270 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.adoc
Expand Up @@ -37,6 +37,8 @@ In the _styleDescription_, the definition of a color are now a select list of al
- https://github.com/eclipse-sirius/sirius-components/issues/1946[1#946] Enabled child extenders in the View DSL implementation.
This allows downstream projects and applications to provide their own sub-types of the DSL types (e.g. new WidgetDescriptions).
In addition to registering the extension metadmodel itself, users must provide a `ChildExtenderProvider` bean for their extensions to be properly integrated.
- https://github.com/eclipse-sirius/sirius-components/issues/1914[#1914] [form] It is now possible for applications to provide their own custom widgets without forking Sirius Components.
See link:how-to/contribute-custom-widget.adoc[the documentation] for more details.

=== Improvements

Expand Down
Expand Up @@ -231,8 +231,8 @@ private void checkResult(FormDescription description) {
VariableManager variableManager = new VariableManager();
variableManager.put(VariableManager.SELF, List.of(EcorePackage.eINSTANCE));

FormRenderer formRenderer = new FormRenderer();
FormComponentProps props = new FormComponentProps(variableManager, description);
FormRenderer formRenderer = new FormRenderer(List.of());
FormComponentProps props = new FormComponentProps(variableManager, description, List.of());
Element element = new Element(FormComponent.class, props);
Form form = formRenderer.render(element);

Expand Down
@@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright (c) 2023 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.components.collaborative.api;

import java.util.Objects;

import org.eclipse.sirius.components.core.api.IRepresentationDescriptionSearchService;
import org.springframework.stereotype.Service;

/**
* Bundles the common dependencies that most {@link IRepresentationEventProcessorFactory} implementations need into a
* single object for convenience.
*
* @author pcdavid
*/
@Service
public class RepresentationEventProcessorFactoryConfiguration {

private final IRepresentationDescriptionSearchService representationDescriptionSearchService;

private final IRepresentationSearchService representationSearchService;

private final IRepresentationRefreshPolicyRegistry representationRefreshPolicyRegistry;

private final ISubscriptionManagerFactory subscriptionManagerFactory;

public RepresentationEventProcessorFactoryConfiguration(IRepresentationDescriptionSearchService representationDescriptionSearchService, IRepresentationSearchService representationSearchService,
IRepresentationRefreshPolicyRegistry representationRefreshPolicyRegistry, ISubscriptionManagerFactory subscriptionManagerFactory) {
this.representationDescriptionSearchService = Objects.requireNonNull(representationDescriptionSearchService);
this.representationSearchService = Objects.requireNonNull(representationSearchService);
this.representationRefreshPolicyRegistry = Objects.requireNonNull(representationRefreshPolicyRegistry);
this.subscriptionManagerFactory = Objects.requireNonNull(subscriptionManagerFactory);
}

public IRepresentationDescriptionSearchService getRepresentationDescriptionSearchService() {
return this.representationDescriptionSearchService;
}

public IRepresentationSearchService getRepresentationSearchService() {
return this.representationSearchService;
}

public IRepresentationRefreshPolicyRegistry getRepresentationRefreshPolicyRegistry() {
return this.representationRefreshPolicyRegistry;
}

public ISubscriptionManagerFactory getSubscriptionManagerFactory() {
return this.subscriptionManagerFactory;
}
}
Expand Up @@ -22,6 +22,7 @@
import org.eclipse.sirius.components.collaborative.api.IRepresentationRefreshPolicyRegistry;
import org.eclipse.sirius.components.collaborative.api.IRepresentationSearchService;
import org.eclipse.sirius.components.collaborative.api.ISubscriptionManagerFactory;
import org.eclipse.sirius.components.collaborative.api.RepresentationEventProcessorFactoryConfiguration;
import org.eclipse.sirius.components.collaborative.diagrams.api.DiagramConfiguration;
import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramCreationService;
import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramEventHandler;
Expand Down Expand Up @@ -51,15 +52,14 @@ public class DiagramEventProcessorFactory implements IRepresentationEventProcess

private final IRepresentationRefreshPolicyRegistry representationRefreshPolicyRegistry;

public DiagramEventProcessorFactory(IRepresentationSearchService representationSearchService, IDiagramCreationService diagramCreationService, List<IDiagramEventHandler> diagramEventHandlers,
ISubscriptionManagerFactory subscriptionManagerFactory, IRepresentationDescriptionSearchService representationDescriptionSearchService,
IRepresentationRefreshPolicyRegistry representationRefreshPolicyRegistry) {
this.representationSearchService = Objects.requireNonNull(representationSearchService);
public DiagramEventProcessorFactory(RepresentationEventProcessorFactoryConfiguration configuration, IDiagramCreationService diagramCreationService,
List<IDiagramEventHandler> diagramEventHandlers) {
this.representationSearchService = Objects.requireNonNull(configuration.getRepresentationSearchService());
this.diagramCreationService = Objects.requireNonNull(diagramCreationService);
this.diagramEventHandlers = Objects.requireNonNull(diagramEventHandlers);
this.subscriptionManagerFactory = Objects.requireNonNull(subscriptionManagerFactory);
this.representationDescriptionSearchService = Objects.requireNonNull(representationDescriptionSearchService);
this.representationRefreshPolicyRegistry = Objects.requireNonNull(representationRefreshPolicyRegistry);
this.subscriptionManagerFactory = Objects.requireNonNull(configuration.getSubscriptionManagerFactory());
this.representationDescriptionSearchService = Objects.requireNonNull(configuration.getRepresentationDescriptionSearchService());
this.representationRefreshPolicyRegistry = Objects.requireNonNull(configuration.getRepresentationRefreshPolicyRegistry());
}

@Override
Expand Down
Expand Up @@ -27,10 +27,12 @@
import org.eclipse.sirius.components.core.api.IObjectService;
import org.eclipse.sirius.components.core.api.IRepresentationDescriptionSearchService;
import org.eclipse.sirius.components.formdescriptioneditors.FormDescriptionEditor;
import org.eclipse.sirius.components.formdescriptioneditors.IWidgetPreviewConverterProvider;
import org.eclipse.sirius.components.formdescriptioneditors.components.FormDescriptionEditorComponent;
import org.eclipse.sirius.components.formdescriptioneditors.components.FormDescriptionEditorComponentProps;
import org.eclipse.sirius.components.formdescriptioneditors.description.FormDescriptionEditorDescription;
import org.eclipse.sirius.components.formdescriptioneditors.renderer.FormDescriptionEditorRenderer;
import org.eclipse.sirius.components.forms.renderer.IWidgetDescriptor;
import org.eclipse.sirius.components.representations.Element;
import org.eclipse.sirius.components.representations.VariableManager;
import org.springframework.stereotype.Service;
Expand All @@ -52,13 +54,19 @@ public class FormDescriptionEditorCreationService implements IFormDescriptionEdi

private final IObjectService objectService;

private final List<IWidgetDescriptor> widgetDescriptors;

private final List<IWidgetPreviewConverterProvider> customWidgetConverterProviders;

private final Timer timer;

public FormDescriptionEditorCreationService(IRepresentationDescriptionSearchService representationDescriptionSearchService, IRepresentationPersistenceService representationPersistenceService,
IObjectService objectService, MeterRegistry meterRegistry) {
IObjectService objectService, List<IWidgetDescriptor> widgetDescriptors, List<IWidgetPreviewConverterProvider> customWidgetConverterProviders, MeterRegistry meterRegistry) {
this.representationDescriptionSearchService = Objects.requireNonNull(representationDescriptionSearchService);
this.representationPersistenceService = Objects.requireNonNull(representationPersistenceService);
this.objectService = Objects.requireNonNull(objectService);
this.widgetDescriptors = List.copyOf(widgetDescriptors);
this.customWidgetConverterProviders = List.copyOf(customWidgetConverterProviders);

// @formatter:off
this.timer = Timer.builder(Monitoring.REPRESENTATION_EVENT_PROCESSOR_REFRESH)
Expand Down Expand Up @@ -116,10 +124,10 @@ private FormDescriptionEditor doRender(String label, Object targetObject, IEditi

Optional<FormDescriptionEditor> optionalPreviousFormDescriptionEditor = optionalFormDescriptionEditorContext.map(IFormDescriptionEditorContext::getFormDescriptionEditor);

var formDescriptionEditorComponentProps = new FormDescriptionEditorComponentProps(variableManager, formDescriptionEditorDescription, optionalPreviousFormDescriptionEditor);
var formDescriptionEditorComponentProps = new FormDescriptionEditorComponentProps(variableManager, formDescriptionEditorDescription, optionalPreviousFormDescriptionEditor, this.widgetDescriptors, this.customWidgetConverterProviders);
Element element = new Element(FormDescriptionEditorComponent.class, formDescriptionEditorComponentProps);

FormDescriptionEditor newFormDescriptionEditor = new FormDescriptionEditorRenderer().render(element);
FormDescriptionEditor newFormDescriptionEditor = new FormDescriptionEditorRenderer(this.widgetDescriptors).render(element);

long end = System.currentTimeMillis();
this.timer.record(end - start, TimeUnit.MILLISECONDS);
Expand Down
Expand Up @@ -22,6 +22,7 @@
import org.eclipse.sirius.components.collaborative.api.IRepresentationRefreshPolicyRegistry;
import org.eclipse.sirius.components.collaborative.api.IRepresentationSearchService;
import org.eclipse.sirius.components.collaborative.api.ISubscriptionManagerFactory;
import org.eclipse.sirius.components.collaborative.api.RepresentationEventProcessorFactoryConfiguration;
import org.eclipse.sirius.components.collaborative.formdescriptioneditors.api.FormDescriptionEditorConfiguration;
import org.eclipse.sirius.components.collaborative.formdescriptioneditors.api.IFormDescriptionEditorCreationService;
import org.eclipse.sirius.components.collaborative.formdescriptioneditors.api.IFormDescriptionEditorEventHandler;
Expand Down Expand Up @@ -51,16 +52,14 @@ public class FormDescriptionEditorEventProcessorFactory implements IRepresentati

private final IRepresentationRefreshPolicyRegistry representationRefreshPolicyRegistry;

public FormDescriptionEditorEventProcessorFactory(IRepresentationDescriptionSearchService representationDescriptionSearchService,
IFormDescriptionEditorCreationService formDescriptionEditormCreationService, IRepresentationSearchService representationSearchService,
List<IFormDescriptionEditorEventHandler> formDescriptionEditorEventHandlers, ISubscriptionManagerFactory subscriptionManagerFactory,
IRepresentationRefreshPolicyRegistry representationRefreshPolicyRegistry) {
this.representationDescriptionSearchService = Objects.requireNonNull(representationDescriptionSearchService);
public FormDescriptionEditorEventProcessorFactory(RepresentationEventProcessorFactoryConfiguration configuration, IFormDescriptionEditorCreationService formDescriptionEditormCreationService,
List<IFormDescriptionEditorEventHandler> formDescriptionEditorEventHandlers) {
this.representationDescriptionSearchService = Objects.requireNonNull(configuration.getRepresentationDescriptionSearchService());
this.formDescriptionEditormCreationService = Objects.requireNonNull(formDescriptionEditormCreationService);
this.representationSearchService = Objects.requireNonNull(representationSearchService);
this.representationSearchService = Objects.requireNonNull(configuration.getRepresentationSearchService());
this.formDescriptionEditorEventHandlers = Objects.requireNonNull(formDescriptionEditorEventHandlers);
this.subscriptionManagerFactory = Objects.requireNonNull(subscriptionManagerFactory);
this.representationRefreshPolicyRegistry = Objects.requireNonNull(representationRefreshPolicyRegistry);
this.subscriptionManagerFactory = Objects.requireNonNull(configuration.getSubscriptionManagerFactory());
this.representationRefreshPolicyRegistry = Objects.requireNonNull(configuration.getRepresentationRefreshPolicyRegistry());
}

@Override
Expand Down
Expand Up @@ -12,12 +12,14 @@
*******************************************************************************/
package org.eclipse.sirius.components.collaborative.formdescriptioneditors.handlers;

import java.util.List;
import java.util.Objects;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.sirius.components.collaborative.api.ChangeDescription;
import org.eclipse.sirius.components.collaborative.api.ChangeKind;
import org.eclipse.sirius.components.collaborative.api.Monitoring;
Expand All @@ -31,6 +33,7 @@
import org.eclipse.sirius.components.core.api.IObjectService;
import org.eclipse.sirius.components.core.api.IPayload;
import org.eclipse.sirius.components.core.api.SuccessPayload;
import org.eclipse.sirius.components.formdescriptioneditors.IWidgetDescriptionProvider;
import org.eclipse.sirius.components.view.FlexDirection;
import org.eclipse.sirius.components.view.FlexboxContainerDescription;
import org.eclipse.sirius.components.view.GroupDescription;
Expand All @@ -56,11 +59,14 @@ public class AddWidgetEventHandler implements IFormDescriptionEditorEventHandler

private final ICollaborativeFormDescriptionEditorMessageService messageService;

private final List<IWidgetDescriptionProvider> widgetDescriptionProviders;

private final Counter counter;

public AddWidgetEventHandler(IObjectService objectService, ICollaborativeFormDescriptionEditorMessageService messageService, MeterRegistry meterRegistry) {
public AddWidgetEventHandler(IObjectService objectService, ICollaborativeFormDescriptionEditorMessageService messageService, List<IWidgetDescriptionProvider> widgetDescriptionProviders, MeterRegistry meterRegistry) {
this.objectService = Objects.requireNonNull(objectService);
this.messageService = Objects.requireNonNull(messageService);
this.widgetDescriptionProviders = List.copyOf(widgetDescriptionProviders);

// @formatter:off
this.counter = Counter.builder(Monitoring.EVENT_HANDLER)
Expand Down Expand Up @@ -103,9 +109,9 @@ private boolean addWidget(IEditingContext editingContext, IFormDescriptionEditor
var optionalSelf = this.objectService.getObject(editingContext, containerId);
if (optionalSelf.isPresent()) {
Object container = optionalSelf.get();
EClassifier eClassifier = ViewPackage.eINSTANCE.getEClassifier(kind + "Description");
if (eClassifier instanceof EClass) {
var widgetDescription = ViewFactory.eINSTANCE.create((EClass) eClassifier);
EClassifier eClassifier = this.getWidgetDescriptionType(kind);
if (eClassifier instanceof EClass eClass) {
var widgetDescription = EcoreUtil.create(eClass);
if (widgetDescription instanceof FlexboxContainerDescription) {
((FlexboxContainerDescription) widgetDescription).setFlexDirection(FlexDirection.get(kind));
}
Expand All @@ -124,6 +130,17 @@ private boolean addWidget(IEditingContext editingContext, IFormDescriptionEditor
return success;
}


private EClassifier getWidgetDescriptionType(String kind) {
for (IWidgetDescriptionProvider widgetDescriptionProvider : this.widgetDescriptionProviders) {
var optionalType = widgetDescriptionProvider.getWidgetDescriptionType(kind);
if (optionalType.isPresent()) {
return optionalType.get();
}
}
return ViewPackage.eINSTANCE.getEClassifier(kind + "Description");
}

private void createStyle(WidgetDescription widgetDescription) {
EStructuralFeature styleFeature = widgetDescription.eClass().getEStructuralFeature("style");
if (styleFeature instanceof EReference) {
Expand Down
@@ -0,0 +1,26 @@
/*******************************************************************************
* Copyright (c) 2023 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.components.formdescriptioneditors;

import java.util.Optional;

import org.eclipse.emf.ecore.EClass;

/**
* Provides the EClass to use to represent a given kind of widget in a Form Description Editor.
*
* @author pcdavid
*/
public interface IWidgetDescriptionProvider {
Optional<EClass> getWidgetDescriptionType(String widgetKind);
}
@@ -0,0 +1,27 @@
/*******************************************************************************
* Copyright (c) 2023 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.components.formdescriptioneditors;

import org.eclipse.emf.ecore.util.Switch;
import org.eclipse.sirius.components.formdescriptioneditors.description.FormDescriptionEditorDescription;
import org.eclipse.sirius.components.forms.description.AbstractWidgetDescription;
import org.eclipse.sirius.components.representations.VariableManager;

/**
* Provides a switch to convert View-based custom widget descriptions into their API equivalent "preview widget" in a Form Description Editor.
*
* @author pcdavid
*/
public interface IWidgetPreviewConverterProvider {
Switch<AbstractWidgetDescription> getWidgetConverter(FormDescriptionEditorDescription formDescriptionEditorDescription, VariableManager variableManager);
}

0 comments on commit 5f7a5f9

Please sign in to comment.