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 15, 2023
1 parent 240c1b8 commit 85841a9
Show file tree
Hide file tree
Showing 74 changed files with 1,046 additions and 278 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.adoc
Expand Up @@ -62,6 +62,8 @@ All visible tree items containing the value typed in the filter bar will be high
The filter button inside the filter bar allows to filter (hide) all visible tree items not containing the value typed in the filter bar.
+
image:doc/screenshots/filterBarFilterButton.png[Filter Bar Filter Button,30%,30%]
- 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.

- https://github.com/eclipse-sirius/sirius-components/issues/1830[#1830] [layout] This feature is experimental and can be activated on a diagram by adding "__EXPERIMENTAL" to its name. The new algorithm does the minimum possible to place node without overlap.

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
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2022 Obeo.
* Copyright (c) 2022, 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
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 = Objects.requireNonNull(widgetDescriptors);
this.customWidgetConverterProviders = Objects.requireNonNull(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
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2022 Obeo.
* Copyright (c) 2022, 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
Expand All @@ -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 = Objects.requireNonNull(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
Expand Up @@ -14,6 +14,7 @@

import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;
import java.util.Optional;
import java.util.UUID;

Expand Down Expand Up @@ -50,7 +51,7 @@ public Optional<Object> getObject(IEditingContext editingContext, String objectI
return Optional.of(ViewFactory.eINSTANCE.createFlexboxContainerDescription());
}
};
var handler = new AddWidgetEventHandler(objectService, new ICollaborativeFormDescriptionEditorMessageService.NoOp(), new SimpleMeterRegistry());
var handler = new AddWidgetEventHandler(objectService, new ICollaborativeFormDescriptionEditorMessageService.NoOp(), List.of(), new SimpleMeterRegistry());
var input = new AddWidgetInput(UUID.randomUUID(), "editingContextId", "representationId", "containerId", "Checkbox", 0);

assertThat(handler.canHandle(input)).isTrue();
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 comments on commit 85841a9

Please sign in to comment.