Skip to content

Commit

Permalink
[1070] Reveal the currently selected representation in the explorer
Browse files Browse the repository at this point in the history
- Switch to a flat tree format for the GraphQL message so that the
  frontend does not need to keep track of try to guess the depth of
  the tree (and remove the now obsolete maxDpeth).
- Ask the backend to reveal the currently selected elements by
  expanding their ancestors.
- Update TreeRenderer to convert elements to
  reveal (TreeEventInput.revealed) into ancestors to expand using the
  new TreeDescription.getAncestorsProvider().

Bug: #1070
Signed-off-by: Pierre-Charles David <pierre-charles.david@obeo.fr>
  • Loading branch information
pcdavid committed Feb 21, 2022
1 parent 4bcf663 commit b9aac8d
Show file tree
Hide file tree
Showing 17 changed files with 323 additions and 159 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
- https://github.com/eclipse-sirius/sirius-components/issues/1045[#1045] [core] Providers will now return List<?> instead of List<Object>. This makes it possible for applications to reuse existing services to implement providers without making useless copies of lists
- https://github.com/eclipse-sirius/sirius-components/issues/1068[#1068] [form] The form representation is now supporting multiple elements as an input
- https://github.com/eclipse-sirius/sirius-components/issues/1068[#1068] [workbench] The integration of the details view in the workbench is not limited to semantic objects with a kind starting with `siriusComponents://semantic`. Any object can be used as the input of the details view and we will now provide the identifier of all the objects in the selection. This may include graphical elements such as nodes, edges, representations or anything selected in the explorer for example
- https://github.com/eclipse-sirius/sirius-components/issues/1070[#1070] [tree] `TreeDescription` has a new `getAncestorsProvider()` API used to compute the set of ancestors to expand in a given tree to reveal specified elements.
- https://github.com/eclipse-sirius/sirius-components/issues/1070[#1070] [tree] The shape of the GraphQL Schema used to send tree instances to the backend has been changed into a flat tree of items (to be rebuilt into a proper tree on the front). This makes the depth of the GraphQL subscription needed to track a particular tree independent on the depth of the tree itself.

=== Dependency update

Expand All @@ -52,6 +54,7 @@
- https://github.com/eclipse-sirius/sirius-components/issues/1054[#1054] [diagram] Add missing variables to compute the label of an edge
- https://github.com/eclipse-sirius/sirius-components/issues/1063[#1063] [explorer] It is now possible to expand or collapse items in the explorer without selecting them by clicking directly on the expand/collapse arrow icon
- https://github.com/eclipse-sirius/sirius-components/issues/1068[#1068] [form] Add support for displaying details on arbitrary element kinds
- https://github.com/eclipse-sirius/sirius-components/issues/1070[#1070] [explorer] When selecting a specific representation (for example from its URL or from the onboard area), it is automatically made visible and selected in the explorer.

