diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index c8806953ae..bccb310007 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -25,6 +25,7 @@ === Improvements +- https://github.com/eclipse-sirius/sirius-web/issues/4348[#4348] [sirius-web] Move quick tools declaration on the backend == v2025.1.0 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 08bd282b67..c7ed6ba8eb 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 @@ -19,6 +19,8 @@ */ public final class DiagramImageConstants { + public static final String HIDE_SVG = "/icons/full/obj16/HideTool.svg"; + private static final String IMAGES_ROOT_FOLDER = "/diagram-images"; public static final String EDIT_SVG = IMAGES_ROOT_FOLDER + "/edit.svg"; diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/index.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/index.ts index 42f6233dce..5e9c1f3fc4 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/index.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/index.ts @@ -64,7 +64,7 @@ export type { DiagramPaletteToolContributionProps, } from './renderer/palette/extensions/DiagramPaletteToolContribution.types'; export { diagramPaletteToolExtensionPoint } from './renderer/palette/extensions/DiagramPaletteToolExtensionPoints'; -export type { GQLToolVariable, GQLToolVariableType } from './renderer/palette/usePalette.types'; +export type { GQLToolVariable, GQLToolVariableType } from './renderer/palette/tools/useInvokeTool.types'; export type { DiagramPanelActionProps } from './renderer/panel/DiagramPanel.types'; export { diagramPanelActionExtensionPoint } from './renderer/panel/DiagramPanelExtensionPoints'; export { DiagramRepresentation } from './representation/DiagramRepresentation'; diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/collapse/useCollapseExpandElement.tsx b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/collapse/useCollapseExpandElement.tsx new file mode 100644 index 0000000000..08a92c603b --- /dev/null +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/collapse/useCollapseExpandElement.tsx @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2023, 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 { gql, useMutation } from '@apollo/client'; +import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; +import { useCallback, useContext, useEffect } from 'react'; +import { DiagramContext } from '../../contexts/DiagramContext'; +import { DiagramContextValue } from '../../contexts/DiagramContext.types'; +import { + GQLCollapsingState, + GQLErrorPayload, + GQLUpdateCollapsingStateData, + GQLUpdateCollapsingStateInput, + GQLUpdateCollapsingStatePayload, + GQLUpdateCollapsingStateVariables, + UseCollapseExpandElement, +} from './useCollapseExpandElement.types'; + +const updateCollapsingStateMutation = gql` + mutation updateCollapsingState($input: UpdateCollapsingStateInput!) { + updateCollapsingState(input: $input) { + __typename + ... on SuccessPayload { + id + } + ... on ErrorPayload { + message + } + } + } +`; + +const isErrorPayload = (payload: GQLUpdateCollapsingStatePayload): payload is GQLErrorPayload => + payload.__typename === 'ErrorPayload'; + +export const useCollapseExpandElement = (): UseCollapseExpandElement => { + const { addMessages, addErrorMessage } = useMultiToast(); + const { diagramId, editingContextId } = useContext(DiagramContext); + + const [collapseExpandMutation, { error: collpaseNodeError }] = useMutation< + GQLUpdateCollapsingStateData, + GQLUpdateCollapsingStateVariables + >(updateCollapsingStateMutation); + + const collapseExpandElement = useCallback( + async (nodeId: string, collapsingState: GQLCollapsingState) => { + const input: GQLUpdateCollapsingStateInput = { + id: crypto.randomUUID(), + editingContextId, + representationId: diagramId, + diagramElementId: nodeId, + collapsingState, + }; + const { data: collapseNodeData } = await collapseExpandMutation({ variables: { input } }); + if (collapseNodeData) { + const { collapseExpandDiagramElement } = collapseNodeData; + if (isErrorPayload(collapseExpandDiagramElement)) { + addMessages(collapseExpandDiagramElement.messages); + } + } + }, + [editingContextId, diagramId, collapseExpandMutation] + ); + + useEffect(() => { + if (collpaseNodeError) { + addErrorMessage('An unexpected error has occurred, please refresh the page'); + } + }, [collpaseNodeError]); + + return { collapseExpandElement }; +}; diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/collapse/useCollapseExpandElement.types.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/collapse/useCollapseExpandElement.types.ts new file mode 100644 index 0000000000..b6080e7d63 --- /dev/null +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/collapse/useCollapseExpandElement.types.ts @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2023, 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 { GQLMessage } from '../Tool.types'; + +export interface UseCollapseExpandElement { + collapseExpandElement: (nodeId: string, collapsingState: GQLCollapsingState) => void; +} + +export interface GQLUpdateCollapsingStateData { + collapseExpandDiagramElement: GQLUpdateCollapsingStatePayload; +} + +export interface GQLUpdateCollapsingStatePayload { + __typename: string; +} + +export interface GQLUpdateCollapsingStateVariables { + input: GQLUpdateCollapsingStateInput; +} + +export interface GQLUpdateCollapsingStateInput { + id: string; + editingContextId: string; + representationId: string; + diagramElementId: string; + collapsingState: GQLCollapsingState; +} + +export enum GQLCollapsingState { + EXPANDED = 'EXPANDED', + COLLAPSED = 'COLLAPSED', +} + +export interface GQLErrorPayload extends GQLUpdateCollapsingStatePayload { + message: string; + messages: GQLMessage[]; +} diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/delete/useDiagramDelete.tsx b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/delete/useDiagramDelete.tsx index 8c4b0e57d9..83cd289461 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/delete/useDiagramDelete.tsx +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/delete/useDiagramDelete.tsx @@ -10,71 +10,21 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -import { gql, useMutation } from '@apollo/client'; -import { useDeletionConfirmationDialog, useMultiToast } from '@eclipse-sirius/sirius-components-core'; +import { useDeletionConfirmationDialog } from '@eclipse-sirius/sirius-components-core'; import { Edge, Node, useReactFlow } from '@xyflow/react'; -import { useCallback, useContext, useEffect } from 'react'; +import { useCallback, useContext } from 'react'; import { DiagramContext } from '../../contexts/DiagramContext'; import { DiagramContextValue } from '../../contexts/DiagramContext.types'; import { EdgeData, NodeData } from '../DiagramRenderer.types'; -import { - GQLDeleteFromDiagramData, - GQLDeleteFromDiagramInput, - GQLDeleteFromDiagramPayload, - GQLDeleteFromDiagramSuccessPayload, - GQLDeleteFromDiagramVariables, - GQLDeletionPolicy, - GQLErrorPayload, -} from '../palette/usePalette.types'; import { UseDiagramDeleteValue } from './useDiagramDelete.types'; - -export const deleteFromDiagramMutation = gql` - mutation deleteFromDiagram($input: DeleteFromDiagramInput!) { - deleteFromDiagram(input: $input) { - __typename - ... on ErrorPayload { - messages { - body - level - } - } - ... on DeleteFromDiagramSuccessPayload { - messages { - body - level - } - } - } - } -`; - -const isErrorPayload = (payload: GQLDeleteFromDiagramPayload): payload is GQLErrorPayload => - payload.__typename === 'ErrorPayload'; -const isSuccessPayload = (payload: GQLDeleteFromDiagramPayload): payload is GQLDeleteFromDiagramSuccessPayload => - payload.__typename === 'DeleteFromDiagramSuccessPayload'; +import { useDiagramDeleteMutation } from './useDiagramDeleteMutation'; +import { GQLDeletionPolicy } from './useDiagramDeleteMutation.types'; export const useDiagramDelete = (): UseDiagramDeleteValue => { - const { addErrorMessage, addMessages } = useMultiToast(); - const { showDeletionConfirmation } = useDeletionConfirmationDialog(); - const { diagramId, editingContextId, readOnly } = useContext(DiagramContext); const { getNodes } = useReactFlow, Edge>(); - - const [deleteElementsMutation, { data: deleteElementsData, error: deleteElementsError }] = useMutation< - GQLDeleteFromDiagramData, - GQLDeleteFromDiagramVariables - >(deleteFromDiagramMutation); - - useEffect(() => { - if (deleteElementsError) { - addErrorMessage('An unexpected error has occurred, please refresh the page'); - } - if (deleteElementsData) { - const { deleteFromDiagram } = deleteElementsData; - if (isErrorPayload(deleteFromDiagram) || isSuccessPayload(deleteFromDiagram)) { - addMessages(deleteFromDiagram.messages); - } - } - }, [deleteElementsData, deleteElementsError]); + const { diagramId, editingContextId, readOnly } = useContext(DiagramContext); + const { deleteElements } = useDiagramDeleteMutation(); + const { showDeletionConfirmation } = useDeletionConfirmationDialog(); const onDelete = useCallback((event: React.KeyboardEvent) => { const { key } = event; @@ -89,16 +39,9 @@ export const useDiagramDelete = (): UseDiagramDeleteValue => { const nodeToDeleteIds: string[] = getNodes() .filter((node) => node.selected) .map((node) => node.id); - const input: GQLDeleteFromDiagramInput = { - id: crypto.randomUUID(), - editingContextId, - representationId: diagramId, - nodeIds: nodeToDeleteIds, - edgeIds: [], - deletionPolicy: GQLDeletionPolicy.SEMANTIC, - }; + showDeletionConfirmation(() => { - deleteElementsMutation({ variables: { input } }); + deleteElements(nodeToDeleteIds, [], GQLDeletionPolicy.SEMANTIC); }); } }, []); diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/delete/useDiagramDeleteMutation.tsx b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/delete/useDiagramDeleteMutation.tsx new file mode 100644 index 0000000000..3f97b2b8eb --- /dev/null +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/delete/useDiagramDeleteMutation.tsx @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2023, 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 { gql, useMutation } from '@apollo/client'; +import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; +import { useCallback, useContext, useEffect } from 'react'; +import { DiagramContext } from '../../contexts/DiagramContext'; +import { DiagramContextValue } from '../../contexts/DiagramContext.types'; +import { + GQLDeleteFromDiagramData, + GQLDeleteFromDiagramInput, + GQLDeleteFromDiagramPayload, + GQLDeleteFromDiagramSuccessPayload, + GQLDeleteFromDiagramVariables, + GQLDeletionPolicy, + GQLErrorPayload, + UseDiagramDeleteMutation, +} from './useDiagramDeleteMutation.types'; + +export const deleteFromDiagramMutation = gql` + mutation deleteFromDiagram($input: DeleteFromDiagramInput!) { + deleteFromDiagram(input: $input) { + __typename + ... on ErrorPayload { + messages { + body + level + } + } + ... on DeleteFromDiagramSuccessPayload { + messages { + body + level + } + } + } + } +`; + +const isErrorPayload = (payload: GQLDeleteFromDiagramPayload): payload is GQLErrorPayload => + payload.__typename === 'ErrorPayload'; + +const isSuccessPayload = (payload: GQLDeleteFromDiagramPayload): payload is GQLDeleteFromDiagramSuccessPayload => + payload.__typename === 'DeleteFromDiagramSuccessPayload'; + +export const useDiagramDeleteMutation = (): UseDiagramDeleteMutation => { + const { addMessages, addErrorMessage } = useMultiToast(); + const { diagramId, editingContextId } = useContext(DiagramContext); + + const [deleteElementsMutation, { data: deleteElementsData, error: deleteElementsError }] = useMutation< + GQLDeleteFromDiagramData, + GQLDeleteFromDiagramVariables + >(deleteFromDiagramMutation); + + const deleteElements = useCallback( + async (nodeIds: string[], edgesId: string[], deletePocily: GQLDeletionPolicy) => { + const input: GQLDeleteFromDiagramInput = { + id: crypto.randomUUID(), + editingContextId, + representationId: diagramId, + nodeIds: nodeIds, + edgeIds: edgesId, + deletionPolicy: deletePocily, + }; + + const { data } = await deleteElementsMutation({ variables: { input } }); + if (data) { + const { deleteFromDiagram } = data; + if (isErrorPayload(deleteFromDiagram) || isSuccessPayload(deleteFromDiagram)) { + addMessages(deleteFromDiagram.messages); + } + } + }, + [editingContextId, diagramId, deleteElementsMutation] + ); + + useEffect(() => { + if (deleteElementsError) { + addErrorMessage('An unexpected error has occurred, please refresh the page'); + } + if (deleteElementsData) { + const { deleteFromDiagram } = deleteElementsData; + if (isErrorPayload(deleteFromDiagram) || isSuccessPayload(deleteFromDiagram)) { + addMessages(deleteFromDiagram.messages); + } + } + }, [deleteElementsData, deleteElementsError]); + + return { deleteElements }; +}; diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/delete/useDiagramDeleteMutation.types.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/delete/useDiagramDeleteMutation.types.ts new file mode 100644 index 0000000000..d28efe674d --- /dev/null +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/delete/useDiagramDeleteMutation.types.ts @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2023, 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 { GQLMessage } from '../Tool.types'; + +export interface UseDiagramDeleteMutation { + deleteElements: (nodeIds: string[], edgesId: string[], deletePocily: GQLDeletionPolicy) => void; +} + +export interface GQLDeleteFromDiagramSuccessPayload extends GQLDeleteFromDiagramPayload { + messages: GQLMessage[]; +} + +export interface GQLDeleteFromDiagramVariables { + input: GQLDeleteFromDiagramInput; +} + +export interface GQLDeleteFromDiagramInput { + id: string; + editingContextId: string; + representationId: string; + nodeIds: string[]; + edgeIds: string[]; + deletionPolicy: GQLDeletionPolicy; +} + +export interface GQLDeleteFromDiagramData { + deleteFromDiagram: GQLDeleteFromDiagramPayload; +} + +export interface GQLDeleteFromDiagramPayload { + __typename: string; +} + +export enum GQLDeletionPolicy { + SEMANTIC = 'SEMANTIC', + GRAPHICAL = 'GRAPHICAL', +} + +export interface GQLErrorPayload extends GQLDeleteFromDiagramPayload { + message: string; + messages: GQLMessage[]; +} diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/quick-access-tool/PaletteQuickAccessToolBar.tsx b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/quick-access-tool/PaletteQuickAccessToolBar.tsx index 1b94f7b4d7..b974765abf 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/quick-access-tool/PaletteQuickAccessToolBar.tsx +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/quick-access-tool/PaletteQuickAccessToolBar.tsx @@ -21,16 +21,9 @@ import { diagramPaletteToolExtensionPoint } from '../extensions/DiagramPaletteTo import { Tool } from './../../Tool'; import { DiagramPaletteToolContributionProps } from './../extensions/DiagramPaletteToolContribution.types'; import { AdjustSizeTool } from './AdjustSizeTool'; -import { FadeElementTool } from './FadeElementTool'; import { PaletteQuickAccessToolBarProps } from './PaletteQuickAccessToolBar.types'; -import { PinUnPinTool } from './PinUnPinTool'; import { ResetEditedEdgePathTool } from './ResetEditedEdgePathTool'; -const isPinnable = (diagramElement: Node | Edge): diagramElement is Node => { - return !!diagramElement.data && 'pinned' in diagramElement.data; -}; -const isFadable = (diagramElement: Node | Edge): diagramElement is Node => { - return !!diagramElement.data && 'faded' in diagramElement.data; -}; + const isBendable = (diagramElement: Node | Edge): diagramElement is Edge => { return !!diagramElement.data && 'bendingPoints' in diagramElement.data && !!diagramElement.data.bendingPoints; }; @@ -64,22 +57,6 @@ export const PaletteQuickAccessToolBar = ({ ); if (diagramElement) { - if (isPinnable(diagramElement)) { - quickAccessToolComponents.push( - - ); - } - if (isFadable(diagramElement)) { - quickAccessToolComponents.push( - - ); - } if (isBendable(diagramElement)) { quickAccessToolComponents.push( { + const { fadeDiagramElements } = useFadeDiagramElements(); + const { hideDiagramElements } = useHideDiagramElements(); + const { pinDiagramElements } = usePinDiagramElements(); + const { collapseExpandElement } = useCollapseExpandElement(); + const { deleteElements } = useDiagramDeleteMutation(); + const { showDeletionConfirmation } = useDeletionConfirmationDialog(); + const { invokeSingleClickTool } = useSingleClickOnDiagramElementTool({ + x, + y, + diagramElementId, + targetObjectId, + onDirectEditClick, + }); + + const { nodeLookup, edgeLookup } = useStoreApi, Edge>().getState(); + const { hideDiagramPalette, setLastToolInvoked } = useDiagramPalette(); + const { hideDiagramElementPalette } = useDiagramElementPalette(); + + const closeAllPalettes = useCallback(() => { + hideDiagramPalette(); + hideDiagramElementPalette(); + }, [hideDiagramPalette, hideDiagramElementPalette]); + + const invokeDelete = (diagramElementId: string, deletionPolicy: GQLDeletionPolicy) => { + if (!!nodeLookup.get(diagramElementId)) { + deleteElements([diagramElementId], [], deletionPolicy); + } else if (!!edgeLookup.get(diagramElementId)) { + deleteElements([], [diagramElementId], deletionPolicy); + } + }; + + const invokeTool = (tool: GQLTool, variables: GQLToolVariable[]) => { + closeAllPalettes(); + switch (tool.id) { + case 'edit': + onDirectEditClick(); + break; + case 'semantic-delete': + showDeletionConfirmation(() => { + invokeDelete(diagramElementId, GQLDeletionPolicy.SEMANTIC); + }); + break; + case 'graphical-delete': + invokeDelete(diagramElementId, GQLDeletionPolicy.GRAPHICAL); + break; + case 'expand': + collapseExpandElement(diagramElementId, GQLCollapsingState.EXPANDED); + break; + case 'collapse': + collapseExpandElement(diagramElementId, GQLCollapsingState.COLLAPSED); + break; + case 'fade': + fadeDiagramElements([diagramElementId], true); + break; + case 'unfade': + fadeDiagramElements([diagramElementId], false); + break; + case 'hide': + hideDiagramElements([diagramElementId], true); + break; + case 'pin': + pinDiagramElements([diagramElementId], true); + break; + case 'unpin': + pinDiagramElements([diagramElementId], false); + break; + default: + invokeSingleClickTool(tool, variables); + break; + } + if (palette) { + setLastToolInvoked(palette.id, tool); + } + }; + + return { invokeTool }; +}; diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/tools/useInvokeTool.types.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/tools/useInvokeTool.types.ts new file mode 100644 index 0000000000..5af2f32d6c --- /dev/null +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/tools/useInvokeTool.types.ts @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2023, 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 { GQLMessage } from '../../Tool.types'; +import { GQLPalette, GQLTool } from '../Palette.types'; + +export interface InvokeToolProps { + x: number; + y: number; + diagramElementId: string; + targetObjectId: string; + palette: GQLPalette | null; + onDirectEditClick: () => void; +} + +export interface InvokeTool { + invokeTool: (tool: GQLTool, variables: GQLToolVariable[]) => void; +} + +export interface GQLInvokeSingleClickOnDiagramElementToolData { + invokeSingleClickOnDiagramElementTool: GQLInvokeSingleClickOnDiagramElementToolPayload; +} + +export interface GQLInvokeSingleClickOnDiagramElementToolPayload { + __typename: string; +} + +export interface GQLInvokeSingleClickOnDiagramElementToolSuccessPayload + extends GQLInvokeSingleClickOnDiagramElementToolPayload { + id: string; + newSelection: GQLWorkbenchSelection; + messages: GQLMessage[]; +} + +export interface GQLWorkbenchSelection { + entries: GQLWorkbenchSelectionEntry[]; +} + +export interface GQLWorkbenchSelectionEntry { + id: string; + kind: string; +} + +export interface GQLToolVariable { + name: string; + value: string; + type: GQLToolVariableType; +} + +export type GQLToolVariableType = 'STRING' | 'OBJECT_ID' | 'OBJECT_ID_ARRAY'; + +export interface GQLInvokeSingleClickOnDiagramElementToolVariables { + input: GQLInvokeSingleClickOnDiagramElementToolInput; +} + +export interface GQLInvokeSingleClickOnDiagramElementToolInput { + id: string; + editingContextId: string; + representationId: string; + diagramElementId: string; + toolId: string; + startingPositionX: number; + startingPositionY: number; + variables: GQLToolVariable[]; +} + +export interface GQLErrorPayload extends GQLInvokeSingleClickOnDiagramElementToolPayload { + message: string; + messages: GQLMessage[]; +} diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/tools/useSingleClickOnDiagramElementTool.tsx b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/tools/useSingleClickOnDiagramElementTool.tsx new file mode 100644 index 0000000000..c81e1d6a48 --- /dev/null +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/tools/useSingleClickOnDiagramElementTool.tsx @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2023, 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 { gql, useMutation } from '@apollo/client'; +import { useMultiToast, useSelection } from '@eclipse-sirius/sirius-components-core'; +import { useCallback, useContext } from 'react'; +import { DiagramContext } from '../../../contexts/DiagramContext'; +import { DiagramContextValue } from '../../../contexts/DiagramContext.types'; +import { useDialog } from '../../../dialog/useDialog'; +import { GQLSingleClickOnDiagramElementTool, GQLTool } from '../Palette.types'; +import { + GQLErrorPayload, + GQLInvokeSingleClickOnDiagramElementToolData, + GQLInvokeSingleClickOnDiagramElementToolInput, + GQLInvokeSingleClickOnDiagramElementToolPayload, + GQLInvokeSingleClickOnDiagramElementToolSuccessPayload, + GQLInvokeSingleClickOnDiagramElementToolVariables, + GQLToolVariable, + InvokeSingleClickTool, + InvokeSingleClickToolProps, +} from './useSingleClickOnDiagramElementTool.types'; + +const invokeSingleClickOnDiagramElementToolMutation = gql` + mutation invokeSingleClickOnDiagramElementTool($input: InvokeSingleClickOnDiagramElementToolInput!) { + invokeSingleClickOnDiagramElementTool(input: $input) { + __typename + ... on InvokeSingleClickOnDiagramElementToolSuccessPayload { + newSelection { + entries { + id + kind + } + } + messages { + body + level + } + } + ... on ErrorPayload { + messages { + body + level + } + } + } + } +`; + +const isInvokeSingleClickSuccessPayload = ( + payload: GQLInvokeSingleClickOnDiagramElementToolPayload +): payload is GQLInvokeSingleClickOnDiagramElementToolSuccessPayload => + payload.__typename === 'InvokeSingleClickOnDiagramElementToolSuccessPayload'; + +const isSingleClickOnDiagramElementTool = (tool: GQLTool): tool is GQLSingleClickOnDiagramElementTool => + tool.__typename === 'SingleClickOnDiagramElementTool'; + +const isErrorPayload = (payload: GQLInvokeSingleClickOnDiagramElementToolPayload): payload is GQLErrorPayload => + payload.__typename === 'ErrorPayload'; + +export const useSingleClickOnDiagramElementTool = ({ + x, + y, + diagramElementId, + targetObjectId, +}: InvokeSingleClickToolProps): InvokeSingleClickTool => { + const { addMessages } = useMultiToast(); + const { diagramId, editingContextId } = useContext(DiagramContext); + const { setSelection } = useSelection(); + const { showDialog } = useDialog(); + + const [invokeSingleClickOnDiagramElementTool] = useMutation< + GQLInvokeSingleClickOnDiagramElementToolData, + GQLInvokeSingleClickOnDiagramElementToolVariables + >(invokeSingleClickOnDiagramElementToolMutation); + + const executeTool = useCallback( + async (tool: GQLTool, variables: GQLToolVariable[]) => { + if (isSingleClickOnDiagramElementTool(tool)) { + const { id: toolId } = tool; + const input: GQLInvokeSingleClickOnDiagramElementToolInput = { + id: crypto.randomUUID(), + editingContextId, + representationId: diagramId, + diagramElementId, + toolId, + startingPositionX: x, + startingPositionY: y, + variables, + }; + + const { data } = await invokeSingleClickOnDiagramElementTool({ + variables: { input }, + }); + if (data) { + const { invokeSingleClickOnDiagramElementTool } = data; + if (isInvokeSingleClickSuccessPayload(invokeSingleClickOnDiagramElementTool)) { + const { newSelection } = invokeSingleClickOnDiagramElementTool; + if (newSelection?.entries.length ?? 0 > 0) { + setSelection(newSelection); + } + addMessages(invokeSingleClickOnDiagramElementTool.messages); + } + if (isErrorPayload(invokeSingleClickOnDiagramElementTool)) { + addMessages(invokeSingleClickOnDiagramElementTool.messages); + } + } + } + }, + [ + x, + y, + editingContextId, + diagramId, + diagramElementId, + invokeSingleClickOnDiagramElementToolMutation, + isSingleClickOnDiagramElementTool, + ] + ); + + const handleDialogDescription = (tool: GQLSingleClickOnDiagramElementTool) => { + const onConfirm = (variables: GQLToolVariable[]) => executeTool(tool, variables); + showDialog(tool.dialogDescriptionId, [{ name: 'targetObjectId', value: targetObjectId }], onConfirm, () => {}); + }; + + const invokeSingleClickTool = (tool: GQLTool, variables: GQLToolVariable[]) => { + if (isSingleClickOnDiagramElementTool(tool)) { + if (tool.dialogDescriptionId) { + handleDialogDescription(tool); + } else { + executeTool(tool, variables); + } + } + }; + + return { invokeSingleClickTool }; +}; diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/tools/useSingleClickOnDiagramElementTool.types.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/tools/useSingleClickOnDiagramElementTool.types.ts new file mode 100644 index 0000000000..4590e9faf1 --- /dev/null +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/tools/useSingleClickOnDiagramElementTool.types.ts @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2023, 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 { GQLMessage } from '../../Tool.types'; +import { GQLTool } from '../Palette.types'; + +export interface InvokeSingleClickToolProps { + x: number; + y: number; + diagramElementId: string; + targetObjectId: string; + onDirectEditClick: () => void; +} + +export interface InvokeSingleClickTool { + invokeSingleClickTool: (tool: GQLTool, variables: GQLToolVariable[]) => void; +} + +export interface GQLInvokeSingleClickOnDiagramElementToolData { + invokeSingleClickOnDiagramElementTool: GQLInvokeSingleClickOnDiagramElementToolPayload; +} + +export interface GQLInvokeSingleClickOnDiagramElementToolPayload { + __typename: string; +} + +export interface GQLInvokeSingleClickOnDiagramElementToolSuccessPayload + extends GQLInvokeSingleClickOnDiagramElementToolPayload { + id: string; + newSelection: GQLWorkbenchSelection; + messages: GQLMessage[]; +} + +export interface GQLWorkbenchSelection { + entries: GQLWorkbenchSelectionEntry[]; +} + +export interface GQLWorkbenchSelectionEntry { + id: string; + kind: string; +} + +export interface GQLToolVariable { + name: string; + value: string; + type: GQLToolVariableType; +} + +export type GQLToolVariableType = 'STRING' | 'OBJECT_ID' | 'OBJECT_ID_ARRAY'; + +export interface GQLInvokeSingleClickOnDiagramElementToolVariables { + input: GQLInvokeSingleClickOnDiagramElementToolInput; +} + +export interface GQLInvokeSingleClickOnDiagramElementToolInput { + id: string; + editingContextId: string; + representationId: string; + diagramElementId: string; + toolId: string; + startingPositionX: number; + startingPositionY: number; + variables: GQLToolVariable[]; +} + +export interface GQLErrorPayload extends GQLInvokeSingleClickOnDiagramElementToolPayload { + message: string; + messages: GQLMessage[]; +} diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/usePalette.tsx b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/usePalette.tsx index e364eb5522..132dc3c72a 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/usePalette.tsx +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/usePalette.tsx @@ -11,40 +11,18 @@ * Obeo - initial API and implementation *******************************************************************************/ -import { gql, useMutation, useQuery } from '@apollo/client'; -import { useDeletionConfirmationDialog, useMultiToast, useSelection } from '@eclipse-sirius/sirius-components-core'; -import { Edge, Node, useStoreApi } from '@xyflow/react'; -import { useCallback, useContext, useEffect } from 'react'; +import { gql, useQuery } from '@apollo/client'; +import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; +import { useContext, useEffect } from 'react'; import { DiagramContext } from '../../contexts/DiagramContext'; import { DiagramContextValue } from '../../contexts/DiagramContext.types'; -import { useDialog } from '../../dialog/useDialog'; -import { EdgeData, NodeData } from '../DiagramRenderer.types'; -import { GQLPalette, GQLSingleClickOnDiagramElementTool, GQLTool } from './Palette.types'; - -import { useDiagramElementPalette } from './useDiagramElementPalette'; -import { useDiagramPalette } from './useDiagramPalette'; +import { GQLPalette, GQLTool } from './Palette.types'; +import { useInvokeTool } from './tools/useInvokeTool'; import { - GQLCollapsingState, - GQLDeleteFromDiagramData, - GQLDeleteFromDiagramInput, - GQLDeleteFromDiagramPayload, - GQLDeleteFromDiagramSuccessPayload, - GQLDeleteFromDiagramVariables, - GQLDeletionPolicy, GQLDiagramDescription, - GQLErrorPayload, GQLGetToolSectionsData, GQLGetToolSectionsVariables, - GQLInvokeSingleClickOnDiagramElementToolData, - GQLInvokeSingleClickOnDiagramElementToolInput, - GQLInvokeSingleClickOnDiagramElementToolPayload, - GQLInvokeSingleClickOnDiagramElementToolSuccessPayload, - GQLInvokeSingleClickOnDiagramElementToolVariables, GQLRepresentationDescription, - GQLToolVariable, - GQLUpdateCollapsingStateData, - GQLUpdateCollapsingStateInput, - GQLUpdateCollapsingStateVariables, UsePaletteProps, UsePaletteValue, } from './usePalette.types'; @@ -94,79 +72,6 @@ export const getPaletteQuery = gql` } `; -const invokeSingleClickOnDiagramElementToolMutation = gql` - mutation invokeSingleClickOnDiagramElementTool($input: InvokeSingleClickOnDiagramElementToolInput!) { - invokeSingleClickOnDiagramElementTool(input: $input) { - __typename - ... on InvokeSingleClickOnDiagramElementToolSuccessPayload { - newSelection { - entries { - id - kind - } - } - messages { - body - level - } - } - ... on ErrorPayload { - messages { - body - level - } - } - } - } -`; - -export const deleteFromDiagramMutation = gql` - mutation deleteFromDiagram($input: DeleteFromDiagramInput!) { - deleteFromDiagram(input: $input) { - __typename - ... on ErrorPayload { - messages { - body - level - } - } - ... on DeleteFromDiagramSuccessPayload { - messages { - body - level - } - } - } - } -`; - -const updateCollapsingStateMutation = gql` - mutation updateCollapsingState($input: UpdateCollapsingStateInput!) { - updateCollapsingState(input: $input) { - __typename - ... on SuccessPayload { - id - } - ... on ErrorPayload { - message - } - } - } -`; - -const isErrorPayload = ( - payload: GQLDeleteFromDiagramPayload | GQLInvokeSingleClickOnDiagramElementToolPayload -): payload is GQLErrorPayload => payload.__typename === 'ErrorPayload'; -const isDeleteSuccessPayload = (payload: GQLDeleteFromDiagramPayload): payload is GQLDeleteFromDiagramSuccessPayload => - payload.__typename === 'DeleteFromDiagramSuccessPayload'; -const isInvokeSingleClickSuccessPayload = ( - payload: GQLInvokeSingleClickOnDiagramElementToolPayload -): payload is GQLInvokeSingleClickOnDiagramElementToolSuccessPayload => - payload.__typename === 'InvokeSingleClickOnDiagramElementToolSuccessPayload'; - -const isSingleClickOnDiagramElementTool = (tool: GQLTool): tool is GQLSingleClickOnDiagramElementTool => - tool.__typename === 'SingleClickOnDiagramElementTool'; - const isDiagramDescription = ( representationDescription: GQLRepresentationDescription ): representationDescription is GQLDiagramDescription => representationDescription.__typename === 'DiagramDescription'; @@ -178,13 +83,8 @@ export const usePalette = ({ targetObjectId, onDirectEditClick, }: UsePaletteProps): UsePaletteValue => { - const { nodeLookup, edgeLookup } = useStoreApi, Edge>().getState(); const { diagramId, editingContextId } = useContext(DiagramContext); - - const { addErrorMessage, addMessages } = useMultiToast(); - const { showDeletionConfirmation } = useDeletionConfirmationDialog(); - const { showDialog } = useDialog(); - const { setSelection } = useSelection(); + const { addErrorMessage } = useMultiToast(); const { data: paletteData, error: paletteError } = useQuery( getPaletteQuery, @@ -197,165 +97,20 @@ export const usePalette = ({ } ); - const { hideDiagramPalette } = useDiagramPalette(); - const { hideDiagramElementPalette } = useDiagramElementPalette(); - - const closeAllPalettes = useCallback(() => { - hideDiagramPalette(); - hideDiagramElementPalette(); - }, [hideDiagramPalette, hideDiagramElementPalette]); - const description: GQLRepresentationDescription | undefined = paletteData?.viewer.editingContext.representation.description; + const palette: GQLPalette | null = description && isDiagramDescription(description) ? description.palette : null; + + const { invokeTool } = useInvokeTool({ x, y, diagramElementId, targetObjectId, palette, onDirectEditClick }); + + const handleToolClick = (tool: GQLTool) => invokeTool(tool, []); + useEffect(() => { if (paletteError) { addErrorMessage('An unexpected error has occurred, please refresh the page'); } }, [paletteError]); - const [deleteElementsMutation] = useMutation( - deleteFromDiagramMutation - ); - - const [invokeSingleClickOnDiagramElementTool] = useMutation< - GQLInvokeSingleClickOnDiagramElementToolData, - GQLInvokeSingleClickOnDiagramElementToolVariables - >(invokeSingleClickOnDiagramElementToolMutation); - - const invokeSingleClickTool = useCallback( - async (tool: GQLTool, variables: GQLToolVariable[]) => { - if (isSingleClickOnDiagramElementTool(tool)) { - const { id: toolId } = tool; - const input: GQLInvokeSingleClickOnDiagramElementToolInput = { - id: crypto.randomUUID(), - editingContextId, - representationId: diagramId, - diagramElementId, - toolId, - startingPositionX: x, - startingPositionY: y, - variables, - }; - - const { data } = await invokeSingleClickOnDiagramElementTool({ - variables: { input }, - }); - if (data) { - const { invokeSingleClickOnDiagramElementTool } = data; - if (isInvokeSingleClickSuccessPayload(invokeSingleClickOnDiagramElementTool)) { - const { newSelection } = invokeSingleClickOnDiagramElementTool; - if (newSelection?.entries.length ?? 0 > 0) { - setSelection(newSelection); - } - addMessages(invokeSingleClickOnDiagramElementTool.messages); - } - if (isErrorPayload(invokeSingleClickOnDiagramElementTool)) { - addMessages(invokeSingleClickOnDiagramElementTool.messages); - } - } - } - }, - [ - x, - y, - editingContextId, - diagramId, - diagramElementId, - invokeSingleClickOnDiagramElementToolMutation, - isSingleClickOnDiagramElementTool, - ] - ); - - const invokeDelete = (diagramElementId: string, deletionPolicy: GQLDeletionPolicy) => { - if (!!nodeLookup.get(diagramElementId)) { - invokeDeleteMutation([diagramElementId], [], deletionPolicy); - } else if (!!edgeLookup.get(diagramElementId)) { - invokeDeleteMutation([], [diagramElementId], deletionPolicy); - } - }; - - const invokeDeleteMutation = useCallback( - async (nodeIds: string[], edgeIds: string[], deletionPolicy: GQLDeletionPolicy) => { - const input: GQLDeleteFromDiagramInput = { - id: crypto.randomUUID(), - editingContextId, - representationId: diagramId, - nodeIds, - edgeIds, - deletionPolicy, - }; - const { data } = await deleteElementsMutation({ variables: { input } }); - if (data) { - const { deleteFromDiagram } = data; - if (isErrorPayload(deleteFromDiagram) || isDeleteSuccessPayload(deleteFromDiagram)) { - addMessages(deleteFromDiagram.messages); - } - } - }, - [editingContextId, diagramId, deleteElementsMutation] - ); - - const [collapseExpandMutation] = useMutation( - updateCollapsingStateMutation - ); - - const collapseExpandElement = useCallback( - (nodeId: string, collapsingState: GQLCollapsingState) => { - const input: GQLUpdateCollapsingStateInput = { - id: crypto.randomUUID(), - editingContextId, - representationId: diagramId, - diagramElementId: nodeId, - collapsingState, - }; - collapseExpandMutation({ variables: { input } }); - }, - [editingContextId, diagramId, collapseExpandMutation] - ); - - const handleDialogDescription = (tool: GQLSingleClickOnDiagramElementTool) => { - const onConfirm = (variables: GQLToolVariable[]) => { - invokeSingleClickTool(tool, variables); - }; - showDialog(tool.dialogDescriptionId, [{ name: 'targetObjectId', value: targetObjectId }], onConfirm, () => {}); - }; - - const { setLastToolInvoked } = useDiagramPalette(); - - const handleToolClick = (tool: GQLTool) => { - closeAllPalettes(); - switch (tool.id) { - case 'edit': - onDirectEditClick(); - break; - case 'semantic-delete': - showDeletionConfirmation(() => { - invokeDelete(diagramElementId, GQLDeletionPolicy.SEMANTIC); - }); - break; - case 'graphical-delete': - invokeDelete(diagramElementId, GQLDeletionPolicy.GRAPHICAL); - break; - case 'expand': - collapseExpandElement(diagramElementId, GQLCollapsingState.EXPANDED); - break; - case 'collapse': - collapseExpandElement(diagramElementId, GQLCollapsingState.COLLAPSED); - break; - default: - if (isSingleClickOnDiagramElementTool(tool)) { - if (tool.dialogDescriptionId) { - handleDialogDescription(tool); - } else { - invokeSingleClickTool(tool, []); - } - } - break; - } - if (palette) { - setLastToolInvoked(palette.id, tool); - } - }; return { handleToolClick, palette }; }; diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/usePalette.types.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/usePalette.types.ts index efa2cd84ad..6516b61903 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/usePalette.types.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/renderer/palette/usePalette.types.ts @@ -11,7 +11,6 @@ * Obeo - initial API and implementation *******************************************************************************/ -import { GQLMessage } from './../Tool.types'; import { GQLPalette, GQLTool } from './Palette.types'; export interface UsePaletteProps { x: number; @@ -25,122 +24,13 @@ export interface UsePaletteValue { handleToolClick: (tool: GQLTool) => void; palette: GQLPalette | null; } + export interface GQLGetToolSectionsVariables { editingContextId: string; diagramId: string; diagramElementId: string; } -export interface GQLUpdateCollapsingStateData { - collapseExpandDiagramElement: GQLUpdateCollapsingStatePayload; -} - -export interface GQLUpdateCollapsingStatePayload { - __typename: string; -} - -export interface GQLUpdateCollapsingStateVariables { - input: GQLUpdateCollapsingStateInput; -} - -export interface GQLUpdateCollapsingStateInput { - id: string; - editingContextId: string; - representationId: string; - diagramElementId: string; - collapsingState: GQLCollapsingState; -} - -export enum GQLCollapsingState { - EXPANDED = 'EXPANDED', - COLLAPSED = 'COLLAPSED', -} - -export interface GQLDeleteFromDiagramSuccessPayload extends GQLDeleteFromDiagramPayload { - messages: GQLMessage[]; -} - -export interface GQLDeleteFromDiagramVariables { - input: GQLDeleteFromDiagramInput; -} - -export interface GQLDeleteFromDiagramInput { - id: string; - editingContextId: string; - representationId: string; - nodeIds: string[]; - edgeIds: string[]; - deletionPolicy: GQLDeletionPolicy; -} - -export interface GQLDeleteFromDiagramData { - deleteFromDiagram: GQLDeleteFromDiagramPayload; -} - -export interface GQLDeleteFromDiagramPayload { - __typename: string; -} - -export enum GQLDeletionPolicy { - SEMANTIC = 'SEMANTIC', - GRAPHICAL = 'GRAPHICAL', -} - -export interface GQLErrorPayload - extends GQLInvokeSingleClickOnDiagramElementToolPayload, - GQLDeleteFromDiagramPayload, - GQLUpdateCollapsingStatePayload { - message: string; - messages: GQLMessage[]; -} - -export interface GQLInvokeSingleClickOnDiagramElementToolData { - invokeSingleClickOnDiagramElementTool: GQLInvokeSingleClickOnDiagramElementToolPayload; -} - -export interface GQLInvokeSingleClickOnDiagramElementToolPayload { - __typename: string; -} - -export interface GQLInvokeSingleClickOnDiagramElementToolSuccessPayload - extends GQLInvokeSingleClickOnDiagramElementToolPayload { - id: string; - newSelection: GQLWorkbenchSelection; - messages: GQLMessage[]; -} - -export interface GQLWorkbenchSelection { - entries: GQLWorkbenchSelectionEntry[]; -} - -export interface GQLWorkbenchSelectionEntry { - id: string; - kind: string; -} - -export interface GQLInvokeSingleClickOnDiagramElementToolVariables { - input: GQLInvokeSingleClickOnDiagramElementToolInput; -} - -export interface GQLInvokeSingleClickOnDiagramElementToolInput { - id: string; - editingContextId: string; - representationId: string; - diagramElementId: string; - toolId: string; - startingPositionX: number; - startingPositionY: number; - variables: GQLToolVariable[]; -} - -export interface GQLToolVariable { - name: string; - value: string; - type: GQLToolVariableType; -} - -export type GQLToolVariableType = 'STRING' | 'OBJECT_ID' | 'OBJECT_ID_ARRAY'; - export interface GQLGetToolSectionsData { viewer: GQLViewer; } diff --git a/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/PaletteDefaultToolsProvider.java b/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/PaletteDefaultToolsProvider.java index d203da808f..b61197aaa5 100644 --- a/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/PaletteDefaultToolsProvider.java +++ b/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/PaletteDefaultToolsProvider.java @@ -20,7 +20,9 @@ import org.eclipse.sirius.components.collaborative.diagrams.dto.ITool; import org.eclipse.sirius.components.collaborative.diagrams.dto.SingleClickOnDiagramElementTool; import org.eclipse.sirius.components.collaborative.diagrams.dto.ToolSection; +import org.eclipse.sirius.components.diagrams.Edge; import org.eclipse.sirius.components.diagrams.Node; +import org.eclipse.sirius.components.diagrams.ViewModifier; import org.eclipse.sirius.components.diagrams.description.EdgeDescription; import org.eclipse.sirius.components.diagrams.description.EdgeLabelKind; import org.eclipse.sirius.components.diagrams.description.IDiagramElementDescription; @@ -81,15 +83,25 @@ private List createExtraTools(Object diagramElementDescription, Object di var graphicalDeleteTool = this.createExtraGraphicalDeleteTool(targetDescriptions); extraTools.add(graphicalDeleteTool); } + if (this.isCollapsible(diagramElementDescription, diagramElement)) { + // Collapse or expand Tool (the handler is never called) + var expandCollapseTool = this.createExtraExpandCollapseTool(targetDescriptions, diagramElement); + expandCollapseTool.ifPresent(extraTools::add); + } if (this.hasDeleteTool(diagramElementDescription)) { // Semantic Delete Tool (the handler is never called) var semanticDeleteTool = this.createExtraSemanticDeleteTool(targetDescriptions); extraTools.add(semanticDeleteTool); } - if (this.isCollapsible(diagramElementDescription, diagramElement)) { - // Collapse or expand Tool (the handler is never called) - var expandCollapseTool = this.createExtraExpandCollapseTool(targetDescriptions, diagramElement); - expandCollapseTool.ifPresent(extraTools::add); + var fadeTool = this.createFadeTool(targetDescriptions, diagramElement); + extraTools.add(fadeTool); + + var hideTool = this.createHideTool(targetDescriptions); + extraTools.add(hideTool); + + if (diagramElement instanceof Node node) { + var pinTool = this.createPinTool(targetDescriptions, node); + extraTools.add(pinTool); } return extraTools; } @@ -147,6 +159,49 @@ private boolean isCollapsible(Object diagramElementDescription, Object diagramEl return false; } + private ITool createFadeTool(List targetDescriptions, Object diagramElement) { + String label = "Fade element"; + String id = "fade"; + if (diagramElement instanceof Node node && node.getModifiers().contains(ViewModifier.Faded) + || diagramElement instanceof Edge edge && edge.getModifiers().contains(ViewModifier.Faded)) { + label = "Unfade element"; + id = "unfade"; + } + return SingleClickOnDiagramElementTool.newSingleClickOnDiagramElementTool(id) + .label(label) + .iconURL(List.of(DiagramImageConstants.FADE_SVG)) + .targetDescriptions(targetDescriptions) + .appliesToDiagramRoot(false) + .build(); + } + + private ITool createHideTool(List targetDescriptions) { + return SingleClickOnDiagramElementTool.newSingleClickOnDiagramElementTool("hide") + .label("Hide element") + .iconURL(List.of(DiagramImageConstants.HIDE_SVG)) + .targetDescriptions(targetDescriptions) + .appliesToDiagramRoot(false) + .build(); + } + + private ITool createPinTool(List targetDescriptions, Node node) { + if (node.isPinned()) { + return SingleClickOnDiagramElementTool.newSingleClickOnDiagramElementTool("unpin") + .label("Unpin") + .iconURL(List.of(DiagramImageConstants.UNPIN_SVG)) + .targetDescriptions(targetDescriptions) + .appliesToDiagramRoot(false) + .build(); + } else { + return SingleClickOnDiagramElementTool.newSingleClickOnDiagramElementTool("pin") + .label("Pin") + .iconURL(List.of(DiagramImageConstants.PIN_SVG)) + .targetDescriptions(targetDescriptions) + .appliesToDiagramRoot(false) + .build(); + } + } + private boolean hasLabelEditTool(Object diagramElementDescription) { boolean result = true; if (diagramElementDescription instanceof NodeDescription nodeDescription) {