Skip to content

Commit b588729

Browse files
Add option to expand and collapse all children of segment group (#7911)
* 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
1 parent 5e2fb23 commit b588729

File tree

2 files changed

+89
-3
lines changed

2 files changed

+89
-3
lines changed

CHANGELOG.unreleased.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
1414
- 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)
1515
- Added the option to move a bounding box via dragging while pressing ctrl / meta. [#7892](https://github.com/scalableminds/webknossos/pull/7892)
1616
- Added route `/import?url=<url_to_datasource>` to automatically import and view remote datasets. [#7844](https://github.com/scalableminds/webknossos/pull/7844)
17-
- 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)
17+
- Added option to expand or collapse all subgroups of a segment group in the segments tab. [#7911](https://github.com/scalableminds/webknossos/pull/7911)
18+
- 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)
1819
- Upgraded backend dependencies for improved performance and stability. [#7922](https://github.com/scalableminds/webknossos/pull/7922)
1920

2021
### Changed

frontend/javascripts/oxalis/view/right-border-tabs/segments_tab/segments_view.tsx

+87-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
EyeInvisibleOutlined,
1313
EyeOutlined,
1414
CloseOutlined,
15+
ShrinkOutlined,
16+
ExpandAltOutlined,
1517
} from "@ant-design/icons";
1618
import type RcTree from "rc-tree";
1719
import { getJobs, startComputeMeshFileJob } from "admin/admin_rest_api";
@@ -323,6 +325,7 @@ type State = {
323325
[groupId: number]: { areSomeSegmentsVisible: boolean; areSomeSegmentsInvisible: boolean };
324326
};
325327
activeStatisticsModalGroupId: number | null;
328+
expandedGroupKeys: Key[];
326329
};
327330

328331
const formatMagWithLabel = (mag: Vector3, index: number) => {
@@ -402,6 +405,7 @@ class SegmentsView extends React.Component<Props, State> {
402405
groupToDelete: null,
403406
groupsSegmentsVisibilityStateMap: {},
404407
activeStatisticsModalGroupId: null,
408+
expandedGroupKeys: [],
405409
};
406410
tree: React.RefObject<RcTree>;
407411

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

427431
Store.dispatch(ensureSegmentIndexIsLoadedAction(this.props.visibleSegmentationLayer?.name));
432+
433+
const allGroups = this.getSubGroupsAsTreeNodes(MISSING_GROUP_ID);
434+
allGroups.push(this.getKeyForGroupId(MISSING_GROUP_ID));
435+
this.setState({ expandedGroupKeys: allGroups });
428436
}
429437

430438
componentDidUpdate(prevProps: Props) {
@@ -461,6 +469,38 @@ class SegmentsView extends React.Component<Props, State> {
461469
}
462470
}
463471