=== New features

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public <T extends IRepresentationEventProcessor> Optional<T> createRepresentatio
TreeCreationParameters treeCreationParameters = TreeCreationParameters.newTreeCreationParameters(treeConfiguration.getId())
.treeDescription(treeDescription)
.expanded(treeConfiguration.getExpanded())
.revealed(treeConfiguration.getRevealed())
.editingContext(editingContext)
.build();
// @formatter:on
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public Tree create(TreeCreationParameters treeCreationParameters) {
variableManager.put(GetOrCreateRandomIdProvider.PREVIOUS_REPRESENTATION_ID, treeCreationParameters.getId());
variableManager.put(IEditingContext.EDITING_CONTEXT, treeCreationParameters.getEditingContext());
variableManager.put(TreeRenderer.EXPANDED, treeCreationParameters.getExpanded());
variableManager.put(TreeRenderer.REVEALED, treeCreationParameters.getRevealed());

TreeRenderer treeRenderer = new TreeRenderer(variableManager, treeCreationParameters.getTreeDescription());
return treeRenderer.render();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,18 @@ public class TreeConfiguration implements IRepresentationConfiguration {

private final List<String> expanded;

private List<String> revealed;

public TreeConfiguration(String editingContextId, List<String> expanded) {
String uniqueId = editingContextId + expanded.toString();
this(editingContextId, expanded, List.of());

}

public TreeConfiguration(String editingContextId, List<String> expanded, List<String> revealed) {
String uniqueId = editingContextId + expanded.toString() + revealed.toString();
this.treeId = UUID.nameUUIDFromBytes(uniqueId.getBytes()).toString();
this.expanded = Objects.requireNonNull(expanded);
this.revealed = List.copyOf(Objects.requireNonNull(revealed));
}

@Override
Expand All @@ -44,4 +52,7 @@ public List<String> getExpanded() {
return this.expanded;
}

public List<String> getRevealed() {
return this.revealed;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public final class TreeCreationParameters {

private List<String> expanded;

private List<String> revealed;

private IEditingContext editingContext;

private TreeCreationParameters() {
Expand All @@ -51,6 +53,10 @@ public List<String> getExpanded() {
return this.expanded;
}

public List<String> getRevealed() {
return this.revealed;
}

public IEditingContext getEditingContext() {
return this.editingContext;
}
Expand All @@ -61,8 +67,8 @@ public static Builder newTreeCreationParameters(String id) {

@Override
public String toString() {
String pattern = "{0} '{'id: {1}, treeDescriptionId: {2}, expanded: {3}'}'"; //$NON-NLS-1$
return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.treeDescription.getId(), this.expanded);
String pattern = "{0} '{'id: {1}, treeDescriptionId: {2}, expanded: {3}, revealed: {4}'}'"; //$NON-NLS-1$
return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.treeDescription.getId(), this.expanded, this.revealed);
}

/**
Expand All @@ -78,6 +84,8 @@ public static final class Builder {

private List<String> expanded;

private List<String> revealed;

private IEditingContext editingContext;

private Builder(String id) {
Expand All @@ -94,6 +102,11 @@ public Builder expanded(List<String> expanded) {
return this;
}

public Builder revealed(List<String> revealed) {
this.revealed = Objects.requireNonNull(revealed);
return this;
}

public Builder editingContext(IEditingContext editingContext) {
this.editingContext = Objects.requireNonNull(editingContext);
return this;
Expand All @@ -104,6 +117,7 @@ public TreeCreationParameters build() {
treeCreationParameters.id = Objects.requireNonNull(this.id);
treeCreationParameters.treeDescription = Objects.requireNonNull(this.treeDescription);
treeCreationParameters.expanded = Objects.requireNonNull(this.expanded);
treeCreationParameters.revealed = Objects.requireNonNull(this.revealed);
treeCreationParameters.editingContext = Objects.requireNonNull(this.editingContext);
return treeCreationParameters;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,17 @@ public final class TreeEventInput implements IInput {

private List<String> expanded;

private List<String> revealed;

public TreeEventInput() {
// Used by Jackson
}

public TreeEventInput(UUID id, String editingContextId, List<String> expanded) {
public TreeEventInput(UUID id, String editingContextId, List<String> expanded, List<String> revealed) {
this.id = Objects.requireNonNull(id);
this.editingContextId = Objects.requireNonNull(editingContextId);
this.expanded = Objects.requireNonNull(expanded);
this.expanded = List.copyOf(Objects.requireNonNull(expanded));
this.revealed = List.copyOf(Objects.requireNonNull(revealed));
}

@Override
Expand All @@ -54,9 +57,13 @@ public List<String> getExpanded() {
return this.expanded;
}

public List<String> getRevealed() {
return this.revealed;
}

@Override
public String toString() {
String pattern = "{0} '{'id: {1}, editingContextId: {2}, expanded: {3}'}'"; //$NON-NLS-1$
return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.editingContextId, this.expanded);
String pattern = "{0} '{'id: {1}, editingContextId: {2}, expanded: {3}, revealed: {4}'}'"; //$NON-NLS-1$
return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.editingContextId, this.expanded, this.revealed);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ input TreeEventInput {
id: ID!
editingContextId: ID!
expanded: [String!]!
revealed: [String!]!
}

union TreeEventPayload = ErrorPayload | SubscribersUpdatedEventPayload | TreeRefreshedEventPayload
Expand All @@ -18,19 +19,20 @@ type TreeRefreshedEventPayload {
type Tree implements Representation {
id: ID!
metadata: RepresentationMetadata!
children: [TreeItem!]!
items: [TreeItem!]!
}

type TreeItem {
id: ID!
parentId: ID
position: Int
label: String!
kind: String!
imageURL: String!
editable: Boolean!
deletable: Boolean!
expanded: Boolean!
hasChildren: Boolean!
children: [TreeItem]!
}

type TreeDescription implements RepresentationDescription {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public final class TreeDescription implements IRepresentationDescription {

private Function<VariableManager, List<?>> childrenProvider;

private Function<VariableManager, List<String>> ancestorsProvider;

private Function<VariableManager, Boolean> hasChildrenProvider;

private Predicate<VariableManager> canCreatePredicate;
Expand Down Expand Up @@ -115,6 +117,10 @@ public Function<VariableManager, List<?>> getChildrenProvider() {
return this.childrenProvider;
}

public Function<VariableManager, List<String>> getAncestorsProvider() {
return this.ancestorsProvider;
}

public Function<VariableManager, Boolean> getHasChildrenProvider() {
return this.hasChildrenProvider;
}
Expand Down Expand Up @@ -171,6 +177,8 @@ public static final class Builder {

private Function<VariableManager, List<?>> childrenProvider;

private Function<VariableManager, List<String>> ancestorsProvider = variableManager -> List.of();

private Function<VariableManager, Boolean> hasChildrenProvider;

private Predicate<VariableManager> canCreatePredicate;
Expand Down Expand Up @@ -233,6 +241,11 @@ public Builder childrenProvider(Function<VariableManager, List<?>> childrenProvi
return this;
}

public Builder ancestorsProvider(Function<VariableManager, List<String>> ancestorsProvider) {
this.ancestorsProvider = Objects.requireNonNull(ancestorsProvider);
return this;
}

public Builder hasChildrenProvider(Function<VariableManager, Boolean> hasChildrenProvider) {
this.hasChildrenProvider = Objects.requireNonNull(hasChildrenProvider);
return this;
Expand Down Expand Up @@ -266,6 +279,7 @@ public TreeDescription build() {
treeDescription.deletableProvider = Objects.requireNonNull(this.deletableProvider);
treeDescription.elementsProvider = Objects.requireNonNull(this.elementsProvider);
treeDescription.childrenProvider = Objects.requireNonNull(this.childrenProvider);
treeDescription.ancestorsProvider = Objects.requireNonNull(this.ancestorsProvider);
treeDescription.hasChildrenProvider = Objects.requireNonNull(this.hasChildrenProvider);
treeDescription.canCreatePredicate = Objects.requireNonNull(this.canCreatePredicate);
treeDescription.deleteHandler = Objects.requireNonNull(this.deleteHandler);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import org.eclipse.sirius.components.representations.VariableManager;
import org.eclipse.sirius.components.trees.Tree;
Expand All @@ -30,9 +31,11 @@ public class TreeRenderer {

public static final String EXPANDED = "expanded"; //$NON-NLS-1$

private VariableManager variableManager;
public static final String REVEALED = "revealed"; //$NON-NLS-1$

private TreeDescription treeDescription;
private final VariableManager variableManager;

private final TreeDescription treeDescription;

public TreeRenderer(VariableManager variableManager, TreeDescription treeDescription) {
this.variableManager = Objects.requireNonNull(variableManager);
Expand All @@ -43,6 +46,12 @@ public Tree render() {
String treeId = this.treeDescription.getIdProvider().apply(this.variableManager);
String label = this.treeDescription.getLabelProvider().apply(this.variableManager);

List<String> expandedIds = new ArrayList<>();
expandedIds.addAll(this.getExpandedIds());
List<String> ancestors = this.treeDescription.getAncestorsProvider().apply(this.variableManager);
expandedIds.addAll(ancestors);
this.variableManager.put(TreeRenderer.EXPANDED, List.copyOf(expandedIds));

List<?> rootElements = this.treeDescription.getElementsProvider().apply(this.variableManager);
List<TreeItem> childrenItems = new ArrayList<>(rootElements.size());
for (Object rootElement : rootElements) {
Expand All @@ -60,6 +69,16 @@ public Tree render() {
// @formatter:on
}

private List<String> getExpandedIds() {
List<String> expandedIds = new ArrayList<>();
Object objects = this.variableManager.getVariables().get(TreeRenderer.EXPANDED);
if (objects instanceof List<?>) {
List<?> list = (List<?>) objects;
expandedIds = list.stream().filter(String.class::isInstance).map(String.class::cast).collect(Collectors.toUnmodifiableList());
}
return expandedIds;
}

private TreeItem renderTreeItem(VariableManager treeItemVariableManager) {
String id = this.treeDescription.getTreeItemIdProvider().apply(treeItemVariableManager);
String kind = this.treeDescription.getKindProvider().apply(treeItemVariableManager);
Expand Down
48 changes: 43 additions & 5 deletions frontend/src/explorer/ExplorerWebSocketContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
import { gql, useSubscription } from '@apollo/client';
import { useSubscription } from '@apollo/client';
import { M, Spacing } from 'core/spacing/Spacing';
import { Text } from 'core/text/Text';
import {
Expand All @@ -20,30 +20,63 @@ import {
HANDLE_DATA__ACTION,
HANDLE_ERROR__ACTION,
HANDLE_EXPANDED__ACTION,
HANDLE_SYNCHRONIZE__ACTION,
LOADING__STATE,
} from 'explorer/machine';
import React, { useReducer } from 'react';
import gql from 'graphql-tag';
import React, { useEffect, useReducer } from 'react';
import { Explorer } from './Explorer';
import styles from './ExplorerWebSocketContainer.module.css';
import { ExplorerWebSocketContainerProps } from './ExplorerWebSocketContainer.types';
import { getTreeEventSubscription } from './getTreeEventSubscription';
import { initialState, reducer } from './reducer';

const treeEventSubscription = gql`
subscription treeEvent($input: TreeEventInput!) {
treeEvent(input: $input) {
__typename
... on TreeRefreshedEventPayload {
id
tree {
id
items {
id
parentId
position
hasChildren
expanded
label
editable
deletable
kind
imageURL
}
}
}
}
}
`;

export const ExplorerWebSocketContainer = ({
editingContextId,
selection,
setSelection,
readOnly,
}: ExplorerWebSocketContainerProps) => {
const [state, dispatch] = useReducer(reducer, initialState);
const { viewState, id, tree, expanded, maxDepth, message } = state;
const { viewState, id, tree, expanded, synchronized, message } = state;

const { error } = useSubscription(gql(getTreeEventSubscription(maxDepth)), {
let revealed = [];
if (synchronized && selection.entries.length > 0) {
revealed = selection.entries.map((entry) => entry.kind + '&id=' + entry.id);
}

const { error } = useSubscription(treeEventSubscription, {
variables: {
input: {
id,
editingContextId,
expanded,
revealed,
},
},
fetchPolicy: 'no-cache',
Expand All @@ -57,6 +90,11 @@ export const ExplorerWebSocketContainer = ({
dispatch({ type: HANDLE_ERROR__ACTION, message: error });
}

// Enable synchronize mode when the selection is explicitly changed
useEffect(() => {
dispatch({ type: HANDLE_SYNCHRONIZE__ACTION, synchronized: true });
}, [selection]);

const onExpand = (id: string, depth: number) => {
dispatch({ type: HANDLE_EXPANDED__ACTION, id, depth });
};
Expand Down
Loading

0 comments on commit b9aac8d

Please sign in to comment.