Skip to content

Commit

Permalink
Add option to expand and collapse all children of segment group (#7911)
Browse files Browse the repository at this point in the history
* WIP: add option to expand and collable all children of segment group

* recursively open subgroups

* adjust icon, label and expand all groups on default

* WIP: add two menu entries

* add two menu entries and hide/show them as useful

* remove comments

* only collapse subgroups

* remove unnecessary types and extract groupid to key conversion to method

* avoid multiple calculations to get children

* only add group itself to expanded if it is collapsed

* add changelog

* address review
  • Loading branch information
dieknolle3333 authored Jul 23, 2024
1 parent 5e2fb23 commit b588729
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 3 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- WEBKNOSSOS now automatically searches in subfolder / sub-collection identifiers for valid datasets in case a provided link to a remote dataset does not directly point to a dataset. [#7912](https://github.com/scalableminds/webknossos/pull/7912)
- Added the option to move a bounding box via dragging while pressing ctrl / meta. [#7892](https://github.com/scalableminds/webknossos/pull/7892)
- Added route `/import?url=<url_to_datasource>` to automatically import and view remote datasets. [#7844](https://github.com/scalableminds/webknossos/pull/7844)
- The context menu that is opened upon right-clicking a segment in the dataview port now contains the segment's name. [#7920](https://github.com/scalableminds/webknossos/pull/7920)
- Added option to expand or collapse all subgroups of a segment group in the segments tab. [#7911](https://github.com/scalableminds/webknossos/pull/7911)
- The context menu that is opened upon right-clicking a segment in the dataview port now contains the segment's name. [#7920](https://github.com/scalableminds/webknossos/pull/7920)
- Upgraded backend dependencies for improved performance and stability. [#7922](https://github.com/scalableminds/webknossos/pull/7922)

### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
EyeInvisibleOutlined,
EyeOutlined,
CloseOutlined,
ShrinkOutlined,
ExpandAltOutlined,
} from "@ant-design/icons";
import type RcTree from "rc-tree";
import { getJobs, startComputeMeshFileJob } from "admin/admin_rest_api";
Expand Down Expand Up @@ -323,6 +325,7 @@ type State = {
[groupId: number]: { areSomeSegmentsVisible: boolean; areSomeSegmentsInvisible: boolean };
};
activeStatisticsModalGroupId: number | null;
expandedGroupKeys: Key[];
};

const formatMagWithLabel = (mag: Vector3, index: number) => {
Expand Down Expand Up @@ -402,6 +405,7 @@ class SegmentsView extends React.Component<Props, State> {
groupToDelete: null,
groupsSegmentsVisibilityStateMap: {},
activeStatisticsModalGroupId: null,
expandedGroupKeys: [],
};
tree: React.RefObject<RcTree>;

Expand All @@ -425,6 +429,10 @@ class SegmentsView extends React.Component<Props, State> {
}

Store.dispatch(ensureSegmentIndexIsLoadedAction(this.props.visibleSegmentationLayer?.name));

const allGroups = this.getSubGroupsAsTreeNodes(MISSING_GROUP_ID);
allGroups.push(this.getKeyForGroupId(MISSING_GROUP_ID));
this.setState({ expandedGroupKeys: allGroups });
}

componentDidUpdate(prevProps: Props) {
Expand Down Expand Up @@ -461,6 +469,38 @@ class SegmentsView extends React.Component<Props, State> {
}
}

onExpandTree = (expandedKeys: Key[]) => {
this.setState({ expandedGroupKeys: expandedKeys });
};

getKeyForGroupId = (groupId: number) => `group-${groupId}`;

getSubGroupsAsTreeNodes = (groupId: number) => {
if (groupId !== MISSING_GROUP_ID) {
return getGroupByIdWithSubgroups(this.props.segmentGroups, groupId)
.filter((group) => group !== groupId)
.map((group) => this.getKeyForGroupId(group));
}
const allSegmentGroups = this.props.segmentGroups.flatMap((group) =>
getGroupByIdWithSubgroups(this.props.segmentGroups, group.groupId),
);
return allSegmentGroups.map((group) => this.getKeyForGroupId(group));
};

expandGroups = (groupsToExpand: Key[]) => {
this.setState({
expandedGroupKeys: this.state.expandedGroupKeys?.concat(groupsToExpand),
});
};

collapseGroups = (groupsToCollapse: Key[]) => {
this.setState({
expandedGroupKeys: this.state.expandedGroupKeys?.filter(
(key) => groupsToCollapse.indexOf(key.toString()) === -1,
),
});
};

onSelectTreeItem = (
keys: Key[],
event: {
Expand Down Expand Up @@ -1405,7 +1445,7 @@ class SegmentsView extends React.Component<Props, State> {
(segmentId) => `segment-${segmentId}`,
);
if (this.props.selectedIds.group != null) {
return mappedIdsToKeys.concat(`group-${this.props.selectedIds.group}`);
return mappedIdsToKeys.concat(this.getKeyForGroupId(this.props.selectedIds.group));
}
return mappedIdsToKeys;
};
Expand Down Expand Up @@ -1686,6 +1726,8 @@ class SegmentsView extends React.Component<Props, State> {
icon: <DeleteOutlined />,
label: "Delete group",
},
this.getExpandSubgroupsItem(id),
this.getCollapseSubgroupsItem(id),
this.getMoveSegmentsHereMenuItem(id),
{
key: "groupAndMeshActionDivider",
Expand Down Expand Up @@ -1792,7 +1834,6 @@ class SegmentsView extends React.Component<Props, State> {
allowDrop={this.allowDrop}
onDrop={this.onDrop}
onSelect={this.onSelectTreeItem}
defaultExpandAll
className="segments-tree"
blockNode
// Passing an explicit height here, makes the tree virtualized
Expand All @@ -1817,6 +1858,8 @@ class SegmentsView extends React.Component<Props, State> {
overflow: "auto", // use hidden when not using virtualization
}}
ref={this.tree}
onExpand={this.onExpandTree}
expandedKeys={this.state.expandedGroupKeys}
/>
</div>
)}
Expand All @@ -1842,6 +1885,48 @@ class SegmentsView extends React.Component<Props, State> {
);
}

getExpandSubgroupsItem(groupId: number) {
const children = this.getSubGroupsAsTreeNodes(groupId);
const expandedKeySet = new Set(this.state.expandedGroupKeys);
const areAllChildrenExpanded = children.every((childNode) => expandedKeySet.has(childNode));
const isGroupItselfExpanded = expandedKeySet.has(this.getKeyForGroupId(groupId));
if (areAllChildrenExpanded && isGroupItselfExpanded) {
return null;
}
const groupsToExpand = children;
// It doesn't make sense to expand subgroups if the group itself is collapsed, so also expand the group.
if (!isGroupItselfExpanded) groupsToExpand.push(this.getKeyForGroupId(groupId));
return {
key: "expandAll",
onClick: () => {
this.expandGroups(groupsToExpand);
this.closeSegmentOrGroupDropdown();
},
icon: <ExpandAltOutlined />,
label: "Expand all subgroups",
};
}

getCollapseSubgroupsItem(groupId: number) {
const children = this.getSubGroupsAsTreeNodes(groupId);
const expandedKeySet = new Set(this.state.expandedGroupKeys);
const areAllChildrenCollapsed =
children.filter((childNode) => expandedKeySet.has(childNode)).length === 0;
const isGroupItselfCollapsed = !expandedKeySet.has(this.getKeyForGroupId(groupId));
if (areAllChildrenCollapsed || isGroupItselfCollapsed) {
return null;
}
return {
key: "collapseAll",
onClick: () => {
this.collapseGroups(children);
this.closeSegmentOrGroupDropdown();
},
icon: <ShrinkOutlined />,
label: "Collapse all subgroups",
};
}

createGroup(parentGroupId: number): void {
if (!this.props.visibleSegmentationLayer) {
return;
Expand Down

0 comments on commit b588729

Please sign in to comment.