472+
onExpandTree = (expandedKeys: Key[]) => {
473+
this.setState({ expandedGroupKeys: expandedKeys });
474+
};
475+
476+
getKeyForGroupId = (groupId: number) => `group-${groupId}`;
477+
478+
getSubGroupsAsTreeNodes = (groupId: number) => {
479+
if (groupId !== MISSING_GROUP_ID) {
480+
return getGroupByIdWithSubgroups(this.props.segmentGroups, groupId)
481+
.filter((group) => group !== groupId)
482+
.map((group) => this.getKeyForGroupId(group));
483+
}
484+
const allSegmentGroups = this.props.segmentGroups.flatMap((group) =>
485+
getGroupByIdWithSubgroups(this.props.segmentGroups, group.groupId),
486+
);
487+
return allSegmentGroups.map((group) => this.getKeyForGroupId(group));
488+
};
489+
490+
expandGroups = (groupsToExpand: Key[]) => {
491+
this.setState({
492+
expandedGroupKeys: this.state.expandedGroupKeys?.concat(groupsToExpand),
493+
});
494+
};
495+
496+
collapseGroups = (groupsToCollapse: Key[]) => {
497+
this.setState({
498+
expandedGroupKeys: this.state.expandedGroupKeys?.filter(
499+
(key) => groupsToCollapse.indexOf(key.toString()) === -1,
500+
),
501+
});
502+
};
503+
464504
onSelectTreeItem = (
465505
keys: Key[],
466506
event: {
@@ -1405,7 +1445,7 @@ class SegmentsView extends React.Component<Props, State> {
14051445
(segmentId) => `segment-${segmentId}`,
14061446
);
14071447
if (this.props.selectedIds.group != null) {
1408-
return mappedIdsToKeys.concat(`group-${this.props.selectedIds.group}`);
1448+
return mappedIdsToKeys.concat(this.getKeyForGroupId(this.props.selectedIds.group));
14091449
}
14101450
return mappedIdsToKeys;
14111451
};
@@ -1686,6 +1726,8 @@ class SegmentsView extends React.Component<Props, State> {
16861726
icon: <DeleteOutlined />,
16871727
label: "Delete group",
16881728
},
1729+
this.getExpandSubgroupsItem(id),
1730+
this.getCollapseSubgroupsItem(id),
16891731
this.getMoveSegmentsHereMenuItem(id),
16901732
{
16911733
key: "groupAndMeshActionDivider",
@@ -1792,7 +1834,6 @@ class SegmentsView extends React.Component<Props, State> {
17921834
allowDrop={this.allowDrop}
17931835
onDrop={this.onDrop}
17941836
onSelect={this.onSelectTreeItem}
1795-
defaultExpandAll
17961837
className="segments-tree"
17971838
blockNode
17981839
// Passing an explicit height here, makes the tree virtualized
@@ -1817,6 +1858,8 @@ class SegmentsView extends React.Component<Props, State> {
18171858
overflow: "auto", // use hidden when not using virtualization
18181859
}}
18191860
ref={this.tree}
1861+
onExpand={this.onExpandTree}
1862+
expandedKeys={this.state.expandedGroupKeys}
18201863
/>
18211864
</div>
18221865
)}
@@ -1842,6 +1885,48 @@ class SegmentsView extends React.Component<Props, State> {
18421885
);
18431886
}
18441887

1888+
getExpandSubgroupsItem(groupId: number) {
1889+
const children = this.getSubGroupsAsTreeNodes(groupId);
1890+
const expandedKeySet = new Set(this.state.expandedGroupKeys);
1891+
const areAllChildrenExpanded = children.every((childNode) => expandedKeySet.has(childNode));
1892+
const isGroupItselfExpanded = expandedKeySet.has(this.getKeyForGroupId(groupId));
1893+
if (areAllChildrenExpanded && isGroupItselfExpanded) {
1894+
return null;
1895+
}
1896+
const groupsToExpand = children;
1897+
// It doesn't make sense to expand subgroups if the group itself is collapsed, so also expand the group.
1898+
if (!isGroupItselfExpanded) groupsToExpand.push(this.getKeyForGroupId(groupId));
1899+
return {
1900+
key: "expandAll",
1901+
onClick: () => {
1902+
this.expandGroups(groupsToExpand);
1903+
this.closeSegmentOrGroupDropdown();
1904+
},
1905+
icon: <ExpandAltOutlined />,
1906+
label: "Expand all subgroups",
1907+
};
1908+
}
1909+
1910+
getCollapseSubgroupsItem(groupId: number) {
1911+
const children = this.getSubGroupsAsTreeNodes(groupId);
1912+
const expandedKeySet = new Set(this.state.expandedGroupKeys);
1913+
const areAllChildrenCollapsed =
1914+
children.filter((childNode) => expandedKeySet.has(childNode)).length === 0;
1915+
const isGroupItselfCollapsed = !expandedKeySet.has(this.getKeyForGroupId(groupId));
1916+
if (areAllChildrenCollapsed || isGroupItselfCollapsed) {
1917+
return null;
1918+
}
1919+
return {
1920+
key: "collapseAll",
1921+
onClick: () => {
1922+
this.collapseGroups(children);
1923+
this.closeSegmentOrGroupDropdown();
1924+
},
1925+
icon: <ShrinkOutlined />,
1926+
label: "Collapse all subgroups",
1927+
};
1928+
}
1929+
18451930
createGroup(parentGroupId: number): void {
18461931
if (!this.props.visibleSegmentationLayer) {
18471932
return;

0 commit comments

Comments
 (0)