diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 43892fad1d1..031800075a4 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -69,6 +69,10 @@ More existing APIs will be migrated to this new common pattern. - https://github.com/eclipse-sirius/sirius-web/issues/3553[#3553] [core] Add RepresentationFactory extension point - https://github.com/eclipse-sirius/sirius-web/issues/3587[#3587] [sirius-web] Add an extension point to contribute new project cards - https://github.com/eclipse-sirius/sirius-web/issues/3614[#3614] [core] Add the ability to contribute additional payloads to representation subscriptions +- https://github.com/eclipse-sirius/sirius-web/issues/3429[#3429] [diagram] Add a diagram filter dialog in the diagram panel. +This dialog presents diagram elements in a tree and allows to select them and update their visibility and status (e.g. hide, show, collapse). ++ +image:doc/screenshots/diagramFilterView.png[Diagram Filter View, 70%] === Improvements diff --git a/doc/screenshots/diagramFilterView.png b/doc/screenshots/diagramFilterView.png new file mode 100644 index 00000000000..f1f50d69f41 Binary files /dev/null and b/doc/screenshots/diagramFilterView.png differ diff --git a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/api/DiagramImageConstants.java b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/api/DiagramImageConstants.java index cb3731f7d1d..08bd282b679 100644 --- a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/api/DiagramImageConstants.java +++ b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/api/DiagramImageConstants.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022, 2023 Obeo. + * Copyright (c) 2022, 2024 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 @@ -31,6 +31,14 @@ public final class DiagramImageConstants { public static final String GRAPHICAL_DELETE_SVG = IMAGES_ROOT_FOLDER + "/graphicalDelete.svg"; + public static final String PIN_SVG = IMAGES_ROOT_FOLDER + "/pin.svg"; + + public static final String UNPIN_SVG = IMAGES_ROOT_FOLDER + "/unpin.svg"; + + public static final String FADE_SVG = IMAGES_ROOT_FOLDER + "/fade.svg"; + + public static final String REVEAL_FADED_ELEMENTS_SVG = IMAGES_ROOT_FOLDER + "/reveal-faded-elements.svg"; + private DiagramImageConstants() { // Prevent instantiation } diff --git a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/resources/diagram-images/fade.svg b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/resources/diagram-images/fade.svg new file mode 100644 index 00000000000..e590c0e6951 --- /dev/null +++ b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/resources/diagram-images/fade.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/resources/diagram-images/pin.svg b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/resources/diagram-images/pin.svg new file mode 100644 index 00000000000..8ba9f03dfc6 --- /dev/null +++ b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/resources/diagram-images/pin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/resources/diagram-images/reveal-faded-elements.svg b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/resources/diagram-images/reveal-faded-elements.svg new file mode 100644 index 00000000000..189c670b41b --- /dev/null +++ b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/resources/diagram-images/reveal-faded-elements.svg @@ -0,0 +1,43 @@ + + + + + + + diff --git a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/resources/diagram-images/unpin.svg b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/resources/diagram-images/unpin.svg new file mode 100644 index 00000000000..11ed10940cb --- /dev/null +++ b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/resources/diagram-images/unpin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/diagrams/backend/sirius-components-diagrams-tests/src/main/java/org/eclipse/sirius/components/diagrams/tests/graphql/FadeDiagramElementMutationRunner.java b/packages/diagrams/backend/sirius-components-diagrams-tests/src/main/java/org/eclipse/sirius/components/diagrams/tests/graphql/FadeDiagramElementMutationRunner.java new file mode 100644 index 00000000000..d35df93b2b9 --- /dev/null +++ b/packages/diagrams/backend/sirius-components-diagrams-tests/src/main/java/org/eclipse/sirius/components/diagrams/tests/graphql/FadeDiagramElementMutationRunner.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2024, 2024 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.diagrams.tests.graphql; + +import java.util.Objects; + +import org.eclipse.sirius.components.collaborative.diagrams.dto.FadeDiagramElementInput; +import org.eclipse.sirius.components.graphql.tests.api.IGraphQLRequestor; +import org.eclipse.sirius.components.graphql.tests.api.IMutationRunner; +import org.springframework.stereotype.Service; + +/** + * Used to fade or reveal faded elements with the GraphQL API. + * + * @author sbegaudeau + */ +@Service +public class FadeDiagramElementMutationRunner implements IMutationRunner { + + private static final String FADE_DIAGRAM_ELEMENT_MUTATION = """ + mutation fadeDiagramElement($input: FadeDiagramElementInput!) { + fadeDiagramElement(input: $input) { + __typename + } + } + """; + + private final IGraphQLRequestor graphQLRequestor; + + public FadeDiagramElementMutationRunner(IGraphQLRequestor graphQLRequestor) { + this.graphQLRequestor = Objects.requireNonNull(graphQLRequestor); + } + + @Override + public String run(FadeDiagramElementInput input) { + return this.graphQLRequestor.execute(FADE_DIAGRAM_ELEMENT_MUTATION, input); + } +} diff --git a/packages/diagrams/backend/sirius-components-diagrams-tests/src/main/java/org/eclipse/sirius/components/diagrams/tests/graphql/HideDiagramElementMutationRunner.java b/packages/diagrams/backend/sirius-components-diagrams-tests/src/main/java/org/eclipse/sirius/components/diagrams/tests/graphql/HideDiagramElementMutationRunner.java new file mode 100644 index 00000000000..730c2c322f5 --- /dev/null +++ b/packages/diagrams/backend/sirius-components-diagrams-tests/src/main/java/org/eclipse/sirius/components/diagrams/tests/graphql/HideDiagramElementMutationRunner.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2024, 2024 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.diagrams.tests.graphql; + +import java.util.Objects; + +import org.eclipse.sirius.components.collaborative.diagrams.dto.HideDiagramElementInput; +import org.eclipse.sirius.components.graphql.tests.api.IGraphQLRequestor; +import org.eclipse.sirius.components.graphql.tests.api.IMutationRunner; +import org.springframework.stereotype.Service; + +/** + * Used to hide or show faded elements with the GraphQL API. + * + * @author sbegaudeau + */ +@Service +public class HideDiagramElementMutationRunner implements IMutationRunner { + + private static final String HIDE_DIAGRAM_ELEMENT_MUTATION = """ + mutation hideDiagramElement($input: HideDiagramElementInput!) { + hideDiagramElement(input: $input) { + __typename + } + } + """; + + private final IGraphQLRequestor graphQLRequestor; + + public HideDiagramElementMutationRunner(IGraphQLRequestor graphQLRequestor) { + this.graphQLRequestor = Objects.requireNonNull(graphQLRequestor); + } + + @Override + public String run(HideDiagramElementInput input) { + return this.graphQLRequestor.execute(HIDE_DIAGRAM_ELEMENT_MUTATION, input); + } +} diff --git a/packages/diagrams/backend/sirius-components-diagrams-tests/src/main/java/org/eclipse/sirius/components/diagrams/tests/graphql/PinDiagramElementMutationRunner.java b/packages/diagrams/backend/sirius-components-diagrams-tests/src/main/java/org/eclipse/sirius/components/diagrams/tests/graphql/PinDiagramElementMutationRunner.java new file mode 100644 index 00000000000..6123804e344 --- /dev/null +++ b/packages/diagrams/backend/sirius-components-diagrams-tests/src/main/java/org/eclipse/sirius/components/diagrams/tests/graphql/PinDiagramElementMutationRunner.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2024, 2024 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.diagrams.tests.graphql; + +import org.eclipse.sirius.components.collaborative.diagrams.dto.PinDiagramElementInput; +import org.eclipse.sirius.components.graphql.tests.api.IGraphQLRequestor; +import org.eclipse.sirius.components.graphql.tests.api.IMutationRunner; +import org.springframework.stereotype.Service; + +/** + * Used to pin or unpin elements with the GraphQL API. + * + * @author sbegaudeau + */ +@Service +public class PinDiagramElementMutationRunner implements IMutationRunner { + + private static final String PIN_DIAGRAM_ELEMENT_MUTATION = """ + mutation pinDiagramElement($input: PinDiagramElementInput!) { + pinDiagramElement(input: $input) { + __typename + } + } + """; + + private final IGraphQLRequestor graphQLRequestor; + + public PinDiagramElementMutationRunner(IGraphQLRequestor graphQLRequestor) { + this.graphQLRequestor = graphQLRequestor; + } + + @Override + public String run(PinDiagramElementInput input) { + return this.graphQLRequestor.execute(PIN_DIAGRAM_ELEMENT_MUTATION, input); + } +} diff --git a/packages/diagrams/backend/sirius-components-diagrams-tests/src/main/java/org/eclipse/sirius/components/diagrams/tests/navigation/DiagramNavigator.java b/packages/diagrams/backend/sirius-components-diagrams-tests/src/main/java/org/eclipse/sirius/components/diagrams/tests/navigation/DiagramNavigator.java index 3f2e5953338..ef3bea19325 100644 --- a/packages/diagrams/backend/sirius-components-diagrams-tests/src/main/java/org/eclipse/sirius/components/diagrams/tests/navigation/DiagramNavigator.java +++ b/packages/diagrams/backend/sirius-components-diagrams-tests/src/main/java/org/eclipse/sirius/components/diagrams/tests/navigation/DiagramNavigator.java @@ -41,6 +41,14 @@ public NodeNavigator nodeWithLabel(String label) { return new NodeNavigator(nodes.get(0), this.cache); } + public NodeNavigator nodeWithId(String id) { + Node node = this.cache.getIdToNode().get(id); + if (node == null) { + throw new IllegalArgumentException(MessageFormat.format("No node found with id \"{0}\"", id)); + } + return new NodeNavigator(node, this.cache); + } + public NodeNavigator nodeWithTargetObjectLabel(String targetObjectLabel) { List nodes = this.cache.getTargetObjectLabelToNodes().get(targetObjectLabel); if (nodes == null || nodes.isEmpty()) { diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/index.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/index.ts index a1a58ad62c6..592e31e8edf 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/index.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/index.ts @@ -52,5 +52,7 @@ export type { DiagramPaletteToolContextValue } from './renderer/palette/DiagramP export { DiagramPaletteToolContext } from './renderer/palette/DiagramPaletteToolContext'; export { DiagramPaletteToolContribution } from './renderer/palette/DiagramPaletteToolContribution'; export type { DiagramPaletteToolContributionComponentProps } from './renderer/palette/DiagramPaletteToolContribution.types'; +export type { DiagramPanelActionProps } from './renderer/panel/DiagramPanel.types'; +export { diagramPanelActionExtensionPoint } from './renderer/panel/DiagramPanelExtensionPoints'; export { DiagramRepresentation } from './representation/DiagramRepresentation'; export type { GQLDiagramDescription } from './representation/DiagramRepresentation.types'; diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/panel/DiagramPanel.tsx b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/panel/DiagramPanel.tsx index 37a8b42a807..c43d8e98917 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/panel/DiagramPanel.tsx +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/panel/DiagramPanel.tsx @@ -11,9 +11,9 @@ * Obeo - initial API and implementation *******************************************************************************/ -import { ShareRepresentationModal } from '@eclipse-sirius/sirius-components-core'; -import IconButton from '@material-ui/core/IconButton'; +import { ComponentExtension, ShareRepresentationModal, useComponents } from '@eclipse-sirius/sirius-components-core'; import CircularProgress from '@material-ui/core/CircularProgress'; +import IconButton from '@material-ui/core/IconButton'; import Paper from '@material-ui/core/Paper'; import Tooltip from '@material-ui/core/Tooltip'; import AccountTreeIcon from '@material-ui/icons/AccountTree'; @@ -41,7 +41,8 @@ import { useFullscreen } from '../fullscreen/useFullscreen'; import { useHideDiagramElements } from '../hide/useHideDiagramElements'; import { useArrangeAll } from '../layout/useArrangeAll'; import { usePinDiagramElements } from '../pin/usePinDiagramElements'; -import { DiagramPanelProps, DiagramPanelState } from './DiagramPanel.types'; +import { DiagramPanelActionProps, DiagramPanelProps, DiagramPanelState } from './DiagramPanel.types'; +import { diagramPanelActionExtensionPoint } from './DiagramPanelExtensionPoints'; import { useExportToImage } from './useExportToImage'; export const DiagramPanel = memo( @@ -53,6 +54,9 @@ export const DiagramPanel = memo( }); const { readOnly } = useContext(DiagramContext); + const diagramPanelActionComponents: ComponentExtension[] = useComponents( + diagramPanelActionExtensionPoint + ); const { getNodes, getEdges, zoomIn, zoomOut, fitView } = useReactFlow(); @@ -226,6 +230,9 @@ export const DiagramPanel = memo( + {diagramPanelActionComponents.map(({ Component: DiagramPanelActionComponent }, index) => ( + + ))} {state.dialogOpen === 'Share' ? ( diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/panel/DiagramPanel.types.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/panel/DiagramPanel.types.ts index f76bbaa4d55..537d723e7bc 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/panel/DiagramPanel.types.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/panel/DiagramPanel.types.ts @@ -26,3 +26,8 @@ export interface DiagramPanelState { } export type DiagramPanelDialog = 'Share'; + +export interface DiagramPanelActionProps { + editingContextId: string; + diagramId: string; +} diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/panel/DiagramPanelExtensionPoints.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/panel/DiagramPanelExtensionPoints.ts new file mode 100644 index 00000000000..28d83937531 --- /dev/null +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/panel/DiagramPanelExtensionPoints.ts @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2024 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 + *******************************************************************************/ + +import { ComponentExtensionPoint } from '@eclipse-sirius/sirius-components-core'; +import { DiagramPanelActionProps } from './DiagramPanel.types'; + +export const diagramPanelActionExtensionPoint: ComponentExtensionPoint = { + identifier: 'diagramPanel#action', + FallbackComponent: () => null, +}; diff --git a/packages/forms/backend/sirius-components-forms-tests/src/main/java/org/eclipse/sirius/components/forms/tests/graphql/EditTreeCheckboxMutationRunner.java b/packages/forms/backend/sirius-components-forms-tests/src/main/java/org/eclipse/sirius/components/forms/tests/graphql/EditTreeCheckboxMutationRunner.java new file mode 100644 index 00000000000..60d460f8d6c --- /dev/null +++ b/packages/forms/backend/sirius-components-forms-tests/src/main/java/org/eclipse/sirius/components/forms/tests/graphql/EditTreeCheckboxMutationRunner.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2024 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.forms.tests.graphql; + +import java.util.Objects; + +import org.eclipse.sirius.components.collaborative.forms.dto.EditTreeCheckboxInput; +import org.eclipse.sirius.components.graphql.tests.api.IGraphQLRequestor; +import org.eclipse.sirius.components.graphql.tests.api.IMutationRunner; +import org.springframework.stereotype.Service; + +/** + * Used to edit a tree checkbox with the GraphQL API. + * + * @author gdaniel + */ +@Service +public class EditTreeCheckboxMutationRunner implements IMutationRunner { + + private static final String EDIT_TREE_CHECKBOX_MUTATION = """ + mutation editTreeCheckbox($input: EditTreeCheckboxInput!) { + editTreeCheckbox(input: $input) { + __typename + ... on ErrorPayload { + messages { + body + level + } + } + ... on SuccessPayload { + messages { + body + level + } + } + } + } + """; + + private final IGraphQLRequestor graphQLRequestor; + + public EditTreeCheckboxMutationRunner(IGraphQLRequestor graphQLRequestor) { + this.graphQLRequestor = Objects.requireNonNull(graphQLRequestor); + } + + @Override + public String run(EditTreeCheckboxInput input) { + return this.graphQLRequestor.execute(EDIT_TREE_CHECKBOX_MUTATION, input); + } + +} diff --git a/packages/forms/backend/sirius-components-forms-tests/src/main/java/org/eclipse/sirius/components/forms/tests/graphql/PushButtonMutationRunner.java b/packages/forms/backend/sirius-components-forms-tests/src/main/java/org/eclipse/sirius/components/forms/tests/graphql/PushButtonMutationRunner.java new file mode 100644 index 00000000000..d3a256b5fe1 --- /dev/null +++ b/packages/forms/backend/sirius-components-forms-tests/src/main/java/org/eclipse/sirius/components/forms/tests/graphql/PushButtonMutationRunner.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2024 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.forms.tests.graphql; + +import java.util.Objects; + +import org.eclipse.sirius.components.collaborative.forms.dto.PushButtonInput; +import org.eclipse.sirius.components.graphql.tests.api.IGraphQLRequestor; +import org.eclipse.sirius.components.graphql.tests.api.IMutationRunner; +import org.springframework.stereotype.Service; + +/** + * Used to push a button with the GraphQL API. + * + * @author gdaniel + */ +@Service +public class PushButtonMutationRunner implements IMutationRunner { + + private static final String PUSH_BUTTON_MUTATION = """ + mutation pushButton($input: PushButtonInput!) { + pushButton(input: $input) { + __typename + ... on ErrorPayload { + messages { + body + level + } + } + ... on SuccessPayload { + messages { + body + level + } + } + } + } + """; + + private final IGraphQLRequestor graphQLRequestor; + + public PushButtonMutationRunner(IGraphQLRequestor graphQLRequestor) { + this.graphQLRequestor = Objects.requireNonNull(graphQLRequestor); + } + + @Override + public String run(PushButtonInput input) { + return this.graphQLRequestor.execute(PUSH_BUTTON_MUTATION, input); + } + +} diff --git a/packages/forms/frontend/sirius-components-forms/src/index.ts b/packages/forms/frontend/sirius-components-forms/src/index.ts index fdcaaeb92bc..7c33d13236d 100644 --- a/packages/forms/frontend/sirius-components-forms/src/index.ts +++ b/packages/forms/frontend/sirius-components-forms/src/index.ts @@ -15,6 +15,8 @@ export * from './form/FormContext'; export * from './form/FormContext.types'; export * from './form/FormEventFragments'; export * from './form/FormEventFragments.types'; +export * from './groups/Group'; +export * from './groups/Group.types'; export type { ButtonStyleProps } from './propertysections/ButtonPropertySection.types'; export type { CheckboxStyleProps } from './propertysections/CheckboxPropertySection.types'; export type { DateTimeStyleProps } from './propertysections/DateTimeWidgetPropertySection.types'; @@ -33,6 +35,8 @@ export * from './representations/FormRepresentation'; export * from './views/DetailsView'; export * from './views/DetailsViewConfiguration'; export * from './views/DetailsViewConfiguration.types'; +export * from './views/FormBasedView'; +export * from './views/FormBasedView.types'; export * from './views/FormConverter.types'; export * from './views/RelatedElementsView'; export * from './views/RepresentationsView'; diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/controllers/SubscriptionDiagramFilterEventDataFetcher.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/controllers/SubscriptionDiagramFilterEventDataFetcher.java new file mode 100644 index 00000000000..6fba4cb0f72 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/controllers/SubscriptionDiagramFilterEventDataFetcher.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.application.diagram.controllers; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import org.eclipse.sirius.components.annotations.spring.graphql.SubscriptionDataFetcher; +import org.eclipse.sirius.components.collaborative.forms.dto.PropertiesEventInput; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; +import org.eclipse.sirius.components.graphql.api.IEventProcessorSubscriptionProvider; +import org.eclipse.sirius.components.graphql.api.IExceptionWrapper; +import org.eclipse.sirius.components.graphql.api.LocalContextConstants; +import org.eclipse.sirius.web.application.diagram.services.filter.api.DiagramFilterConfiguration; +import org.reactivestreams.Publisher; + +import graphql.execution.DataFetcherResult; +import graphql.schema.DataFetchingEnvironment; + +/** + * The data fetcher used to send the refreshed diagram filters to a subscription. + * + * @author gdaniel + */ +@SubscriptionDataFetcher(type = "Subscription", field = "diagramFilterEvent") +public class SubscriptionDiagramFilterEventDataFetcher implements IDataFetcherWithFieldCoordinates>> { + + private static final String INPUT_ARGUMENT = "input"; + + private final ObjectMapper objectMapper; + + private final IExceptionWrapper exceptionWrapper; + + private final IEventProcessorSubscriptionProvider eventProcessorSubscriptionProvider; + + public SubscriptionDiagramFilterEventDataFetcher(ObjectMapper objectMapper, IExceptionWrapper exceptionWrapper, IEventProcessorSubscriptionProvider eventProcessorSubscriptionProvider) { + this.objectMapper = Objects.requireNonNull(objectMapper); + this.exceptionWrapper = Objects.requireNonNull(exceptionWrapper); + this.eventProcessorSubscriptionProvider = Objects.requireNonNull(eventProcessorSubscriptionProvider); + } + + @Override + public Publisher> get(DataFetchingEnvironment environment) throws Exception { + Object argument = environment.getArgument(INPUT_ARGUMENT); + var input = this.objectMapper.convertValue(argument, PropertiesEventInput.class); + var diagramFilterConfiguration = new DiagramFilterConfiguration(input.id().toString(), input.objectIds()); + + Map localContext = new HashMap<>(); + localContext.put(LocalContextConstants.EDITING_CONTEXT_ID, input.editingContextId()); + localContext.put(LocalContextConstants.REPRESENTATION_ID, diagramFilterConfiguration.getId()); + + return this.exceptionWrapper.wrapFlux(() -> this.eventProcessorSubscriptionProvider.getSubscription(input.editingContextId(), diagramFilterConfiguration, input), input) + .map(payload -> DataFetcherResult.newResult() + .data(payload) + .localContext(localContext) + .build()); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/CollapseButtonDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/CollapseButtonDescriptionProvider.java new file mode 100644 index 00000000000..54920f09fb3 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/CollapseButtonDescriptionProvider.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.application.diagram.services.filter; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.diagrams.api.DiagramImageConstants; +import org.eclipse.sirius.components.collaborative.diagrams.dto.UpdateCollapsingStateInput; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.diagrams.CollapsingState; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.forms.ButtonStyle; +import org.eclipse.sirius.components.forms.WidgetIdProvider; +import org.eclipse.sirius.components.forms.description.ButtonDescription; +import org.eclipse.sirius.components.representations.Failure; +import org.eclipse.sirius.components.representations.Success; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterActionContributionProvider; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterHelper; +import org.eclipse.sirius.web.domain.services.api.IMessageService; +import org.springframework.stereotype.Service; + +/** + * Provides the description of the collapse button for the diagram filter's split button. + * + * @author gdaniel + */ +@Service +public class CollapseButtonDescriptionProvider implements IDiagramFilterActionContributionProvider { + + private final IObjectService objectService; + + private final IDiagramFilterHelper diagramFilterHelper; + + private final IMessageService messageService; + + public CollapseButtonDescriptionProvider(IObjectService objectService, IDiagramFilterHelper diagramFilterHelper, IMessageService messageService) { + this.objectService = Objects.requireNonNull(objectService); + this.diagramFilterHelper = Objects.requireNonNull(diagramFilterHelper); + this.messageService = Objects.requireNonNull(messageService); + } + + @Override + public ButtonDescription getButtonDescription() { + return ButtonDescription.newButtonDescription("diagram-filter/split-button/collapse") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> "Collapse") + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .buttonLabelProvider(variableManager -> "Collapse") + .imageURLProvider(variableManager -> DiagramImageConstants.COLLAPSE_SVG) + .pushButtonHandler(variableManager -> { + var diagram = variableManager.get(DiagramFilterDescriptionProvider.DIAGRAM, Diagram.class).get(); + var nodeIds = this.diagramFilterHelper.getSelectedElementIds(variableManager); + var editingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).get(); + boolean hasFailure = nodeIds.stream() + .map(nodeId -> this.diagramFilterHelper.sendDiagramEvent(variableManager, new UpdateCollapsingStateInput(UUID.randomUUID(), editingContext.getId(), diagram.getId(), nodeId, CollapsingState.COLLAPSED))) + .anyMatch(Failure.class::isInstance); + if (hasFailure) { + return new Failure("An error occurred"); + } else { + return new Success(); + } + }) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> ButtonStyle.newButtonStyle() + .backgroundColor("#ffffff") + .foregroundColor("#261E58") + .build() + ) + .helpTextProvider(variableManager -> this.messageService.collapseSelectedElements()) + .build(); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/DiagramFilterDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/DiagramFilterDescriptionProvider.java new file mode 100644 index 00000000000..396bf31bca4 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/DiagramFilterDescriptionProvider.java @@ -0,0 +1,340 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.application.diagram.services.filter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Stream; + +import org.eclipse.sirius.components.collaborative.api.IEditingContextEventProcessor; +import org.eclipse.sirius.components.collaborative.api.IEditingContextEventProcessorRegistry; +import org.eclipse.sirius.components.collaborative.api.IRepresentationEventProcessor; +import org.eclipse.sirius.components.collaborative.diagrams.api.DiagramImageConstants; +import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramEventProcessor; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.diagrams.CollapsingState; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.diagrams.Node; +import org.eclipse.sirius.components.diagrams.ViewModifier; +import org.eclipse.sirius.components.forms.WidgetIdProvider; +import org.eclipse.sirius.components.forms.description.AbstractControlDescription; +import org.eclipse.sirius.components.forms.description.CheckboxDescription; +import org.eclipse.sirius.components.forms.description.FormDescription; +import org.eclipse.sirius.components.forms.description.GroupDescription; +import org.eclipse.sirius.components.forms.description.LabelDescription; +import org.eclipse.sirius.components.forms.description.PageDescription; +import org.eclipse.sirius.components.forms.description.SplitButtonDescription; +import org.eclipse.sirius.components.forms.description.TreeDescription; +import org.eclipse.sirius.components.representations.GetOrCreateRandomIdProvider; +import org.eclipse.sirius.components.representations.Success; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterActionContributionProvider; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterDescriptionProvider; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterHelper; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +/** + * Provides a tree description listing diagram elements and allowing to perform actions on them. + * + * @author gdaniel + */ +@Service +public class DiagramFilterDescriptionProvider implements IDiagramFilterDescriptionProvider { + + public static final String FORM_DESCRIPTION_ID = "diagram_filter_form_description"; + + public static final String SELECTED_TREE_NODES = "selectedTreeNodes"; + + public static final String EDITING_CONTEXT_EVENT_PROCESSOR = "editingContextEventProcessor"; + + public static final String DIAGRAM_EVENT_PROCESSOR = "diagramEventProcessor"; + + public static final String DIAGRAM = "diagram"; + + private static final String GROUP_DESCRIPTION_ID = "defaultDiagramFilterGroup"; + + private static final String PAGE_DESCRIPTION_ID = "defaultDiagramFilterPage"; + + private static final String FORM_TITLE = "Diagram Filters"; + + private final IObjectService objectService; + + private final IEditingContextEventProcessorRegistry editingContextEventProcessorRegistry; + + private final List diagramFilterActionContributionProviders; + + private final IDiagramFilterHelper diagramFilterHelper; + + public DiagramFilterDescriptionProvider(IObjectService objectService, @Lazy IEditingContextEventProcessorRegistry editingContextEventProcessorRegistry, List diagramFilterActionContributionProviders, IDiagramFilterHelper diagramFilterHelper) { + this.objectService = Objects.requireNonNull(objectService); + this.editingContextEventProcessorRegistry = Objects.requireNonNull(editingContextEventProcessorRegistry); + this.diagramFilterActionContributionProviders = Objects.requireNonNull(diagramFilterActionContributionProviders); + this.diagramFilterHelper = Objects.requireNonNull(diagramFilterHelper); + } + + @Override + public FormDescription getFormDescription() { + List groupDescriptions = List.of(this.getGroupDescription()); + + Function targetObjectIdProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) + .map(this.objectService::getId) + .orElse(null); + + return FormDescription.newFormDescription(FORM_DESCRIPTION_ID) + .label(FORM_TITLE) + .idProvider(new GetOrCreateRandomIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> FORM_TITLE) + .targetObjectIdProvider(targetObjectIdProvider) + .canCreatePredicate(variableManager -> false) + .variableManagerInitializer(vm -> { + if (this.editingContextEventProcessorRegistry != null) { + String editingContextId = vm.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).map(IEditingContext::getId).orElse(""); + Optional optDiagram = vm.get(VariableManager.SELF, Object.class) + .filter(Diagram.class::isInstance) + .map(Diagram.class::cast); + String diagramId = optDiagram + .map(Diagram::getId).orElse(null); + + IEditingContextEventProcessor editingContextEventProcessor = this.editingContextEventProcessorRegistry.getEditingContextEventProcessors().stream() + .filter(processor -> processor.getEditingContextId().equals(editingContextId)) + .findFirst() + .orElse(null); + IRepresentationEventProcessor diagramEventProcessor = Optional.ofNullable(editingContextEventProcessor) + .flatMap(processor -> processor.getRepresentationEventProcessors().stream() + .filter(IDiagramEventProcessor.class::isInstance) + .map(IDiagramEventProcessor.class::cast) + .filter(p -> p.getRepresentation().getId().equals(diagramId)) + .findFirst() + ) + .orElse(null); + vm.put(SELECTED_TREE_NODES, this.getCheckMap(optDiagram.get())); + vm.put(EDITING_CONTEXT_EVENT_PROCESSOR, editingContextEventProcessor); + vm.put(DIAGRAM_EVENT_PROCESSOR, diagramEventProcessor); + vm.put(DIAGRAM, diagramEventProcessor.getRepresentation()); + } + return vm; + }) + .pageDescriptions(List.of(this.getPageDescription(groupDescriptions))) + .build(); + } + + private PageDescription getPageDescription(List groupDescriptions) { + Function idProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) + .map(this.objectService::getId) + .orElseGet(() -> UUID.randomUUID().toString()); + + Function labelProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) + .map(this.objectService::getLabel) + .orElse(""); + + Function> semanticElementsProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class).stream().toList(); + + return PageDescription.newPageDescription(PAGE_DESCRIPTION_ID) + .idProvider(idProvider) + .labelProvider(labelProvider) + .semanticElementsProvider(semanticElementsProvider) + .groupDescriptions(groupDescriptions) + .canCreatePredicate(variableManager -> true) + .build(); + } + + private GroupDescription getGroupDescription() { + List controlDescriptions = new ArrayList<>(); + controlDescriptions.add(this.createTreeLabelDescription()); + controlDescriptions.add(this.createTreeCheckboxDescription()); + controlDescriptions.add(this.createTreeDescription()); + controlDescriptions.add(this.createSplitButtonDescription()); + + return GroupDescription.newGroupDescription(GROUP_DESCRIPTION_ID) + .idProvider(variableManager -> FORM_TITLE) + .labelProvider(variableManager -> "Filter elements") + .semanticElementsProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).stream().toList()) + .controlDescriptions(controlDescriptions) + .build(); + } + + private LabelDescription createTreeLabelDescription() { + return LabelDescription.newLabelDescription("diagram-filter/tree-label") + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .idProvider(new WidgetIdProvider()) + .labelProvider(variableManager -> "") + .valueProvider(variableManager -> "Elements on diagram") + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> null) + .build(); + } + + private CheckboxDescription createTreeCheckboxDescription() { + return CheckboxDescription.newCheckboxDescription("diagram-filter/tree-checkbox") + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .idProvider(new WidgetIdProvider()) + .labelProvider(variableManager -> { + int selectedElementCount = this.diagramFilterHelper.getSelectedElementIds(variableManager).size(); + String element = "element"; + if (selectedElementCount > 1) { + element += "s"; + } + return selectedElementCount + " " + element + " selected"; + }) + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .valueProvider(variableManager -> this.diagramFilterHelper.getSelectedElementIds(variableManager).size() > 0) + .newValueHandler((variableManager, newValue) -> { + Map checkMap = variableManager.get(SELECTED_TREE_NODES, Map.class).get(); + checkMap.entrySet().stream() + .forEach(entry -> entry.setValue(newValue)); + return new Success(); + }) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> null) + .build(); + } + + private TreeDescription createTreeDescription() { + return TreeDescription.newTreeDescription("diagram-filter/tree") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .labelProvider(variableManager -> "") + .nodeIdProvider(this::getNodeId) + .nodeLabelProvider(this::getNodeLabel) + .nodeIconURLProvider(vm -> List.of()) + .nodeEndIconsURLProvider(this::computeNodeEndIcons) + .nodeKindProvider(vm -> "") + .nodeSelectableProvider(vm -> true) + .childrenProvider(this::getNodeChildren) + .expandedNodeIdsProvider(vm -> List.of()) + .isCheckableProvider(variableManager -> true) + .checkedValueProvider(variableManager -> this.diagramFilterHelper.getSelectedElementIds(variableManager).contains(this.getNodeId(variableManager))) + .newCheckedValueHandler((variableManager, newValue) -> { + Map selectedTreeNodes = variableManager.get(SELECTED_TREE_NODES, Map.class).get(); + String selfId = this.getNodeId(variableManager); + selectedTreeNodes.put(selfId, newValue); + return new Success(); + }) + .build(); + } + + private SplitButtonDescription createSplitButtonDescription() { + return SplitButtonDescription.newSplitButtonDescription("diagram-filter/split-button") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> { + int selectedElementCount = this.diagramFilterHelper.getSelectedElementIds(variableManager).size(); + String element = "element"; + if (selectedElementCount > 1) { + element += "s"; + } + + return "Apply to " + selectedElementCount + " selected " + element + ": "; + }) + .diagnosticsProvider(variableManager -> List.of()) + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .actions(this.diagramFilterActionContributionProviders.stream() + .map(IDiagramFilterActionContributionProvider::getButtonDescription) + .toList() + ) + .build(); + + } + + private String getNodeId(VariableManager vm) { + var self = vm.get(VariableManager.SELF, Object.class).orElse(null); + if (self instanceof Node node) { + return node.getId(); + } else { + return ""; + } + } + + private String getNodeLabel(VariableManager vm) { + var self = vm.get(VariableManager.SELF, Object.class).orElse(null); + String result = ""; + if (self instanceof Node node) { + if (node.getInsideLabel() != null) { + result = node.getInsideLabel().getText(); + } else if (!node.getOutsideLabels().isEmpty()) { + result = node.getOutsideLabels().get(0).text(); + } else if (node.getTargetObjectLabel() != null) { + result = node.getTargetObjectLabel(); + } + } + return result; + } + + private List getNodeChildren(VariableManager vm) { + var self = vm.get(VariableManager.SELF, Object.class).orElse(null); + final List result; + if (self instanceof Diagram diagram) { + result = diagram.getNodes(); + } else if (self instanceof Node node) { + result = node.getChildNodes(); + } else { + result = List.of(); + } + return result; + } + + private List> computeNodeEndIcons(VariableManager vm) { + var node = vm.get(VariableManager.SELF, Object.class) + .filter(Node.class::isInstance) + .map(Node.class::cast) + .orElse(null); + List> result = new ArrayList<>(); + if (node.getModifiers().contains(ViewModifier.Hidden)) { + result.add(List.of("/icons/full/obj16/HideTool.svg")); + } + if (node.getModifiers().contains(ViewModifier.Faded)) { + result.add(List.of(DiagramImageConstants.FADE_SVG)); + } + if (node.getCollapsingState().equals(CollapsingState.COLLAPSED)) { + result.add(List.of(DiagramImageConstants.COLLAPSE_SVG)); + } + if (node.isPinned()) { + result.add(List.of(DiagramImageConstants.PIN_SVG)); + } + return result; + } + + private Map getCheckMap(Diagram diagram) { + Map checkMap = new HashMap<>(); + for (Node node : diagram.getNodes()) { + this.fillCheckMap(node, checkMap); + } + return checkMap; + } + + private void fillCheckMap(Node node, Map checkMap) { + checkMap.put(node.getId(), false); + Stream.concat(node.getChildNodes().stream(), node.getBorderNodes().stream()) + .forEach(n -> this.fillCheckMap(n, checkMap)); + } +} \ No newline at end of file diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/DiagramFilterEventProcessorFactory.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/DiagramFilterEventProcessorFactory.java new file mode 100644 index 00000000000..4da28711351 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/DiagramFilterEventProcessorFactory.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.application.diagram.services.filter; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import org.eclipse.sirius.components.collaborative.api.IRepresentationConfiguration; +import org.eclipse.sirius.components.collaborative.api.IRepresentationEventProcessor; +import org.eclipse.sirius.components.collaborative.api.IRepresentationEventProcessorFactory; +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.forms.FormEventProcessor; +import org.eclipse.sirius.components.collaborative.forms.api.FormCreationParameters; +import org.eclipse.sirius.components.collaborative.forms.api.IFormEventHandler; +import org.eclipse.sirius.components.collaborative.forms.api.IFormPostProcessor; +import org.eclipse.sirius.components.collaborative.forms.configuration.FormEventProcessorConfiguration; +import org.eclipse.sirius.components.collaborative.forms.configuration.FormEventProcessorFactoryConfiguration; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.forms.description.FormDescription; +import org.eclipse.sirius.components.forms.renderer.IWidgetDescriptor; +import org.eclipse.sirius.web.application.diagram.services.filter.api.DiagramFilterConfiguration; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterDescriptionProvider; +import org.springframework.stereotype.Service; + +/** + * Used to create the diagram filter event processors. + * + * @author gdaniel + */ +@Service +public class DiagramFilterEventProcessorFactory implements IRepresentationEventProcessorFactory { + + private final IDiagramFilterDescriptionProvider diagramFilterDescriptionProvider; + + private final IObjectService objectService; + + private final IRepresentationSearchService representationSearchService; + + private final List widgetDescriptors; + + private final List formEventHandlers; + + private final ISubscriptionManagerFactory subscriptionManagerFactory; + + private final IRepresentationRefreshPolicyRegistry representationRefreshPolicyRegistry; + + private final IFormPostProcessor formPostProcessor; + + public DiagramFilterEventProcessorFactory(RepresentationEventProcessorFactoryConfiguration configuration, IRepresentationSearchService representationSearchService, + IDiagramFilterDescriptionProvider diagramFilterDescriptionProvider, List widgetDescriptors, + FormEventProcessorFactoryConfiguration formConfiguration) { + this.diagramFilterDescriptionProvider = Objects.requireNonNull(diagramFilterDescriptionProvider); + this.objectService = Objects.requireNonNull(formConfiguration.getObjectService()); + this.representationSearchService = Objects.requireNonNull(representationSearchService); + this.widgetDescriptors = Objects.requireNonNull(widgetDescriptors); + this.formEventHandlers = Objects.requireNonNull(formConfiguration.getFormEventHandlers()); + this.subscriptionManagerFactory = Objects.requireNonNull(configuration.getSubscriptionManagerFactory()); + this.representationRefreshPolicyRegistry = Objects.requireNonNull(configuration.getRepresentationRefreshPolicyRegistry()); + this.formPostProcessor = Objects.requireNonNull(formConfiguration.getFormPostProcessor()); + } + + @Override + public boolean canHandle(IRepresentationConfiguration configuration) { + return configuration instanceof DiagramFilterConfiguration; + } + + @Override + public Optional createRepresentationEventProcessor(IRepresentationConfiguration configuration, + IEditingContext editingContext) { + if (configuration instanceof DiagramFilterConfiguration diagramFilterConfiguration && this.diagramFilterDescriptionProvider != null) { + var objects = diagramFilterConfiguration.getObjectIds().stream() + .map(objectId -> this.objectService.getObject(editingContext, objectId)) + .flatMap(Optional::stream) + .toList(); + if (!objects.isEmpty()) { + FormDescription formDescription = this.diagramFilterDescriptionProvider.getFormDescription(); + FormCreationParameters formCreationParameters = FormCreationParameters.newFormCreationParameters(diagramFilterConfiguration.getId()) + .editingContext(editingContext) + .formDescription(formDescription) + .object(objects.get(0)) + .selection(objects) + .build(); + + var formEventProcessorConfiguration = new FormEventProcessorConfiguration(editingContext, this.objectService, formCreationParameters, this.widgetDescriptors, this.formEventHandlers); + IRepresentationEventProcessor formEventProcessor = new FormEventProcessor(formEventProcessorConfiguration, this.subscriptionManagerFactory.create(), this.representationSearchService, this.representationRefreshPolicyRegistry, this.formPostProcessor); + + return Optional.of(formEventProcessor); + } + } + return Optional.empty(); + } +} diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/DiagramFilterHelper.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/DiagramFilterHelper.java new file mode 100644 index 00000000000..bdb57756d98 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/DiagramFilterHelper.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.application.diagram.services.filter; + +import java.text.MessageFormat; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.eclipse.sirius.components.collaborative.api.ChangeDescription; +import org.eclipse.sirius.components.collaborative.api.IEditingContextEventProcessor; +import org.eclipse.sirius.components.collaborative.api.IRepresentationEventProcessor; +import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramInput; +import org.eclipse.sirius.components.core.api.ErrorPayload; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.core.api.SuccessPayload; +import org.eclipse.sirius.components.representations.Failure; +import org.eclipse.sirius.components.representations.IStatus; +import org.eclipse.sirius.components.representations.Success; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import reactor.core.publisher.Sinks; +import reactor.core.publisher.Sinks.EmitResult; +import reactor.core.publisher.Sinks.Many; +import reactor.core.publisher.Sinks.One; + +/** + * Utility methods for the "Diagram Filter" view. + * + * @author gdaniel + */ +@Service +public class DiagramFilterHelper implements IDiagramFilterHelper { + + private final Logger logger = LoggerFactory.getLogger(DiagramFilterHelper.class); + + @Override + public Set getSelectedElementIds(VariableManager variableManager) { + return variableManager.get(DiagramFilterDescriptionProvider.SELECTED_TREE_NODES, Map.class) + .map(checkMap -> ((Map) checkMap).entrySet().stream() + .filter(e -> e.getValue().equals(Boolean.TRUE)) + .map(Map.Entry::getKey) + .collect(Collectors.toSet()) + ) + .orElse(Set.of()); + } + + @Override + public IStatus sendDiagramEvent(VariableManager variableManager, IDiagramInput diagramInput) { + Optional representationEventProcessor = variableManager.get(DiagramFilterDescriptionProvider.DIAGRAM_EVENT_PROCESSOR, IRepresentationEventProcessor.class); + Optional editingContextEventProcessor = variableManager.get(DiagramFilterDescriptionProvider.EDITING_CONTEXT_EVENT_PROCESSOR, IEditingContextEventProcessor.class); + + if (representationEventProcessor.isEmpty() || editingContextEventProcessor.isEmpty()) { + String errorMessage = "Cannot find the diagramEventProcessor or the editingContextEventProcessor"; + this.logger.warn(errorMessage); + return new Failure(errorMessage); + } + + One payloadSink = Sinks.one(); + Many changeDescriptions = Sinks.many().unicast().onBackpressureBuffer(); + Consumer errorConsumer = throwable -> this.logger.warn(throwable.getMessage(), throwable); + changeDescriptions.asFlux().subscribe(changeDescription -> { + editingContextEventProcessor.get().getRepresentationEventProcessors().forEach(ep -> ep.refresh(changeDescription)); + }, errorConsumer); + + representationEventProcessor.get().handle(payloadSink, changeDescriptions, diagramInput); + IPayload handlerResult = payloadSink.asMono().block(); + + IStatus result; + + EmitResult changeDescriptionEmitResult = changeDescriptions.tryEmitComplete(); + if (changeDescriptionEmitResult.isFailure()) { + String errorMessage = MessageFormat.format("An error has occurred while marking the publisher as complete: {0}", changeDescriptionEmitResult); + this.logger.warn(errorMessage); + result = new Failure(errorMessage); + } else { + if (handlerResult instanceof SuccessPayload) { + result = new Success(); + } else if (handlerResult instanceof ErrorPayload errorPayload) { + result = new Failure(errorPayload.message()); + } else { + result = new Failure("Unknown error"); + } + } + return result; + } + +} diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/DiagramFilterRefreshPolicyProvider.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/DiagramFilterRefreshPolicyProvider.java new file mode 100644 index 00000000000..f2480238512 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/DiagramFilterRefreshPolicyProvider.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.application.diagram.services.filter; + +import org.eclipse.sirius.components.collaborative.api.ChangeKind; +import org.eclipse.sirius.components.collaborative.api.IRepresentationRefreshPolicy; +import org.eclipse.sirius.components.collaborative.api.IRepresentationRefreshPolicyProvider; +import org.eclipse.sirius.components.collaborative.diagrams.DiagramChangeKind; +import org.eclipse.sirius.components.representations.IRepresentationDescription; +import org.springframework.stereotype.Service; + +/** + * The representation refresh policy provider for the "Diagram Filter" form representation. + * + * @author gdaniel + */ +@Service +public class DiagramFilterRefreshPolicyProvider implements IRepresentationRefreshPolicyProvider { + + + @Override + public boolean canHandle(IRepresentationDescription representationDescription) { + return DiagramFilterDescriptionProvider.FORM_DESCRIPTION_ID.equals(representationDescription.getId()); + } + + @Override + public IRepresentationRefreshPolicy getRepresentationRefreshPolicy(IRepresentationDescription representationDescription) { + return changeDescription -> { + boolean shouldRefresh = false; + shouldRefresh = shouldRefresh || ChangeKind.SEMANTIC_CHANGE.equals(changeDescription.getKind()); + shouldRefresh = shouldRefresh || DiagramChangeKind.DIAGRAM_LAYOUT_CHANGE.equals(changeDescription.getKind()); + shouldRefresh = shouldRefresh || DiagramChangeKind.DIAGRAM_ELEMENT_VISIBILITY_CHANGE.equals(changeDescription.getKind()); + shouldRefresh = shouldRefresh || DiagramChangeKind.DIAGRAM_ELEMENT_COLLAPSING_STATE_CHANGE.equals(changeDescription.getKind()); + return shouldRefresh; + }; + } + +} diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/ExpandButtonDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/ExpandButtonDescriptionProvider.java new file mode 100644 index 00000000000..b4cb47af1e3 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/ExpandButtonDescriptionProvider.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.application.diagram.services.filter; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.diagrams.api.DiagramImageConstants; +import org.eclipse.sirius.components.collaborative.diagrams.dto.UpdateCollapsingStateInput; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.diagrams.CollapsingState; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.forms.ButtonStyle; +import org.eclipse.sirius.components.forms.WidgetIdProvider; +import org.eclipse.sirius.components.forms.description.ButtonDescription; +import org.eclipse.sirius.components.representations.Failure; +import org.eclipse.sirius.components.representations.Success; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterActionContributionProvider; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterHelper; +import org.eclipse.sirius.web.domain.services.api.IMessageService; +import org.springframework.stereotype.Service; + +/** + * Provides the description of the expand button for the diagram filter's split button. + * + * @author gdaniel + */ +@Service +public class ExpandButtonDescriptionProvider implements IDiagramFilterActionContributionProvider { + + private final IObjectService objectService; + + private final IDiagramFilterHelper diagramFilterHelper; + + private final IMessageService messageService; + + public ExpandButtonDescriptionProvider(IObjectService objectService, IDiagramFilterHelper diagramFilterHelper, IMessageService messageService) { + this.objectService = Objects.requireNonNull(objectService); + this.diagramFilterHelper = Objects.requireNonNull(diagramFilterHelper); + this.messageService = Objects.requireNonNull(messageService); + } + + @Override + public ButtonDescription getButtonDescription() { + return ButtonDescription.newButtonDescription("diagram-filter/split-button/expand") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> "Expand") + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .buttonLabelProvider(variableManager -> "Expand") + .imageURLProvider(variableManager -> DiagramImageConstants.EXPAND_SVG) + .pushButtonHandler(variableManager -> { + var diagram = variableManager.get(DiagramFilterDescriptionProvider.DIAGRAM, Diagram.class).get(); + var nodeIds = this.diagramFilterHelper.getSelectedElementIds(variableManager); + var editingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).get(); + boolean hasFailure = nodeIds.stream() + .map(nodeId -> this.diagramFilterHelper.sendDiagramEvent(variableManager, new UpdateCollapsingStateInput(UUID.randomUUID(), editingContext.getId(), diagram.getId(), nodeId, CollapsingState.EXPANDED))) + .anyMatch(Failure.class::isInstance); + if (hasFailure) { + return new Failure("An error occurred"); + } else { + return new Success(); + } + }) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> ButtonStyle.newButtonStyle() + .backgroundColor("#ffffff") + .foregroundColor("#261E58") + .build() + ) + .helpTextProvider(variableManager -> this.messageService.expandSelectedElements()) + .build(); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/FadeButtonDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/FadeButtonDescriptionProvider.java new file mode 100644 index 00000000000..15324d1db09 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/FadeButtonDescriptionProvider.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.application.diagram.services.filter; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.diagrams.api.DiagramImageConstants; +import org.eclipse.sirius.components.collaborative.diagrams.dto.FadeDiagramElementInput; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.forms.ButtonStyle; +import org.eclipse.sirius.components.forms.WidgetIdProvider; +import org.eclipse.sirius.components.forms.description.ButtonDescription; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterActionContributionProvider; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterHelper; +import org.eclipse.sirius.web.domain.services.api.IMessageService; +import org.springframework.stereotype.Service; + +/** + * Provides the description of the fade button for the diagram filter's split button. + * + * @author gdaniel + */ +@Service +public class FadeButtonDescriptionProvider implements IDiagramFilterActionContributionProvider { + + private final IObjectService objectService; + + private final IDiagramFilterHelper diagramFilterHelper; + + private final IMessageService messageService; + + public FadeButtonDescriptionProvider(IObjectService objectService, IDiagramFilterHelper diagramFilterHelper, IMessageService messageService) { + this.objectService = Objects.requireNonNull(objectService); + this.diagramFilterHelper = Objects.requireNonNull(diagramFilterHelper); + this.messageService = Objects.requireNonNull(messageService); + } + + @Override + public ButtonDescription getButtonDescription() { + return ButtonDescription.newButtonDescription("diagram-filter/split-button/fade") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> "Fade") + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .buttonLabelProvider(variableManager -> "Fade") + .imageURLProvider(variableManager -> DiagramImageConstants.FADE_SVG) + .pushButtonHandler(variableManager -> { + var diagram = variableManager.get(DiagramFilterDescriptionProvider.DIAGRAM, Diagram.class).get(); + var nodeIds = this.diagramFilterHelper.getSelectedElementIds(variableManager); + var editingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).get(); + return this.diagramFilterHelper.sendDiagramEvent(variableManager, new FadeDiagramElementInput(UUID.randomUUID(), editingContext.getId(), diagram.getId(), nodeIds, true)); + }) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> ButtonStyle.newButtonStyle() + .backgroundColor("#ffffff") + .foregroundColor("#261E58") + .build() + ) + .helpTextProvider(variableManager -> this.messageService.fadeSelectedElements()) + .build(); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/HideButtonDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/HideButtonDescriptionProvider.java new file mode 100644 index 00000000000..b5a2487e550 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/HideButtonDescriptionProvider.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.application.diagram.services.filter; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.diagrams.dto.HideDiagramElementInput; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.forms.ButtonStyle; +import org.eclipse.sirius.components.forms.WidgetIdProvider; +import org.eclipse.sirius.components.forms.description.ButtonDescription; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterActionContributionProvider; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterHelper; +import org.eclipse.sirius.web.domain.services.api.IMessageService; +import org.springframework.stereotype.Service; + +/** + * Provides the description of the hide button for the diagram filter's split button. + * + * @author gdaniel + */ +@Service +public class HideButtonDescriptionProvider implements IDiagramFilterActionContributionProvider { + + private final IObjectService objectService; + + private final IDiagramFilterHelper diagramFilterHelper; + + private final IMessageService messageService; + + public HideButtonDescriptionProvider(IObjectService objectService, IDiagramFilterHelper diagramFilterHelper, IMessageService messageService) { + this.objectService = Objects.requireNonNull(objectService); + this.diagramFilterHelper = Objects.requireNonNull(diagramFilterHelper); + this.messageService = Objects.requireNonNull(messageService); + } + + @Override + public ButtonDescription getButtonDescription() { + return ButtonDescription.newButtonDescription("diagram-filter/split-button/hide") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> "Hide") + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .buttonLabelProvider(variableManager -> "Hide") + .imageURLProvider(variableManager -> "/icons/full/obj16/HideTool.svg") + .pushButtonHandler(variableManager -> { + var diagram = variableManager.get(DiagramFilterDescriptionProvider.DIAGRAM, Diagram.class).get(); + var nodeIds = this.diagramFilterHelper.getSelectedElementIds(variableManager); + var editingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).get(); + return this.diagramFilterHelper.sendDiagramEvent(variableManager, new HideDiagramElementInput(UUID.randomUUID(), editingContext.getId(), diagram.getId(), nodeIds, true)); + }) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> ButtonStyle.newButtonStyle() + .backgroundColor("#ffffff") + .foregroundColor("#261E58") + .build() + ) + .helpTextProvider(variableManager -> this.messageService.hideSelectedElements()) + .build(); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/PinButtonDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/PinButtonDescriptionProvider.java new file mode 100644 index 00000000000..72dbb3db053 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/PinButtonDescriptionProvider.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.application.diagram.services.filter; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.diagrams.api.DiagramImageConstants; +import org.eclipse.sirius.components.collaborative.diagrams.dto.PinDiagramElementInput; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.forms.ButtonStyle; +import org.eclipse.sirius.components.forms.WidgetIdProvider; +import org.eclipse.sirius.components.forms.description.ButtonDescription; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterActionContributionProvider; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterHelper; +import org.eclipse.sirius.web.domain.services.api.IMessageService; +import org.springframework.stereotype.Service; + +/** + * Provides the description of the pin button for the diagram filter's split button. + * + * @author gdaniel + */ +@Service +public class PinButtonDescriptionProvider implements IDiagramFilterActionContributionProvider { + + private final IObjectService objectService; + + private final IDiagramFilterHelper diagramFilterHelper; + + private final IMessageService messageService; + + public PinButtonDescriptionProvider(IObjectService objectService, IDiagramFilterHelper diagramFilterHelper, IMessageService messageService) { + this.objectService = Objects.requireNonNull(objectService); + this.diagramFilterHelper = Objects.requireNonNull(diagramFilterHelper); + this.messageService = Objects.requireNonNull(messageService); + + } + + @Override + public ButtonDescription getButtonDescription() { + return ButtonDescription.newButtonDescription("diagram-filter/split-button/pin") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> "Pin") + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .buttonLabelProvider(variableManager -> "Pin") + .imageURLProvider(variableManager -> DiagramImageConstants.PIN_SVG) + .pushButtonHandler(variableManager -> { + var diagram = variableManager.get(DiagramFilterDescriptionProvider.DIAGRAM, Diagram.class).get(); + var nodeIds = this.diagramFilterHelper.getSelectedElementIds(variableManager); + var editingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).get(); + return this.diagramFilterHelper.sendDiagramEvent(variableManager, new PinDiagramElementInput(UUID.randomUUID(), editingContext.getId(), diagram.getId(), nodeIds, true)); + }) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> ButtonStyle.newButtonStyle() + .backgroundColor("#ffffff") + .foregroundColor("#261E58") + .build() + ) + .helpTextProvider(variableManager -> this.messageService.pinSelectedElements()) + .build(); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/RevealFadedElementsButtonDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/RevealFadedElementsButtonDescriptionProvider.java new file mode 100644 index 00000000000..7dcef24942b --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/RevealFadedElementsButtonDescriptionProvider.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.application.diagram.services.filter; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.diagrams.api.DiagramImageConstants; +import org.eclipse.sirius.components.collaborative.diagrams.dto.FadeDiagramElementInput; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.forms.ButtonStyle; +import org.eclipse.sirius.components.forms.WidgetIdProvider; +import org.eclipse.sirius.components.forms.description.ButtonDescription; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterActionContributionProvider; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterHelper; +import org.eclipse.sirius.web.domain.services.api.IMessageService; +import org.springframework.stereotype.Service; + +/** + * Provides the description of the reveal faded elements button for the diagram filter's split button. + * + * @author gdaniel + */ +@Service +public class RevealFadedElementsButtonDescriptionProvider implements IDiagramFilterActionContributionProvider { + + private final IObjectService objectService; + + private final IDiagramFilterHelper diagramFilterHelper; + + private final IMessageService messageService; + + public RevealFadedElementsButtonDescriptionProvider(IObjectService objectService, IDiagramFilterHelper diagramFilterHelper, IMessageService messageService) { + this.objectService = Objects.requireNonNull(objectService); + this.diagramFilterHelper = Objects.requireNonNull(diagramFilterHelper); + this.messageService = Objects.requireNonNull(messageService); + } + + @Override + public ButtonDescription getButtonDescription() { + return ButtonDescription.newButtonDescription("diagram-filter/split-button/reveal-faded-elements") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> "Reveal faded elements") + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .buttonLabelProvider(variableManager -> "Reveal faded elements") + .imageURLProvider(variableManager -> DiagramImageConstants.REVEAL_FADED_ELEMENTS_SVG) + .pushButtonHandler(variableManager -> { + var diagram = variableManager.get(DiagramFilterDescriptionProvider.DIAGRAM, Diagram.class).get(); + var nodeIds = this.diagramFilterHelper.getSelectedElementIds(variableManager); + var editingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).get(); + return this.diagramFilterHelper.sendDiagramEvent(variableManager, new FadeDiagramElementInput(UUID.randomUUID(), editingContext.getId(), diagram.getId(), nodeIds, false)); + }) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> ButtonStyle.newButtonStyle() + .backgroundColor("#ffffff") + .foregroundColor("#261E58") + .build() + ) + .helpTextProvider(variableManager -> this.messageService.revealSelectedFadedElements()) + .build(); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/ShowButtonDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/ShowButtonDescriptionProvider.java new file mode 100644 index 00000000000..873d85a7bca --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/ShowButtonDescriptionProvider.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.application.diagram.services.filter; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.diagrams.dto.HideDiagramElementInput; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.forms.ButtonStyle; +import org.eclipse.sirius.components.forms.WidgetIdProvider; +import org.eclipse.sirius.components.forms.description.ButtonDescription; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterActionContributionProvider; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterHelper; +import org.eclipse.sirius.web.domain.services.api.IMessageService; +import org.springframework.stereotype.Service; + +/** + * Provides the description of the show button for the diagram filter's split button. + * + * @author gdaniel + */ +@Service +public class ShowButtonDescriptionProvider implements IDiagramFilterActionContributionProvider { + + private final IObjectService objectService; + + private final IDiagramFilterHelper diagramFilterHelper; + + private final IMessageService messageService; + + public ShowButtonDescriptionProvider(IObjectService objectService, IDiagramFilterHelper diagramFilterHelper, IMessageService messageService) { + this.objectService = Objects.requireNonNull(objectService); + this.diagramFilterHelper = Objects.requireNonNull(diagramFilterHelper); + this.messageService = Objects.requireNonNull(messageService); + } + + @Override + public ButtonDescription getButtonDescription() { + return ButtonDescription.newButtonDescription("diagram-filter/split-button/show") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> "Show") + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .buttonLabelProvider(variableManager -> "Show") + .imageURLProvider(variableManager -> "/icons/full/obj16/ShowTool.svg") + .pushButtonHandler(variableManager -> { + var diagram = variableManager.get(DiagramFilterDescriptionProvider.DIAGRAM, Diagram.class).get(); + var nodeIds = this.diagramFilterHelper.getSelectedElementIds(variableManager); + var editingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).get(); + return this.diagramFilterHelper.sendDiagramEvent(variableManager, new HideDiagramElementInput(UUID.randomUUID(), editingContext.getId(), diagram.getId(), nodeIds, false)); + }) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> ButtonStyle.newButtonStyle() + .backgroundColor("#ffffff") + .foregroundColor("#261E58") + .build() + ) + .helpTextProvider(variableManager -> this.messageService.showSelectedElements()) + .build(); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/UnpinButtonDescription.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/UnpinButtonDescription.java new file mode 100644 index 00000000000..eef511b6f5b --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/UnpinButtonDescription.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.application.diagram.services.filter; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.diagrams.api.DiagramImageConstants; +import org.eclipse.sirius.components.collaborative.diagrams.dto.PinDiagramElementInput; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.forms.ButtonStyle; +import org.eclipse.sirius.components.forms.WidgetIdProvider; +import org.eclipse.sirius.components.forms.description.ButtonDescription; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterActionContributionProvider; +import org.eclipse.sirius.web.application.diagram.services.filter.api.IDiagramFilterHelper; +import org.eclipse.sirius.web.domain.services.api.IMessageService; +import org.springframework.stereotype.Service; + +/** + * Provides the description of the unpin button for the diagram filter's split button. + * + * @author gdaniel + */ +@Service +public class UnpinButtonDescription implements IDiagramFilterActionContributionProvider { + + private final IObjectService objectService; + + private final IDiagramFilterHelper diagramFilterHelper; + + private final IMessageService messageService; + + public UnpinButtonDescription(IObjectService objectService, IDiagramFilterHelper diagramFilterHelper, IMessageService messageService) { + this.objectService = Objects.requireNonNull(objectService); + this.diagramFilterHelper = Objects.requireNonNull(diagramFilterHelper); + this.messageService = Objects.requireNonNull(messageService); + } + + @Override + public ButtonDescription getButtonDescription() { + return ButtonDescription.newButtonDescription("diagram-filter/split-button/unpin") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> "Unpin") + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .buttonLabelProvider(variableManager -> "Unpin") + .imageURLProvider(variableManager -> DiagramImageConstants.UNPIN_SVG) + .pushButtonHandler(variableManager -> { + var diagram = variableManager.get(DiagramFilterDescriptionProvider.DIAGRAM, Diagram.class).get(); + var nodeIds = this.diagramFilterHelper.getSelectedElementIds(variableManager); + var editingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).get(); + return this.diagramFilterHelper.sendDiagramEvent(variableManager, new PinDiagramElementInput(UUID.randomUUID(), editingContext.getId(), diagram.getId(), nodeIds, false)); + }) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> ButtonStyle.newButtonStyle() + .backgroundColor("#ffffff") + .foregroundColor("#261E58") + .build() + ) + .helpTextProvider(variableManager -> this.messageService.unpinSelectedElements()) + .build(); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/api/DiagramFilterConfiguration.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/api/DiagramFilterConfiguration.java new file mode 100644 index 00000000000..af524d75503 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/api/DiagramFilterConfiguration.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.application.diagram.services.filter.api; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Objects; + +import org.eclipse.sirius.components.collaborative.api.IRepresentationConfiguration; + +/** + * The configuration used to create a diagram filter event processor. + * + * @author gdaniel + */ +public class DiagramFilterConfiguration implements IRepresentationConfiguration { + + private static final String DIAGRAM_FILTER_PREFIX = "diagramFilter://"; + + private final String formId; + + private final List objectIds; + + public DiagramFilterConfiguration(String id, List objectIds) { + this.objectIds = Objects.requireNonNull(objectIds); + var encodedIds = objectIds.stream().map(objectId -> URLEncoder.encode(objectId, StandardCharsets.UTF_8)).toList(); + this.formId = DIAGRAM_FILTER_PREFIX + "?id=" + id + "&objectIds=[" + String.join(",", encodedIds) + "]"; + } + + @Override + public String getId() { + return this.formId; + } + + public List getObjectIds() { + return this.objectIds; + } +} diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/api/IDiagramFilterActionContributionProvider.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/api/IDiagramFilterActionContributionProvider.java new file mode 100644 index 00000000000..378d9e7e0ed --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/api/IDiagramFilterActionContributionProvider.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.application.diagram.services.filter.api; + +import org.eclipse.sirius.components.forms.description.ButtonDescription; + +/** + * Provides the description of a button for the diagram filter's split button. + * + * @author gdaniel + */ +public interface IDiagramFilterActionContributionProvider { + + ButtonDescription getButtonDescription(); + +} diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/api/IDiagramFilterDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/api/IDiagramFilterDescriptionProvider.java new file mode 100644 index 00000000000..c6f73921ec0 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/api/IDiagramFilterDescriptionProvider.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.application.diagram.services.filter.api; + +import org.eclipse.sirius.components.forms.description.FormDescription; + +/** + * Interface used to contribute the form to display for the "Diagram Filter" view. + * + * @author gdaniel + */ +public interface IDiagramFilterDescriptionProvider { + + FormDescription getFormDescription(); + +} diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/api/IDiagramFilterHelper.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/api/IDiagramFilterHelper.java new file mode 100644 index 00000000000..82a5bc0bd48 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/diagram/services/filter/api/IDiagramFilterHelper.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.application.diagram.services.filter.api; + +import java.util.Set; + +import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramInput; +import org.eclipse.sirius.components.representations.IStatus; +import org.eclipse.sirius.components.representations.VariableManager; + +/** + * Provides utility methods for the "Diagram Filter" view. + * + * @author gdaniel + */ +public interface IDiagramFilterHelper { + + Set getSelectedElementIds(VariableManager variableManager); + + IStatus sendDiagramEvent(VariableManager variableManager, IDiagramInput diagramInput); +} diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/resources/schema/diagram-filter.graphqls b/packages/sirius-web/backend/sirius-web-application/src/main/resources/schema/diagram-filter.graphqls new file mode 100644 index 00000000000..730d5e666e0 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/resources/schema/diagram-filter.graphqls @@ -0,0 +1,3 @@ +extend type Subscription { + diagramFilterEvent(input : PropertiesEventInput!): PropertiesEventPayload! +} \ No newline at end of file diff --git a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/services/api/IMessageService.java b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/services/api/IMessageService.java index 8ac6cd19e0b..444e60d3188 100644 --- a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/services/api/IMessageService.java +++ b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/services/api/IMessageService.java @@ -18,9 +18,26 @@ * @author sbegaudeau */ public interface IMessageService { + + String revealSelectedFadedElements(); + + String collapseSelectedElements(); + + String expandSelectedElements(); + + String fadeSelectedElements(); + + String hideSelectedElements(); + String invalidName(); String notFound(); + String pinSelectedElements(); + + String showSelectedElements(); + String unexpectedError(); + + String unpinSelectedElements(); } diff --git a/packages/sirius-web/backend/sirius-web-infrastructure/src/main/java/org/eclipse/sirius/web/infrastructure/i18n/MessageService.java b/packages/sirius-web/backend/sirius-web-infrastructure/src/main/java/org/eclipse/sirius/web/infrastructure/i18n/MessageService.java index 46f9667ef19..6ad34eeb18b 100644 --- a/packages/sirius-web/backend/sirius-web-infrastructure/src/main/java/org/eclipse/sirius/web/infrastructure/i18n/MessageService.java +++ b/packages/sirius-web/backend/sirius-web-infrastructure/src/main/java/org/eclipse/sirius/web/infrastructure/i18n/MessageService.java @@ -33,6 +33,31 @@ public MessageService(@Qualifier("messageSourceAccessor") MessageSourceAccessor this.messageSourceAccessor = Objects.requireNonNull(messageSourceAccessor); } + @Override + public String revealSelectedFadedElements() { + return this.messageSourceAccessor.getMessage("REVEAL_SELECTED_FADED_ELEMENTS"); + } + + @Override + public String collapseSelectedElements() { + return this.messageSourceAccessor.getMessage("COLLAPSE_SELECTED_ELEMENTS"); + } + + @Override + public String expandSelectedElements() { + return this.messageSourceAccessor.getMessage("EXPAND_SELECTED_ELEMENTS"); + } + + @Override + public String fadeSelectedElements() { + return this.messageSourceAccessor.getMessage("FADE_SELECTED_ELEMENTS"); + } + + @Override + public String hideSelectedElements() { + return this.messageSourceAccessor.getMessage("HIDE_SELECTED_ELEMENTS"); + } + @Override public String invalidName() { return this.messageSourceAccessor.getMessage("INVALID_NAME"); @@ -43,8 +68,23 @@ public String notFound() { return this.messageSourceAccessor.getMessage("NOT_FOUND"); } + @Override + public String pinSelectedElements() { + return this.messageSourceAccessor.getMessage("PIN_SELECTED_ELEMENTS"); + } + + @Override + public String showSelectedElements() { + return this.messageSourceAccessor.getMessage("SHOW_SELECTED_ELEMENTS"); + } + @Override public String unexpectedError() { return this.messageSourceAccessor.getMessage("UNEXPECTED_ERROR"); } + + @Override + public String unpinSelectedElements() { + return this.messageSourceAccessor.getMessage("UNPIN_SELECTED_ELEMENTS"); + } } diff --git a/packages/sirius-web/backend/sirius-web-infrastructure/src/main/resources/i18n/messages.properties b/packages/sirius-web/backend/sirius-web-infrastructure/src/main/resources/i18n/messages.properties index e736658da23..3e52c8a1ba4 100644 --- a/packages/sirius-web/backend/sirius-web-infrastructure/src/main/resources/i18n/messages.properties +++ b/packages/sirius-web/backend/sirius-web-infrastructure/src/main/resources/i18n/messages.properties @@ -10,6 +10,14 @@ # Contributors: # Obeo - initial API and implementation ################################################################################################ +REVEAL_SELECTED_FADED_ELEMENTS=Reveal selected faded elements +COLLAPSE_SELECTED_ELEMENTS=Collapse selected elements +EXPAND_SELECTED_ELEMENTS=Expand selected elements +FADE_SELECTED_ELEMENTS=Fade selected elements +HIDE_SELECTED_ELEMENTS=Hide selected elements INVALID_NAME=Invalid name NOT_FOUND=Not found -UNEXPECTED_ERROR=An unexpected error has occurred, please contact the server administrator \ No newline at end of file +PIN_SELECTED_ELEMENTS=Pin selected elements +SHOW_SELECTED_ELEMENTS=Show selected elements +UNEXPECTED_ERROR=An unexpected error has occurred, please contact the server administrator +UNPIN_SELECTED_ELEMENTS=Unpin selected elements \ No newline at end of file diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/CollapseButtonDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/CollapseButtonDescriptionProvider.java new file mode 100644 index 00000000000..951b825ea8d --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/CollapseButtonDescriptionProvider.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.services.diagramfilter; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.diagrams.api.DiagramImageConstants; +import org.eclipse.sirius.components.collaborative.diagrams.dto.UpdateCollapsingStateInput; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.diagrams.CollapsingState; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.forms.ButtonStyle; +import org.eclipse.sirius.components.forms.WidgetIdProvider; +import org.eclipse.sirius.components.forms.description.ButtonDescription; +import org.eclipse.sirius.components.representations.Failure; +import org.eclipse.sirius.components.representations.Success; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterActionContributionProvider; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterHelper; +import org.eclipse.sirius.web.services.messages.IServicesMessageService; +import org.springframework.stereotype.Service; + +/** + * Provides the description of the collapse button for the diagram filter's split button. + * + * @author gdaniel + */ +@Service +public class CollapseButtonDescriptionProvider implements IDiagramFilterActionContributionProvider { + + private final IObjectService objectService; + + private final IDiagramFilterHelper diagramFilterHelper; + + private final IServicesMessageService messageService; + + public CollapseButtonDescriptionProvider(IObjectService objectService, IDiagramFilterHelper diagramFilterHelper, IServicesMessageService messageService) { + this.objectService = Objects.requireNonNull(objectService); + this.diagramFilterHelper = Objects.requireNonNull(diagramFilterHelper); + this.messageService = Objects.requireNonNull(messageService); + } + + @Override + public ButtonDescription getButtonDescription() { + return ButtonDescription.newButtonDescription("diagram-filter/split-button/collapse") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> "Collapse") + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .buttonLabelProvider(variableManager -> "Collapse") + .imageURLProvider(variableManager -> DiagramImageConstants.COLLAPSE_SVG) + .pushButtonHandler(variableManager -> { + var diagram = variableManager.get(DiagramFilterDescriptionProvider.DIAGRAM, Diagram.class).get(); + var nodeIds = this.diagramFilterHelper.getSelectedElementIds(variableManager); + var editingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).get(); + boolean hasFailure = nodeIds.stream() + .map(nodeId -> this.diagramFilterHelper.sendDiagramEvent(variableManager, new UpdateCollapsingStateInput(UUID.randomUUID(), editingContext.getId(), diagram.getId(), nodeId, CollapsingState.COLLAPSED))) + .anyMatch(Failure.class::isInstance); + if (hasFailure) { + return new Failure("An error occurred"); + } else { + return new Success(); + } + }) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> ButtonStyle.newButtonStyle() + .backgroundColor("#ffffff") + .foregroundColor("#261E58") + .build() + ) + .helpTextProvider(variableManager -> this.messageService.collapseSelectedElements()) + .build(); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/DiagramFilterDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/DiagramFilterDescriptionProvider.java new file mode 100644 index 00000000000..ef2f8bca489 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/DiagramFilterDescriptionProvider.java @@ -0,0 +1,340 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.services.diagramfilter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Stream; + +import org.eclipse.sirius.components.collaborative.api.IEditingContextEventProcessor; +import org.eclipse.sirius.components.collaborative.api.IEditingContextEventProcessorRegistry; +import org.eclipse.sirius.components.collaborative.api.IRepresentationEventProcessor; +import org.eclipse.sirius.components.collaborative.diagrams.api.DiagramImageConstants; +import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramEventProcessor; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.diagrams.CollapsingState; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.diagrams.Node; +import org.eclipse.sirius.components.diagrams.ViewModifier; +import org.eclipse.sirius.components.forms.WidgetIdProvider; +import org.eclipse.sirius.components.forms.description.AbstractControlDescription; +import org.eclipse.sirius.components.forms.description.CheckboxDescription; +import org.eclipse.sirius.components.forms.description.FormDescription; +import org.eclipse.sirius.components.forms.description.GroupDescription; +import org.eclipse.sirius.components.forms.description.LabelDescription; +import org.eclipse.sirius.components.forms.description.PageDescription; +import org.eclipse.sirius.components.forms.description.SplitButtonDescription; +import org.eclipse.sirius.components.forms.description.TreeDescription; +import org.eclipse.sirius.components.representations.GetOrCreateRandomIdProvider; +import org.eclipse.sirius.components.representations.Success; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterActionContributionProvider; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterDescriptionProvider; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterHelper; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +/** + * Provides a tree description listing diagram elements and allowing to perform actions on them. + * + * @author gdaniel + */ +@Service +public class DiagramFilterDescriptionProvider implements IDiagramFilterDescriptionProvider { + + public static final String FORM_DESCRIPTION_ID = "diagram_filter_form_description"; + + public static final String SELECTED_TREE_NODES = "selectedTreeNodes"; + + public static final String EDITING_CONTEXT_EVENT_PROCESSOR = "editingContextEventProcessor"; + + public static final String DIAGRAM_EVENT_PROCESSOR = "diagramEventProcessor"; + + public static final String DIAGRAM = "diagram"; + + private static final String GROUP_DESCRIPTION_ID = "defaultDiagramFilterGroup"; + + private static final String PAGE_DESCRIPTION_ID = "defaultDiagramFilterPage"; + + private static final String FORM_TITLE = "Diagram Filters"; + + private final IObjectService objectService; + + private final IEditingContextEventProcessorRegistry editingContextEventProcessorRegistry; + + private final List diagramFilterActionContributionProviders; + + private final IDiagramFilterHelper diagramFilterHelper; + + public DiagramFilterDescriptionProvider(IObjectService objectService, @Lazy IEditingContextEventProcessorRegistry editingContextEventProcessorRegistry, List diagramFilterActionContributionProviders, IDiagramFilterHelper diagramFilterHelper) { + this.objectService = Objects.requireNonNull(objectService); + this.editingContextEventProcessorRegistry = Objects.requireNonNull(editingContextEventProcessorRegistry); + this.diagramFilterActionContributionProviders = Objects.requireNonNull(diagramFilterActionContributionProviders); + this.diagramFilterHelper = Objects.requireNonNull(diagramFilterHelper); + } + + @Override + public FormDescription getFormDescription() { + List groupDescriptions = List.of(this.getGroupDescription()); + + Function targetObjectIdProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) + .map(this.objectService::getId) + .orElse(null); + + return FormDescription.newFormDescription(FORM_DESCRIPTION_ID) + .label(FORM_TITLE) + .idProvider(new GetOrCreateRandomIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> FORM_TITLE) + .targetObjectIdProvider(targetObjectIdProvider) + .canCreatePredicate(variableManager -> false) + .variableManagerInitializer(vm -> { + if (this.editingContextEventProcessorRegistry != null) { + String editingContextId = vm.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).map(IEditingContext::getId).orElse(""); + Optional optDiagram = vm.get(VariableManager.SELF, Object.class) + .filter(Diagram.class::isInstance) + .map(Diagram.class::cast); + String diagramId = optDiagram + .map(Diagram::getId).orElse(null); + + IEditingContextEventProcessor editingContextEventProcessor = this.editingContextEventProcessorRegistry.getEditingContextEventProcessors().stream() + .filter(processor -> processor.getEditingContextId().equals(editingContextId)) + .findFirst() + .orElse(null); + IRepresentationEventProcessor diagramEventProcessor = Optional.ofNullable(editingContextEventProcessor) + .flatMap(processor -> processor.getRepresentationEventProcessors().stream() + .filter(IDiagramEventProcessor.class::isInstance) + .map(IDiagramEventProcessor.class::cast) + .filter(p -> p.getRepresentation().getId().equals(diagramId)) + .findFirst() + ) + .orElse(null); + vm.put(SELECTED_TREE_NODES, this.getCheckMap(optDiagram.get())); + vm.put(EDITING_CONTEXT_EVENT_PROCESSOR, editingContextEventProcessor); + vm.put(DIAGRAM_EVENT_PROCESSOR, diagramEventProcessor); + vm.put(DIAGRAM, diagramEventProcessor.getRepresentation()); + } + return vm; + }) + .pageDescriptions(List.of(this.getPageDescription(groupDescriptions))) + .build(); + } + + private PageDescription getPageDescription(List groupDescriptions) { + Function idProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) + .map(this.objectService::getId) + .orElseGet(() -> UUID.randomUUID().toString()); + + Function labelProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) + .map(this.objectService::getLabel) + .orElse(""); + + Function> semanticElementsProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class).stream().toList(); + + return PageDescription.newPageDescription(PAGE_DESCRIPTION_ID) + .idProvider(idProvider) + .labelProvider(labelProvider) + .semanticElementsProvider(semanticElementsProvider) + .groupDescriptions(groupDescriptions) + .canCreatePredicate(variableManager -> true) + .build(); + } + + private GroupDescription getGroupDescription() { + List controlDescriptions = new ArrayList<>(); + controlDescriptions.add(this.createTreeLabelDescription()); + controlDescriptions.add(this.createTreeCheckboxDescription()); + controlDescriptions.add(this.createTreeDescription()); + controlDescriptions.add(this.createSplitButtonDescription()); + + return GroupDescription.newGroupDescription(GROUP_DESCRIPTION_ID) + .idProvider(variableManager -> FORM_TITLE) + .labelProvider(variableManager -> "Filter elements") + .semanticElementsProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).stream().toList()) + .controlDescriptions(controlDescriptions) + .build(); + } + + private LabelDescription createTreeLabelDescription() { + return LabelDescription.newLabelDescription("diagram-filter/tree-label") + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .idProvider(new WidgetIdProvider()) + .labelProvider(variableManager -> "") + .valueProvider(variableManager -> "Elements on diagram") + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> null) + .build(); + } + + private CheckboxDescription createTreeCheckboxDescription() { + return CheckboxDescription.newCheckboxDescription("diagram-filter/tree-checkbox") + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .idProvider(new WidgetIdProvider()) + .labelProvider(variableManager -> { + int selectedElementCount = this.diagramFilterHelper.getSelectedElementIds(variableManager).size(); + String element = "element"; + if (selectedElementCount > 1) { + element += "s"; + } + return selectedElementCount + " " + element + " selected"; + }) + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .valueProvider(variableManager -> this.diagramFilterHelper.getSelectedElementIds(variableManager).size() > 0) + .newValueHandler((variableManager, newValue) -> { + Map checkMap = variableManager.get(SELECTED_TREE_NODES, Map.class).get(); + checkMap.entrySet().stream() + .forEach(entry -> entry.setValue(newValue)); + return new Success(); + }) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> null) + .build(); + } + + private TreeDescription createTreeDescription() { + return TreeDescription.newTreeDescription("diagram-filter/tree") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .labelProvider(variableManager -> "") + .nodeIdProvider(this::getNodeId) + .nodeLabelProvider(this::getNodeLabel) + .nodeIconURLProvider(vm -> List.of()) + .nodeEndIconsURLProvider(this::computeNodeEndIcons) + .nodeKindProvider(vm -> "") + .nodeSelectableProvider(vm -> true) + .childrenProvider(this::getNodeChildren) + .expandedNodeIdsProvider(vm -> List.of()) + .isCheckableProvider(variableManager -> true) + .checkedValueProvider(variableManager -> this.diagramFilterHelper.getSelectedElementIds(variableManager).contains(this.getNodeId(variableManager))) + .newCheckedValueHandler((variableManager, newValue) -> { + Map selectedTreeNodes = variableManager.get(SELECTED_TREE_NODES, Map.class).get(); + String selfId = this.getNodeId(variableManager); + selectedTreeNodes.put(selfId, newValue); + return new Success(); + }) + .build(); + } + + private SplitButtonDescription createSplitButtonDescription() { + return SplitButtonDescription.newSplitButtonDescription("diagram-filter/split-button") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> { + int selectedElementCount = this.diagramFilterHelper.getSelectedElementIds(variableManager).size(); + String element = "element"; + if (selectedElementCount > 1) { + element += "s"; + } + + return "Apply to " + selectedElementCount + " selected " + element + ": "; + }) + .diagnosticsProvider(variableManager -> List.of()) + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .actions(this.diagramFilterActionContributionProviders.stream() + .map(IDiagramFilterActionContributionProvider::getButtonDescription) + .toList() + ) + .build(); + + } + + private String getNodeId(VariableManager vm) { + var self = vm.get(VariableManager.SELF, Object.class).orElse(null); + if (self instanceof Node node) { + return node.getId(); + } else { + return ""; + } + } + + private String getNodeLabel(VariableManager vm) { + var self = vm.get(VariableManager.SELF, Object.class).orElse(null); + String result = ""; + if (self instanceof Node node) { + if (node.getInsideLabel() != null) { + result = node.getInsideLabel().getText(); + } else if (!node.getOutsideLabels().isEmpty()) { + result = node.getOutsideLabels().get(0).text(); + } else if (node.getTargetObjectLabel() != null) { + result = node.getTargetObjectLabel(); + } + } + return result; + } + + private List getNodeChildren(VariableManager vm) { + var self = vm.get(VariableManager.SELF, Object.class).orElse(null); + final List result; + if (self instanceof Diagram diagram) { + result = diagram.getNodes(); + } else if (self instanceof Node node) { + result = node.getChildNodes(); + } else { + result = List.of(); + } + return result; + } + + private List> computeNodeEndIcons(VariableManager vm) { + var node = vm.get(VariableManager.SELF, Object.class) + .filter(Node.class::isInstance) + .map(Node.class::cast) + .orElse(null); + List> result = new ArrayList<>(); + if (node.getModifiers().contains(ViewModifier.Hidden)) { + result.add(List.of("/icons/full/obj16/HideTool.svg")); + } + if (node.getModifiers().contains(ViewModifier.Faded)) { + result.add(List.of("/icons/full/obj16/FadeTool.svg")); + } + if (node.getCollapsingState().equals(CollapsingState.COLLAPSED)) { + result.add(List.of(DiagramImageConstants.COLLAPSE_SVG)); + } + if (node.isPinned()) { + result.add(List.of(DiagramImageConstants.PIN_SVG)); + } + return result; + } + + private Map getCheckMap(Diagram diagram) { + Map checkMap = new HashMap<>(); + for (Node node : diagram.getNodes()) { + this.fillCheckMap(node, checkMap); + } + return checkMap; + } + + private void fillCheckMap(Node node, Map checkMap) { + checkMap.put(node.getId(), false); + Stream.concat(node.getChildNodes().stream(), node.getBorderNodes().stream()) + .forEach(n -> this.fillCheckMap(n, checkMap)); + } +} \ No newline at end of file diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/DiagramFilterEventProcessorFactory.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/DiagramFilterEventProcessorFactory.java new file mode 100644 index 00000000000..aa2dbd039d4 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/DiagramFilterEventProcessorFactory.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.services.diagramfilter; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import org.eclipse.sirius.components.collaborative.api.IRepresentationConfiguration; +import org.eclipse.sirius.components.collaborative.api.IRepresentationEventProcessor; +import org.eclipse.sirius.components.collaborative.api.IRepresentationEventProcessorFactory; +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.forms.FormEventProcessor; +import org.eclipse.sirius.components.collaborative.forms.api.FormCreationParameters; +import org.eclipse.sirius.components.collaborative.forms.api.IFormEventHandler; +import org.eclipse.sirius.components.collaborative.forms.api.IFormPostProcessor; +import org.eclipse.sirius.components.collaborative.forms.configuration.FormEventProcessorConfiguration; +import org.eclipse.sirius.components.collaborative.forms.configuration.FormEventProcessorFactoryConfiguration; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.forms.description.FormDescription; +import org.eclipse.sirius.components.forms.renderer.IWidgetDescriptor; +import org.eclipse.sirius.web.services.diagramfilter.api.DiagramFilterConfiguration; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterDescriptionProvider; +import org.springframework.stereotype.Service; + +/** + * Used to create the diagram filter event processors. + * + * @author gdaniel + */ +@Service +public class DiagramFilterEventProcessorFactory implements IRepresentationEventProcessorFactory { + + private final IDiagramFilterDescriptionProvider diagramFilterDescriptionProvider; + + private final IObjectService objectService; + + private final IRepresentationSearchService representationSearchService; + + private final List widgetDescriptors; + + private final List formEventHandlers; + + private final ISubscriptionManagerFactory subscriptionManagerFactory; + + private final IRepresentationRefreshPolicyRegistry representationRefreshPolicyRegistry; + + private final IFormPostProcessor formPostProcessor; + + public DiagramFilterEventProcessorFactory(RepresentationEventProcessorFactoryConfiguration configuration, IRepresentationSearchService representationSearchService, IDiagramFilterDescriptionProvider diagramFilterDescriptionProvider, + List widgetDescriptors, FormEventProcessorFactoryConfiguration formConfiguration) { + this.diagramFilterDescriptionProvider = Objects.requireNonNull(diagramFilterDescriptionProvider); + this.objectService = Objects.requireNonNull(formConfiguration.getObjectService()); + this.representationSearchService = Objects.requireNonNull(representationSearchService); + this.widgetDescriptors = Objects.requireNonNull(widgetDescriptors); + this.formEventHandlers = Objects.requireNonNull(formConfiguration.getFormEventHandlers()); + this.subscriptionManagerFactory = Objects.requireNonNull(configuration.getSubscriptionManagerFactory()); + this.representationRefreshPolicyRegistry = Objects.requireNonNull(configuration.getRepresentationRefreshPolicyRegistry()); + this.formPostProcessor = Objects.requireNonNull(formConfiguration.getFormPostProcessor()); + } + + @Override + public boolean canHandle(IRepresentationConfiguration configuration) { + return configuration instanceof DiagramFilterConfiguration; + } + + @Override + public Optional createRepresentationEventProcessor(IRepresentationConfiguration configuration, + IEditingContext editingContext) { + if (configuration instanceof DiagramFilterConfiguration diagramFilterConfiguration && this.diagramFilterDescriptionProvider != null) { + var objects = diagramFilterConfiguration.getObjectIds().stream() + .map(objectId -> this.objectService.getObject(editingContext, objectId)) + .flatMap(Optional::stream) + .toList(); + if (!objects.isEmpty()) { + FormDescription formDescription = this.diagramFilterDescriptionProvider.getFormDescription(); + FormCreationParameters formCreationParameters = FormCreationParameters.newFormCreationParameters(diagramFilterConfiguration.getId()) + .editingContext(editingContext) + .formDescription(formDescription) + .object(objects.get(0)) + .selection(objects) + .build(); + + var formEventProcessorConfiguration = new FormEventProcessorConfiguration(editingContext, this.objectService, formCreationParameters, this.widgetDescriptors, this.formEventHandlers); + IRepresentationEventProcessor formEventProcessor = new FormEventProcessor(formEventProcessorConfiguration, this.subscriptionManagerFactory.create(), this.representationSearchService, this.representationRefreshPolicyRegistry, this.formPostProcessor); + + return Optional.of(formEventProcessor); + } + } + return Optional.empty(); + } +} diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/DiagramFilterHelper.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/DiagramFilterHelper.java new file mode 100644 index 00000000000..c98475d3aea --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/DiagramFilterHelper.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.services.diagramfilter; + +import java.text.MessageFormat; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.sirius.components.collaborative.api.ChangeDescription; +import org.eclipse.sirius.components.collaborative.api.IEditingContextEventProcessor; +import org.eclipse.sirius.components.collaborative.api.IRepresentationEventProcessor; +import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramInput; +import org.eclipse.sirius.components.core.api.ErrorPayload; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.core.api.SuccessPayload; +import org.eclipse.sirius.components.representations.Failure; +import org.eclipse.sirius.components.representations.IStatus; +import org.eclipse.sirius.components.representations.Success; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import reactor.core.publisher.Sinks; +import reactor.core.publisher.Sinks.EmitResult; +import reactor.core.publisher.Sinks.Many; +import reactor.core.publisher.Sinks.One; + +/** + * Utility methods for the "Diagram Filter" view. + * + * @author gdaniel + */ +@Service +public class DiagramFilterHelper implements IDiagramFilterHelper { + + private final Logger logger = LoggerFactory.getLogger(DiagramFilterHelper.class); + + @Override + public Set getSelectedElementIds(VariableManager variableManager) { + return variableManager.get(DiagramFilterDescriptionProvider.SELECTED_TREE_NODES, Map.class) + .map(checkMap -> { + Stream entryStream = checkMap.entrySet().stream(); + return entryStream.filter(Map.Entry.class::isInstance) + .map(Map.Entry.class::cast) + .filter(entry -> entry.getValue().equals(Boolean.TRUE)) + .map(Map.Entry::getKey) + .map(Object::toString) + .collect(Collectors.toSet()); + }) + .orElse(Set.of()); + } + + @Override + public IStatus sendDiagramEvent(VariableManager variableManager, IDiagramInput diagramInput) { + var optionalRepresentationEventProcessor = variableManager.get(DiagramFilterDescriptionProvider.DIAGRAM_EVENT_PROCESSOR, IRepresentationEventProcessor.class); + var optionalEditingContextEventProcessor = variableManager.get(DiagramFilterDescriptionProvider.EDITING_CONTEXT_EVENT_PROCESSOR, IEditingContextEventProcessor.class); + + if (optionalRepresentationEventProcessor.isPresent() && optionalEditingContextEventProcessor.isPresent()) { + var editingContextEventProcessor = optionalEditingContextEventProcessor.get(); + var representationEventProcessor = optionalRepresentationEventProcessor.get(); + + One payloadSink = Sinks.one(); + Many changeDescriptions = Sinks.many().unicast().onBackpressureBuffer(); + Consumer errorConsumer = throwable -> this.logger.warn(throwable.getMessage(), throwable); + changeDescriptions.asFlux().subscribe(changeDescription -> editingContextEventProcessor.getRepresentationEventProcessors() + .forEach(eventProcessor -> eventProcessor.refresh(changeDescription)), errorConsumer); + + representationEventProcessor.handle(payloadSink, changeDescriptions, diagramInput); + IPayload handlerResult = payloadSink.asMono().block(); + + IStatus result = null; + + EmitResult changeDescriptionEmitResult = changeDescriptions.tryEmitComplete(); + if (changeDescriptionEmitResult.isFailure()) { + String errorMessage = MessageFormat.format("An error has occurred while marking the publisher as complete: {0}", changeDescriptionEmitResult); + this.logger.warn(errorMessage); + result = new Failure(errorMessage); + } else { + if (handlerResult instanceof SuccessPayload) { + result = new Success(); + } else if (handlerResult instanceof ErrorPayload errorPayload) { + result = new Failure(errorPayload.message()); + } else { + result = new Failure("Unknown error"); + } + } + return result; + } + + String errorMessage = "Cannot find the diagramEventProcessor or the editingContextEventProcessor"; + this.logger.warn(errorMessage); + return new Failure(errorMessage); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/DiagramFilterRefreshPolicyProvider.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/DiagramFilterRefreshPolicyProvider.java new file mode 100644 index 00000000000..f88d10a3c8b --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/DiagramFilterRefreshPolicyProvider.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.services.diagramfilter; + +import org.eclipse.sirius.components.collaborative.api.ChangeKind; +import org.eclipse.sirius.components.collaborative.api.IRepresentationRefreshPolicy; +import org.eclipse.sirius.components.collaborative.api.IRepresentationRefreshPolicyProvider; +import org.eclipse.sirius.components.collaborative.diagrams.DiagramChangeKind; +import org.eclipse.sirius.components.representations.IRepresentationDescription; +import org.springframework.stereotype.Service; + +/** + * The representation refresh policy provider for the "Diagram Filter" form representation. + * + * @author gdaniel + */ +@Service +public class DiagramFilterRefreshPolicyProvider implements IRepresentationRefreshPolicyProvider { + + + @Override + public boolean canHandle(IRepresentationDescription representationDescription) { + return DiagramFilterDescriptionProvider.FORM_DESCRIPTION_ID.equals(representationDescription.getId()); + } + + @Override + public IRepresentationRefreshPolicy getRepresentationRefreshPolicy(IRepresentationDescription representationDescription) { + return changeDescription -> { + boolean shouldRefresh = false; + shouldRefresh = shouldRefresh || ChangeKind.SEMANTIC_CHANGE.equals(changeDescription.getKind()); + shouldRefresh = shouldRefresh || DiagramChangeKind.DIAGRAM_LAYOUT_CHANGE.equals(changeDescription.getKind()); + shouldRefresh = shouldRefresh || DiagramChangeKind.DIAGRAM_ELEMENT_VISIBILITY_CHANGE.equals(changeDescription.getKind()); + shouldRefresh = shouldRefresh || DiagramChangeKind.DIAGRAM_ELEMENT_COLLAPSING_STATE_CHANGE.equals(changeDescription.getKind()); + return shouldRefresh; + }; + } + +} diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/ExpandButtonDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/ExpandButtonDescriptionProvider.java new file mode 100644 index 00000000000..d84713a7cb2 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/ExpandButtonDescriptionProvider.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.services.diagramfilter; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.diagrams.api.DiagramImageConstants; +import org.eclipse.sirius.components.collaborative.diagrams.dto.UpdateCollapsingStateInput; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.diagrams.CollapsingState; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.forms.ButtonStyle; +import org.eclipse.sirius.components.forms.WidgetIdProvider; +import org.eclipse.sirius.components.forms.description.ButtonDescription; +import org.eclipse.sirius.components.representations.Failure; +import org.eclipse.sirius.components.representations.Success; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterActionContributionProvider; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterHelper; +import org.eclipse.sirius.web.services.messages.IServicesMessageService; +import org.springframework.stereotype.Service; + +/** + * Provides the description of the expand button for the diagram filter's split button. + * + * @author gdaniel + */ +@Service +public class ExpandButtonDescriptionProvider implements IDiagramFilterActionContributionProvider { + + private final IObjectService objectService; + + private final IDiagramFilterHelper diagramFilterHelper; + + private final IServicesMessageService messageService; + + public ExpandButtonDescriptionProvider(IObjectService objectService, IDiagramFilterHelper diagramFilterHelper, IServicesMessageService messageService) { + this.objectService = Objects.requireNonNull(objectService); + this.diagramFilterHelper = Objects.requireNonNull(diagramFilterHelper); + this.messageService = Objects.requireNonNull(messageService); + } + + @Override + public ButtonDescription getButtonDescription() { + return ButtonDescription.newButtonDescription("diagram-filter/split-button/expand") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> "Expand") + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .buttonLabelProvider(variableManager -> "Expand") + .imageURLProvider(variableManager -> DiagramImageConstants.EXPAND_SVG) + .pushButtonHandler(variableManager -> { + var diagram = variableManager.get(DiagramFilterDescriptionProvider.DIAGRAM, Diagram.class).get(); + var nodeIds = this.diagramFilterHelper.getSelectedElementIds(variableManager); + var editingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).get(); + boolean hasFailure = nodeIds.stream() + .map(nodeId -> this.diagramFilterHelper.sendDiagramEvent(variableManager, new UpdateCollapsingStateInput(UUID.randomUUID(), editingContext.getId(), diagram.getId(), nodeId, CollapsingState.EXPANDED))) + .anyMatch(Failure.class::isInstance); + if (hasFailure) { + return new Failure("An error occurred"); + } else { + return new Success(); + } + }) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> ButtonStyle.newButtonStyle() + .backgroundColor("#ffffff") + .foregroundColor("#261E58") + .build() + ) + .helpTextProvider(variableManager -> this.messageService.expandSelectedElements()) + .build(); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/FadeButtonDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/FadeButtonDescriptionProvider.java new file mode 100644 index 00000000000..58feac98e70 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/FadeButtonDescriptionProvider.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.services.diagramfilter; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.diagrams.api.DiagramImageConstants; +import org.eclipse.sirius.components.collaborative.diagrams.dto.FadeDiagramElementInput; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.forms.ButtonStyle; +import org.eclipse.sirius.components.forms.WidgetIdProvider; +import org.eclipse.sirius.components.forms.description.ButtonDescription; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterActionContributionProvider; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterHelper; +import org.eclipse.sirius.web.services.messages.IServicesMessageService; +import org.springframework.stereotype.Service; + +/** + * Provides the description of the fade button for the diagram filter's split button. + * + * @author gdaniel + */ +@Service +public class FadeButtonDescriptionProvider implements IDiagramFilterActionContributionProvider { + + private final IObjectService objectService; + + private final IDiagramFilterHelper diagramFilterHelper; + + private final IServicesMessageService messageService; + + public FadeButtonDescriptionProvider(IObjectService objectService, IDiagramFilterHelper diagramFilterHelper, IServicesMessageService messageService) { + this.objectService = Objects.requireNonNull(objectService); + this.diagramFilterHelper = Objects.requireNonNull(diagramFilterHelper); + this.messageService = Objects.requireNonNull(messageService); + } + + @Override + public ButtonDescription getButtonDescription() { + return ButtonDescription.newButtonDescription("diagram-filter/split-button/fade") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> "Fade") + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .buttonLabelProvider(variableManager -> "Fade") + .imageURLProvider(variableManager -> DiagramImageConstants.FADE_SVG) + .pushButtonHandler(variableManager -> { + var diagram = variableManager.get(DiagramFilterDescriptionProvider.DIAGRAM, Diagram.class).get(); + var nodeIds = this.diagramFilterHelper.getSelectedElementIds(variableManager); + var editingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).get(); + return this.diagramFilterHelper.sendDiagramEvent(variableManager, new FadeDiagramElementInput(UUID.randomUUID(), editingContext.getId(), diagram.getId(), nodeIds, true)); + }) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> ButtonStyle.newButtonStyle() + .backgroundColor("#ffffff") + .foregroundColor("#261E58") + .build() + ) + .helpTextProvider(variableManager -> this.messageService.fadeSelectedElements()) + .build(); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/HideButtonDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/HideButtonDescriptionProvider.java new file mode 100644 index 00000000000..a53ce4dadf2 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/HideButtonDescriptionProvider.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.services.diagramfilter; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.diagrams.dto.HideDiagramElementInput; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.forms.ButtonStyle; +import org.eclipse.sirius.components.forms.WidgetIdProvider; +import org.eclipse.sirius.components.forms.description.ButtonDescription; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterActionContributionProvider; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterHelper; +import org.eclipse.sirius.web.services.messages.IServicesMessageService; +import org.springframework.stereotype.Service; + +/** + * Provides the description of the hide button for the diagram filter's split button. + * + * @author gdaniel + */ +@Service +public class HideButtonDescriptionProvider implements IDiagramFilterActionContributionProvider { + + private final IObjectService objectService; + + private final IDiagramFilterHelper diagramFilterHelper; + + private final IServicesMessageService messageService; + + public HideButtonDescriptionProvider(IObjectService objectService, IDiagramFilterHelper diagramFilterHelper, IServicesMessageService messageService) { + this.objectService = Objects.requireNonNull(objectService); + this.diagramFilterHelper = Objects.requireNonNull(diagramFilterHelper); + this.messageService = Objects.requireNonNull(messageService); + } + + @Override + public ButtonDescription getButtonDescription() { + return ButtonDescription.newButtonDescription("diagram-filter/split-button/hide") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> "Hide") + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .buttonLabelProvider(variableManager -> "Hide") + .imageURLProvider(variableManager -> "/icons/full/obj16/HideTool.svg") + .pushButtonHandler(variableManager -> { + var diagram = variableManager.get(DiagramFilterDescriptionProvider.DIAGRAM, Diagram.class).get(); + var nodeIds = this.diagramFilterHelper.getSelectedElementIds(variableManager); + var editingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).get(); + return this.diagramFilterHelper.sendDiagramEvent(variableManager, new HideDiagramElementInput(UUID.randomUUID(), editingContext.getId(), diagram.getId(), nodeIds, true)); + }) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> ButtonStyle.newButtonStyle() + .backgroundColor("#ffffff") + .foregroundColor("#261E58") + .build() + ) + .helpTextProvider(variableManager -> this.messageService.hideSelectedElements()) + .build(); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/PinButtonDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/PinButtonDescriptionProvider.java new file mode 100644 index 00000000000..3c6cd41dfde --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/PinButtonDescriptionProvider.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.services.diagramfilter; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.diagrams.api.DiagramImageConstants; +import org.eclipse.sirius.components.collaborative.diagrams.dto.PinDiagramElementInput; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.forms.ButtonStyle; +import org.eclipse.sirius.components.forms.WidgetIdProvider; +import org.eclipse.sirius.components.forms.description.ButtonDescription; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterActionContributionProvider; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterHelper; +import org.eclipse.sirius.web.services.messages.IServicesMessageService; +import org.springframework.stereotype.Service; + +/** + * Provides the description of the pin button for the diagram filter's split button. + * + * @author gdaniel + */ +@Service +public class PinButtonDescriptionProvider implements IDiagramFilterActionContributionProvider { + + private final IObjectService objectService; + + private final IDiagramFilterHelper diagramFilterHelper; + + private final IServicesMessageService messageService; + + public PinButtonDescriptionProvider(IObjectService objectService, IDiagramFilterHelper diagramFilterHelper, IServicesMessageService messageService) { + this.objectService = Objects.requireNonNull(objectService); + this.diagramFilterHelper = Objects.requireNonNull(diagramFilterHelper); + this.messageService = Objects.requireNonNull(messageService); + + } + + @Override + public ButtonDescription getButtonDescription() { + return ButtonDescription.newButtonDescription("diagram-filter/split-button/pin") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> "Pin") + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .buttonLabelProvider(variableManager -> "Pin") + .imageURLProvider(variableManager -> DiagramImageConstants.PIN_SVG) + .pushButtonHandler(variableManager -> { + var diagram = variableManager.get(DiagramFilterDescriptionProvider.DIAGRAM, Diagram.class).get(); + var nodeIds = this.diagramFilterHelper.getSelectedElementIds(variableManager); + var editingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).get(); + return this.diagramFilterHelper.sendDiagramEvent(variableManager, new PinDiagramElementInput(UUID.randomUUID(), editingContext.getId(), diagram.getId(), nodeIds, true)); + }) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> ButtonStyle.newButtonStyle() + .backgroundColor("#ffffff") + .foregroundColor("#261E58") + .build() + ) + .helpTextProvider(variableManager -> this.messageService.pinSelectedElements()) + .build(); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/RevealFadedElementsButtonDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/RevealFadedElementsButtonDescriptionProvider.java new file mode 100644 index 00000000000..eedd1459d40 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/RevealFadedElementsButtonDescriptionProvider.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.services.diagramfilter; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.diagrams.api.DiagramImageConstants; +import org.eclipse.sirius.components.collaborative.diagrams.dto.FadeDiagramElementInput; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.forms.ButtonStyle; +import org.eclipse.sirius.components.forms.WidgetIdProvider; +import org.eclipse.sirius.components.forms.description.ButtonDescription; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterActionContributionProvider; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterHelper; +import org.eclipse.sirius.web.services.messages.IServicesMessageService; +import org.springframework.stereotype.Service; + +/** + * Provides the description of the reveal faded elements button for the diagram filter's split button. + * + * @author gdaniel + */ +@Service +public class RevealFadedElementsButtonDescriptionProvider implements IDiagramFilterActionContributionProvider { + + private final IObjectService objectService; + + private final IDiagramFilterHelper diagramFilterHelper; + + private final IServicesMessageService messageService; + + public RevealFadedElementsButtonDescriptionProvider(IObjectService objectService, IDiagramFilterHelper diagramFilterHelper, IServicesMessageService messageService) { + this.objectService = Objects.requireNonNull(objectService); + this.diagramFilterHelper = Objects.requireNonNull(diagramFilterHelper); + this.messageService = Objects.requireNonNull(messageService); + } + + @Override + public ButtonDescription getButtonDescription() { + return ButtonDescription.newButtonDescription("diagram-filter/split-button/reveal-faded-elements") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> "Reveal faded elements") + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .buttonLabelProvider(variableManager -> "Reveal faded elements") + .imageURLProvider(variableManager -> DiagramImageConstants.REVEAL_FADED_ELEMENTS_SVG) + .pushButtonHandler(variableManager -> { + var diagram = variableManager.get(DiagramFilterDescriptionProvider.DIAGRAM, Diagram.class).get(); + var nodeIds = this.diagramFilterHelper.getSelectedElementIds(variableManager); + var editingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).get(); + return this.diagramFilterHelper.sendDiagramEvent(variableManager, new FadeDiagramElementInput(UUID.randomUUID(), editingContext.getId(), diagram.getId(), nodeIds, false)); + }) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> ButtonStyle.newButtonStyle() + .backgroundColor("#ffffff") + .foregroundColor("#261E58") + .build() + ) + .helpTextProvider(variableManager -> this.messageService.revealSelectedFadedElements()) + .build(); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/ShowButtonDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/ShowButtonDescriptionProvider.java new file mode 100644 index 00000000000..62be8387309 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/ShowButtonDescriptionProvider.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.services.diagramfilter; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.diagrams.dto.HideDiagramElementInput; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.forms.ButtonStyle; +import org.eclipse.sirius.components.forms.WidgetIdProvider; +import org.eclipse.sirius.components.forms.description.ButtonDescription; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterActionContributionProvider; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterHelper; +import org.eclipse.sirius.web.services.messages.IServicesMessageService; +import org.springframework.stereotype.Service; + +/** + * Provides the description of the show button for the diagram filter's split button. + * + * @author gdaniel + */ +@Service +public class ShowButtonDescriptionProvider implements IDiagramFilterActionContributionProvider { + + private final IObjectService objectService; + + private final IDiagramFilterHelper diagramFilterHelper; + + private final IServicesMessageService messageService; + + public ShowButtonDescriptionProvider(IObjectService objectService, IDiagramFilterHelper diagramFilterHelper, IServicesMessageService messageService) { + this.objectService = Objects.requireNonNull(objectService); + this.diagramFilterHelper = Objects.requireNonNull(diagramFilterHelper); + this.messageService = Objects.requireNonNull(messageService); + } + + @Override + public ButtonDescription getButtonDescription() { + return ButtonDescription.newButtonDescription("diagram-filter/split-button/show") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> "Show") + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .buttonLabelProvider(variableManager -> "Show") + .imageURLProvider(variableManager -> "/icons/full/obj16/ShowTool.svg") + .pushButtonHandler(variableManager -> { + var diagram = variableManager.get(DiagramFilterDescriptionProvider.DIAGRAM, Diagram.class).get(); + var nodeIds = this.diagramFilterHelper.getSelectedElementIds(variableManager); + var editingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).get(); + return this.diagramFilterHelper.sendDiagramEvent(variableManager, new HideDiagramElementInput(UUID.randomUUID(), editingContext.getId(), diagram.getId(), nodeIds, false)); + }) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> ButtonStyle.newButtonStyle() + .backgroundColor("#ffffff") + .foregroundColor("#261E58") + .build() + ) + .helpTextProvider(variableManager -> this.messageService.showSelectedElements()) + .build(); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/SubscriptionDiagramFilterEventDataFetcher.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/SubscriptionDiagramFilterEventDataFetcher.java new file mode 100644 index 00000000000..4f183a36df9 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/SubscriptionDiagramFilterEventDataFetcher.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.services.diagramfilter; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import org.eclipse.sirius.components.annotations.spring.graphql.SubscriptionDataFetcher; +import org.eclipse.sirius.components.collaborative.forms.dto.PropertiesEventInput; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; +import org.eclipse.sirius.components.graphql.api.IEventProcessorSubscriptionProvider; +import org.eclipse.sirius.components.graphql.api.IExceptionWrapper; +import org.eclipse.sirius.components.graphql.api.LocalContextConstants; +import org.eclipse.sirius.web.services.diagramfilter.api.DiagramFilterConfiguration; +import org.reactivestreams.Publisher; + +import graphql.execution.DataFetcherResult; +import graphql.schema.DataFetchingEnvironment; + +/** + * The data fetcher used to send the refreshed diagram filters to a subscription. + * + * @author gdaniel + */ +@SubscriptionDataFetcher(type = "Subscription", field = "diagramFilterEvent") +public class SubscriptionDiagramFilterEventDataFetcher implements IDataFetcherWithFieldCoordinates>> { + + private static final String INPUT_ARGUMENT = "input"; + + private final ObjectMapper objectMapper; + + private final IExceptionWrapper exceptionWrapper; + + private final IEventProcessorSubscriptionProvider eventProcessorSubscriptionProvider; + + public SubscriptionDiagramFilterEventDataFetcher(ObjectMapper objectMapper, IExceptionWrapper exceptionWrapper, IEventProcessorSubscriptionProvider eventProcessorSubscriptionProvider) { + this.objectMapper = Objects.requireNonNull(objectMapper); + this.exceptionWrapper = Objects.requireNonNull(exceptionWrapper); + this.eventProcessorSubscriptionProvider = Objects.requireNonNull(eventProcessorSubscriptionProvider); + } + + @Override + public Publisher> get(DataFetchingEnvironment environment) throws Exception { + Object argument = environment.getArgument(INPUT_ARGUMENT); + var input = this.objectMapper.convertValue(argument, PropertiesEventInput.class); + var diagramFilterConfiguration = new DiagramFilterConfiguration(input.id().toString(), input.objectIds()); + + Map localContext = new HashMap<>(); + localContext.put(LocalContextConstants.EDITING_CONTEXT_ID, input.editingContextId()); + localContext.put(LocalContextConstants.REPRESENTATION_ID, diagramFilterConfiguration.getId()); + + return this.exceptionWrapper.wrapFlux(() -> this.eventProcessorSubscriptionProvider.getSubscription(input.editingContextId(), diagramFilterConfiguration, input), input) + .map(payload -> DataFetcherResult.newResult() + .data(payload) + .localContext(localContext) + .build()); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/UnpinButtonDescription.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/UnpinButtonDescription.java new file mode 100644 index 00000000000..4375f5f8b8f --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/UnpinButtonDescription.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.services.diagramfilter; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.diagrams.api.DiagramImageConstants; +import org.eclipse.sirius.components.collaborative.diagrams.dto.PinDiagramElementInput; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.forms.ButtonStyle; +import org.eclipse.sirius.components.forms.WidgetIdProvider; +import org.eclipse.sirius.components.forms.description.ButtonDescription; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterActionContributionProvider; +import org.eclipse.sirius.web.services.diagramfilter.api.IDiagramFilterHelper; +import org.eclipse.sirius.web.services.messages.IServicesMessageService; +import org.springframework.stereotype.Service; + +/** + * Provides the description of the unpin button for the diagram filter's split button. + * + * @author gdaniel + */ +@Service +public class UnpinButtonDescription implements IDiagramFilterActionContributionProvider { + + private final IObjectService objectService; + + private final IDiagramFilterHelper diagramFilterHelper; + + private final IServicesMessageService messageService; + + public UnpinButtonDescription(IObjectService objectService, IDiagramFilterHelper diagramFilterHelper, IServicesMessageService messageService) { + this.objectService = Objects.requireNonNull(objectService); + this.diagramFilterHelper = Objects.requireNonNull(diagramFilterHelper); + this.messageService = Objects.requireNonNull(messageService); + } + + @Override + public ButtonDescription getButtonDescription() { + return ButtonDescription.newButtonDescription("diagram-filter/split-button/unpin") + .idProvider(new WidgetIdProvider()) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .labelProvider(variableManager -> "Unpin") + .iconURLProvider(variableManager -> List.of()) + .isReadOnlyProvider(variableManager -> false) + .buttonLabelProvider(variableManager -> "Unpin") + .imageURLProvider(variableManager -> DiagramImageConstants.UNPIN_SVG) + .pushButtonHandler(variableManager -> { + var diagram = variableManager.get(DiagramFilterDescriptionProvider.DIAGRAM, Diagram.class).get(); + var nodeIds = this.diagramFilterHelper.getSelectedElementIds(variableManager); + var editingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).get(); + return this.diagramFilterHelper.sendDiagramEvent(variableManager, new PinDiagramElementInput(UUID.randomUUID(), editingContext.getId(), diagram.getId(), nodeIds, false)); + }) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(variableManager -> "") + .messageProvider(variableManager -> "") + .styleProvider(variableManager -> ButtonStyle.newButtonStyle() + .backgroundColor("#ffffff") + .foregroundColor("#261E58") + .build() + ) + .helpTextProvider(variableManager -> this.messageService.unpinSelectedElements()) + .build(); + } + +} diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/api/DiagramFilterConfiguration.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/api/DiagramFilterConfiguration.java new file mode 100644 index 00000000000..0e01e3b5904 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/api/DiagramFilterConfiguration.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.services.diagramfilter.api; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Objects; + +import org.eclipse.sirius.components.collaborative.api.IRepresentationConfiguration; + +/** + * The configuration used to create a diagram filter event processor. + * + * @author gdaniel + */ +public class DiagramFilterConfiguration implements IRepresentationConfiguration { + + private static final String DIAGRAM_FILTER_PREFIX = "diagramFilter://"; + + private final String formId; + + private final List objectIds; + + public DiagramFilterConfiguration(String id, List objectIds) { + this.objectIds = Objects.requireNonNull(objectIds); + var encodedIds = objectIds.stream().map(objectId -> URLEncoder.encode(objectId, StandardCharsets.UTF_8)).toList(); + this.formId = DIAGRAM_FILTER_PREFIX + "?id=" + id + "&objectIds=[" + String.join(",", encodedIds) + "]"; + } + + @Override + public String getId() { + return this.formId; + } + + public List getObjectIds() { + return this.objectIds; + } +} diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/api/IDiagramFilterActionContributionProvider.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/api/IDiagramFilterActionContributionProvider.java new file mode 100644 index 00000000000..08647288f82 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/api/IDiagramFilterActionContributionProvider.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.services.diagramfilter.api; + +import org.eclipse.sirius.components.forms.description.ButtonDescription; + +/** + * Provides the description of a button for the diagram filter's split button. + * + * @author gdaniel + */ +public interface IDiagramFilterActionContributionProvider { + + ButtonDescription getButtonDescription(); + +} diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/api/IDiagramFilterDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/api/IDiagramFilterDescriptionProvider.java new file mode 100644 index 00000000000..5d8c882e9b5 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/api/IDiagramFilterDescriptionProvider.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.services.diagramfilter.api; + +import org.eclipse.sirius.components.forms.description.FormDescription; + +/** + * Interface used to contribute the form to display for the "Diagram Filter" view. + * + * @author gdaniel + */ +public interface IDiagramFilterDescriptionProvider { + + FormDescription getFormDescription(); + +} diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/api/IDiagramFilterHelper.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/api/IDiagramFilterHelper.java new file mode 100644 index 00000000000..973fb874d17 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/diagramfilter/api/IDiagramFilterHelper.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.services.diagramfilter.api; + +import java.util.Set; + +import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramInput; +import org.eclipse.sirius.components.representations.IStatus; +import org.eclipse.sirius.components.representations.VariableManager; + +/** + * Provides utility methods for the "Diagram Filter" view. + * + * @author gdaniel + */ +public interface IDiagramFilterHelper { + + Set getSelectedElementIds(VariableManager variableManager); + + IStatus sendDiagramEvent(VariableManager variableManager, IDiagramInput diagramInput); +} diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/messages/IServicesMessageService.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/messages/IServicesMessageService.java index 1afae141281..bcb587cc3ed 100644 --- a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/messages/IServicesMessageService.java +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/messages/IServicesMessageService.java @@ -21,16 +21,31 @@ */ public interface IServicesMessageService { + String revealSelectedFadedElements(); + + String collapseSelectedElements(); + + String expandSelectedElements(); + + String fadeSelectedElements(); + + String hideSelectedElements(); + String invalidInput(String expectedInputTypeName, String receivedInputTypeName); String invalidProjectName(); + String pinSelectedElements(); + String projectNotFound(); + String showSelectedElements(); + String unexpectedError(); String invalidDocumentName(String name); String stereotypeNotFound(UUID stereotypeId); + String unpinSelectedElements(); } diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/messages/ServicesMessageService.java b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/messages/ServicesMessageService.java index eb16fb65a7e..de05549e1eb 100644 --- a/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/messages/ServicesMessageService.java +++ b/packages/sirius-web/backend/sirius-web-services/src/main/java/org/eclipse/sirius/web/services/messages/ServicesMessageService.java @@ -33,6 +33,31 @@ public ServicesMessageService(@Qualifier("servicesMessageSourceAccessor") Messag this.messageSourceAccessor = Objects.requireNonNull(messageSourceAccessor); } + @Override + public String revealSelectedFadedElements() { + return this.messageSourceAccessor.getMessage("REVEAL_SELECTED_FADED_ELEMENTS"); + } + + @Override + public String collapseSelectedElements() { + return this.messageSourceAccessor.getMessage("COLLAPSE_SELECTED_ELEMENTS"); + } + + @Override + public String expandSelectedElements() { + return this.messageSourceAccessor.getMessage("EXPAND_SELECTED_ELEMENTS"); + } + + @Override + public String fadeSelectedElements() { + return this.messageSourceAccessor.getMessage("FADE_SELECTED_ELEMENTS"); + } + + @Override + public String hideSelectedElements() { + return this.messageSourceAccessor.getMessage("HIDE_SELECTED_ELEMENTS"); + } + @Override public String invalidInput(String expectedInputTypeName, String receivedInputTypeName) { return this.messageSourceAccessor.getMessage("INVALID_INPUT", new Object[] { expectedInputTypeName, receivedInputTypeName }); @@ -43,11 +68,21 @@ public String invalidProjectName() { return this.messageSourceAccessor.getMessage("INVALID_PROJECT_NAME"); } + @Override + public String pinSelectedElements() { + return this.messageSourceAccessor.getMessage("PIN_SELECTED_ELEMENTS"); + } + @Override public String projectNotFound() { return this.messageSourceAccessor.getMessage("PROJECT_NOT_FOUND"); } + @Override + public String showSelectedElements() { + return this.messageSourceAccessor.getMessage("SHOW_SELECTED_ELEMENTS"); + } + @Override public String unexpectedError() { return this.messageSourceAccessor.getMessage("UNEXPECTED_ERROR"); @@ -62,4 +97,9 @@ public String invalidDocumentName(String name) { public String stereotypeNotFound(UUID stereotypeId) { return this.messageSourceAccessor.getMessage("STEREOTYPE_NOT_FOUND", new Object[] { stereotypeId }); } + + @Override + public String unpinSelectedElements() { + return this.messageSourceAccessor.getMessage("UNPIN_SELECTED_ELEMENTS"); + } } diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/resources/messages/sirius-web-services.properties b/packages/sirius-web/backend/sirius-web-services/src/main/resources/messages/sirius-web-services.properties index ad68dab4d18..5a9ac2d64ff 100644 --- a/packages/sirius-web/backend/sirius-web-services/src/main/resources/messages/sirius-web-services.properties +++ b/packages/sirius-web/backend/sirius-web-services/src/main/resources/messages/sirius-web-services.properties @@ -10,9 +10,17 @@ # Contributors: # Obeo - initial API and implementation ################################################################################################ +REVEAL_SELECTED_FADED_ELEMENTS=Reveal selected faded elements +COLLAPSE_SELECTED_ELEMENTS=Collapse selected elements +EXPAND_SELECTED_ELEMENTS=Expand selected elements +FADE_SELECTED_ELEMENTS=Fade selected elements +HIDE_SELECTED_ELEMENTS=Hide selected elements INVALID_INPUT=Invalid input type, "{0}" has been received while "{1}" was expected INVALID_PROJECT_NAME=The name must contain between 3 and 20 characters +PIN_SELECTED_ELEMENTS=Pin selected elements PROJECT_NOT_FOUND=The project does not exist +SHOW_SELECTED_ELEMENTS=Show selected elements UNEXPECTED_ERROR=An unexpected error has occurred, please contact the server administrator INVALID_DOCUMENT_NAME=You cannot create a model with the name "{0}". Please use a valid name (no spaces, at least one character) for the new model -STEREOTYPE_NOT_FOUND=The model type with id "{0}" does not exist, therefore you cannot create a model from it \ No newline at end of file +STEREOTYPE_NOT_FOUND=The model type with id "{0}" does not exist, therefore you cannot create a model from it +UNPIN_SELECTED_ELEMENTS=Unpin selected elements \ No newline at end of file diff --git a/packages/sirius-web/backend/sirius-web-services/src/main/resources/schema/diagram-filter.graphqls b/packages/sirius-web/backend/sirius-web-services/src/main/resources/schema/diagram-filter.graphqls new file mode 100644 index 00000000000..730d5e666e0 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-services/src/main/resources/schema/diagram-filter.graphqls @@ -0,0 +1,3 @@ +extend type Subscription { + diagramFilterEvent(input : PropertiesEventInput!): PropertiesEventPayload! +} \ No newline at end of file diff --git a/packages/sirius-web/backend/sirius-web-services/src/test/java/org/eclipse/sirius/web/services/projects/NoOpServicesMessageService.java b/packages/sirius-web/backend/sirius-web-services/src/test/java/org/eclipse/sirius/web/services/projects/NoOpServicesMessageService.java index a72909d6df4..3efe4c88119 100644 --- a/packages/sirius-web/backend/sirius-web-services/src/test/java/org/eclipse/sirius/web/services/projects/NoOpServicesMessageService.java +++ b/packages/sirius-web/backend/sirius-web-services/src/test/java/org/eclipse/sirius/web/services/projects/NoOpServicesMessageService.java @@ -23,6 +23,31 @@ */ public class NoOpServicesMessageService implements IServicesMessageService { + @Override + public String revealSelectedFadedElements() { + return ""; + } + + @Override + public String collapseSelectedElements() { + return ""; + } + + @Override + public String expandSelectedElements() { + return ""; + } + + @Override + public String fadeSelectedElements() { + return ""; + } + + @Override + public String hideSelectedElements() { + return ""; + } + @Override public String invalidInput(String expectedInputTypeName, String receivedInputTypeName) { return ""; @@ -33,11 +58,21 @@ public String invalidProjectName() { return ""; } + @Override + public String pinSelectedElements() { + return ""; + } + @Override public String projectNotFound() { return ""; } + @Override + public String showSelectedElements() { + return ""; + } + @Override public String unexpectedError() { return ""; @@ -53,4 +88,9 @@ public String stereotypeNotFound(UUID stereotypeId) { return ""; } + @Override + public String unpinSelectedElements() { + return ""; + } + } diff --git a/packages/sirius-web/backend/sirius-web-tests/src/main/java/org/eclipse/sirius/web/tests/graphql/DiagramFilterEventSubscriptionRunner.java b/packages/sirius-web/backend/sirius-web-tests/src/main/java/org/eclipse/sirius/web/tests/graphql/DiagramFilterEventSubscriptionRunner.java new file mode 100644 index 00000000000..b29eb9aa420 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-tests/src/main/java/org/eclipse/sirius/web/tests/graphql/DiagramFilterEventSubscriptionRunner.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.tests.graphql; + +import java.util.Objects; + +import org.eclipse.sirius.components.collaborative.forms.dto.PropertiesEventInput; +import org.eclipse.sirius.components.graphql.tests.api.IGraphQLRequestor; +import org.eclipse.sirius.components.graphql.tests.api.ISubscriptionRunner; +import org.springframework.stereotype.Service; + +import reactor.core.publisher.Flux; + +/** + * Used to get the related elements event subscription with the GraphQL API. + * + * @author gdaniel + */ +@Service +public class DiagramFilterEventSubscriptionRunner implements ISubscriptionRunner { + + private static final String DIAGRAM_FILTER_EVENT_SUBSCRIPTION = """ + subscription diagramFilterEvent($input: PropertiesEventInput!) { + diagramFilterEvent(input: $input) { + __typename + } + } + """; + + private final IGraphQLRequestor graphQLRequestor; + + public DiagramFilterEventSubscriptionRunner(IGraphQLRequestor graphQLRequestor) { + this.graphQLRequestor = Objects.requireNonNull(graphQLRequestor); + } + + @Override + public Flux run(PropertiesEventInput input) { + return this.graphQLRequestor.subscribe(DIAGRAM_FILTER_EVENT_SUBSCRIPTION, input); + } +} diff --git a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/diagrams/DiagramFilterControllerTests.java b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/diagrams/DiagramFilterControllerTests.java new file mode 100644 index 00000000000..e9b8865e413 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/diagrams/DiagramFilterControllerTests.java @@ -0,0 +1,483 @@ +/******************************************************************************* + * Copyright (c) 2024 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.web.application.controllers.diagrams; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +import com.jayway.jsonpath.JsonPath; + +import java.time.Duration; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Predicate; + +import org.eclipse.sirius.components.collaborative.diagrams.api.DiagramImageConstants; +import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramRefreshedEventPayload; +import org.eclipse.sirius.components.collaborative.diagrams.dto.FadeDiagramElementInput; +import org.eclipse.sirius.components.collaborative.diagrams.dto.HideDiagramElementInput; +import org.eclipse.sirius.components.collaborative.diagrams.dto.InvokeSingleClickOnDiagramElementToolInput; +import org.eclipse.sirius.components.collaborative.diagrams.dto.InvokeSingleClickOnDiagramElementToolSuccessPayload; +import org.eclipse.sirius.components.collaborative.diagrams.dto.PinDiagramElementInput; +import org.eclipse.sirius.components.collaborative.dto.CreateRepresentationInput; +import org.eclipse.sirius.components.collaborative.forms.dto.EditTreeCheckboxInput; +import org.eclipse.sirius.components.collaborative.forms.dto.FormRefreshedEventPayload; +import org.eclipse.sirius.components.collaborative.forms.dto.PropertiesEventInput; +import org.eclipse.sirius.components.collaborative.forms.dto.PushButtonInput; +import org.eclipse.sirius.components.core.api.SuccessPayload; +import org.eclipse.sirius.components.diagrams.CollapsingState; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.diagrams.Node; +import org.eclipse.sirius.components.diagrams.ViewModifier; +import org.eclipse.sirius.components.diagrams.tests.graphql.FadeDiagramElementMutationRunner; +import org.eclipse.sirius.components.diagrams.tests.graphql.HideDiagramElementMutationRunner; +import org.eclipse.sirius.components.diagrams.tests.graphql.InvokeSingleClickOnDiagramElementToolMutationRunner; +import org.eclipse.sirius.components.diagrams.tests.graphql.PinDiagramElementMutationRunner; +import org.eclipse.sirius.components.diagrams.tests.navigation.DiagramNavigator; +import org.eclipse.sirius.components.forms.Button; +import org.eclipse.sirius.components.forms.Form; +import org.eclipse.sirius.components.forms.SplitButton; +import org.eclipse.sirius.components.forms.TreeNode; +import org.eclipse.sirius.components.forms.TreeWidget; +import org.eclipse.sirius.components.forms.tests.graphql.EditTreeCheckboxMutationRunner; +import org.eclipse.sirius.components.forms.tests.graphql.PushButtonMutationRunner; +import org.eclipse.sirius.components.forms.tests.navigation.FormNavigator; +import org.eclipse.sirius.web.AbstractIntegrationTests; +import org.eclipse.sirius.web.data.PapayaIdentifiers; +import org.eclipse.sirius.web.services.diagrams.ExpandCollapseDiagramDescriptionProvider; +import org.eclipse.sirius.web.tests.graphql.DiagramFilterEventSubscriptionRunner; +import org.eclipse.sirius.web.tests.services.api.IGivenCreatedDiagramSubscription; +import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.SqlConfig; +import org.springframework.transaction.annotation.Transactional; + +import graphql.execution.DataFetcherResult; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +/** + * Integration tests of the "Diagram Filter" view. + * + * @author gdaniel + */ +@Transactional +@SuppressWarnings("checkstyle:MultipleStringLiterals") +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { "sirius.web.test.enabled=studio" }) +public class DiagramFilterControllerTests extends AbstractIntegrationTests { + + @Autowired + private IGivenInitialServerState givenInitialServerState; + + @Autowired + private IGivenCreatedDiagramSubscription givenCreatedDiagramSubscription; + + @Autowired + private InvokeSingleClickOnDiagramElementToolMutationRunner invokeSingleClickOnDiagramElementToolMutationRunner; + + @Autowired + private EditTreeCheckboxMutationRunner editTreeCheckboxMutationRunner; + + @Autowired + private PushButtonMutationRunner pushButtonMutationRunner; + + @Autowired + private FadeDiagramElementMutationRunner fadeDiagramElementMutationRunner; + + @Autowired + private PinDiagramElementMutationRunner pinDiagramElementMutationRunner; + + @Autowired + private HideDiagramElementMutationRunner hideDiagramElementMutationRunner; + + @Autowired + private ExpandCollapseDiagramDescriptionProvider expandCollapseDiagramDescriptionProvider; + + @Autowired + private DiagramFilterEventSubscriptionRunner diagramFilterEventSubscriptionRunner; + + @BeforeEach + public void beforeEach() { + this.givenInitialServerState.initialize(); + } + + private Flux givenSubscriptionToExpandedCollapseDiagram() { + var input = new CreateRepresentationInput( + UUID.randomUUID(), + PapayaIdentifiers.PAPAYA_PROJECT.toString(), + this.expandCollapseDiagramDescriptionProvider.getRepresentationDescriptionId(), + PapayaIdentifiers.PROJECT_OBJECT.toString(), + "ExpandCollapseDiagram" + ); + return this.givenCreatedDiagramSubscription.createAndSubscribe(input); + } + + @Test + @DisplayName("Given a diagram and a diagram filter, when a tool collapsing nodes is invoked on the diagram, then the diagram filter is updated") + @Sql(scripts = {"/scripts/papaya.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = {"/scripts/cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + public void givenDiagramAndDiagramFilterWhenToolCollapsingNodeIsInvokedOnDiagramThenDiagramFilterIsUpdated() { + BiFunction collapseNodes = (diagram, nodeId) -> { + String collapseToolId = this.expandCollapseDiagramDescriptionProvider.getCollapseNodeToolId(); + var input = new InvokeSingleClickOnDiagramElementToolInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), diagram.getId(), nodeId, collapseToolId, 0, 0, null); + var result = this.invokeSingleClickOnDiagramElementToolMutationRunner.run(input); + + String typename = JsonPath.read(result, "$.data.invokeSingleClickOnDiagramElementTool.__typename"); + assertThat(typename).isEqualTo(InvokeSingleClickOnDiagramElementToolSuccessPayload.class.getSimpleName()); + + return null; + }; + + this.givenDiagramAndDiagramFilterWhenOperationIsPerformedOnDiagramThenDiagramFilterIsUpdated(collapseNodes); + } + + @Test + @DisplayName("Given a diagram and a diagram filter, when a tool fading nodes is invoked on the diagram, then the diagram filter is updated") + @Sql(scripts = {"/scripts/papaya.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = {"/scripts/cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + public void givenDiagramAndDiagramFilterWhenToolFadingNodeIsInvokedOnDiagramThenDiagramFilterIsUpdated() { + BiFunction fadeNodes = (diagram, nodeId) -> { + var input = new FadeDiagramElementInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), diagram.getId(), Set.of(nodeId), true); + var result = this.fadeDiagramElementMutationRunner.run(input); + + String typename = JsonPath.read(result, "$.data.fadeDiagramElement.__typename"); + assertThat(typename).isEqualTo(SuccessPayload.class.getSimpleName()); + + return null; + }; + + this.givenDiagramAndDiagramFilterWhenOperationIsPerformedOnDiagramThenDiagramFilterIsUpdated(fadeNodes); + } + + @Test + @DisplayName("Given a diagram and a diagram filter, when a tool pinning nodes is invoked on the diagram, then the diagram filter is updated") + @Sql(scripts = {"/scripts/papaya.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = {"/scripts/cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + public void givenDiagramAndDiagramFilterWhenToolPinningNodeIsInvokedOnDiagramThenDiagramFilterIsUpdated() { + BiFunction pinNodes = (diagram, nodeId) -> { + var input = new PinDiagramElementInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), diagram.getId(), Set.of(nodeId), true); + var result = this.pinDiagramElementMutationRunner.run(input); + + String typename = JsonPath.read(result, "$.data.pinDiagramElement.__typename"); + assertThat(typename).isEqualTo(SuccessPayload.class.getSimpleName()); + + return null; + }; + + this.givenDiagramAndDiagramFilterWhenOperationIsPerformedOnDiagramThenDiagramFilterIsUpdated(pinNodes); + } + + @Test + @DisplayName("Given a diagram and a diagram filter, when a tool hiding nodes is invoked on the diagram, then the diagram filter is updated") + @Sql(scripts = {"/scripts/papaya.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = {"/scripts/cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + public void givenDiagramAndDiagramFilterWhenToolHidingNodeIsInvokedOnDiagramThenDiagramFilterIsUpdated() { + BiFunction hideNodes = (diagram, nodeId) -> { + var input = new HideDiagramElementInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), diagram.getId(), Set.of(nodeId), true); + var result = this.hideDiagramElementMutationRunner.run(input); + + String typename = JsonPath.read(result, "$.data.hideDiagramElement.__typename"); + assertThat(typename).isEqualTo(SuccessPayload.class.getSimpleName()); + + return null; + }; + + this.givenDiagramAndDiagramFilterWhenOperationIsPerformedOnDiagramThenDiagramFilterIsUpdated(hideNodes); + } + + private void givenDiagramAndDiagramFilterWhenOperationIsPerformedOnDiagramThenDiagramFilterIsUpdated(BiFunction operationToPerform) { + var diagramFlux = this.givenSubscriptionToExpandedCollapseDiagram(); + + AtomicReference diagramReference = new AtomicReference<>(); + AtomicReference nodeId = new AtomicReference<>(); + + Consumer initialDiagramContentConsumer = payload -> Optional.of(payload) + .map(DiagramRefreshedEventPayload::diagram) + .ifPresentOrElse(diagram -> { + diagramReference.set(diagram); + diagram.getNodes().stream() + .filter(node -> node.getCollapsingState().equals(CollapsingState.EXPANDED)) + .map(Node::getId) + .findFirst() + .ifPresent(nodeId::set); + }, () -> fail("Missing diagram")); + + StepVerifier.create(diagramFlux) + .consumeNextWith(initialDiagramContentConsumer) + .thenCancel() + .verify(Duration.ofSeconds(10)); + + var propertiesInput = new PropertiesEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), List.of(diagramReference.get().getId())); + var diagramFilterFlux = this.diagramFilterEventSubscriptionRunner.run(propertiesInput); + + Predicate formContentMatcher = object -> this.refreshedForm(object) + .filter(form -> { + var diagramNavigator = new DiagramNavigator(diagramReference.get()); + var treeWidget = new FormNavigator(form) + .page("ExpandCollapseDiagram") + .group("Filter elements") + .findWidget("", TreeWidget.class); + assertThat(treeWidget.getNodes()).hasSize(diagramNavigator.findDiagramNodeCount()); + for (TreeNode treeNode : treeWidget.getNodes()) { + var node = diagramNavigator.nodeWithLabel(treeNode.getLabel()).getNode(); + this.assertThatNodeMatchesTreeNodeEndIcons(node, treeNode); + } + return true; + }) + .isPresent(); + + Consumer udpatedDiagramContentConsumer = object -> this.refreshedDiagram(object).ifPresentOrElse(diagramReference::set, () -> fail("Missing diagram")); + + var diagramAndPropertiesFlux = Flux.merge(diagramFlux, diagramFilterFlux); + + StepVerifier.create(diagramAndPropertiesFlux) + .expectNextMatches(DiagramRefreshedEventPayload.class::isInstance) + .expectNextMatches(formContentMatcher) + .then(() -> operationToPerform.apply(diagramReference.get(), nodeId.get())) + .consumeNextWith(udpatedDiagramContentConsumer) + .expectNextMatches(formContentMatcher) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + @Test + @DisplayName("Given a diagram and a diagram filter, when a tool collapsing nodes is invoked on the diagram filter, then the diagram is updated") + @Sql(scripts = {"/scripts/papaya.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = {"/scripts/cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + public void givenDiagramAndDiagramFilterWhenToolCollapsingNodeIsInvokedOnDiagramFilterThenDiagramIsUpdated() { + Predicate nodeToSelectPredicate = node -> node.getCollapsingState().equals(CollapsingState.EXPANDED); + + Predicate