diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 5f497a49eb7..57c35efee0f 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -11,6 +11,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released [Commits](https://github.com/scalableminds/webknossos/compare/23.05.0...HEAD) ### Added +- In addition to drag and drop, the selected tree(s) in the Skeleton tab can also be moved into another group by right-clicking the target group and selecting "Move selected tree(s) here". [#7005](https://github.com/scalableminds/webknossos/pull/7005) ### Changed diff --git a/frontend/javascripts/oxalis/view/right-border-tabs/tree_hierarchy_view.tsx b/frontend/javascripts/oxalis/view/right-border-tabs/tree_hierarchy_view.tsx index 591a7110b8d..a7fa8c0adeb 100644 --- a/frontend/javascripts/oxalis/view/right-border-tabs/tree_hierarchy_view.tsx +++ b/frontend/javascripts/oxalis/view/right-border-tabs/tree_hierarchy_view.tsx @@ -1,6 +1,12 @@ import { AutoSizer } from "react-virtualized"; import { Checkbox, Dropdown, MenuProps, Modal, notification } from "antd"; -import { DeleteOutlined, PlusOutlined, ShrinkOutlined } from "@ant-design/icons"; +import { + DeleteOutlined, + PlusOutlined, + ShrinkOutlined, + ExpandAltOutlined, + ArrowRightOutlined, +} from "@ant-design/icons"; import { connect } from "react-redux"; import { batchActions } from "redux-batched-actions"; import React from "react"; @@ -252,11 +258,32 @@ class TreeHierarchyView extends React.PureComponent { })); }; + onMoveWithContextAction = (nextParentNode: TreeNode) => { + const activeComponent = this.getLabelForActiveItems(); + let allTreesToMove; + if (activeComponent === "tree") { + allTreesToMove = [this.props.activeTreeId]; + } else if (activeComponent === "trees") { + allTreesToMove = this.props.selectedTrees; + } + if (allTreesToMove) { + const moveActions = allTreesToMove.map((treeId) => + setTreeGroupAction( + nextParentNode.id === MISSING_GROUP_ID ? null : nextParentNode.id, + // @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'number' is not assignable to par... Remove this comment to see the full error message + parseInt(treeId, 10), + ), + ); + this.props.onBatchActions(moveActions, "SET_TREE_GROUP"); + } else if (activeComponent === "group") { + // TODO move group after #6966 (segment groups) is merged + } + }; + onMoveNode = ( params: NodeData & FullTree & OnMovePreviousAndNextLocation, ) => { const { nextParentNode, node, treeData } = params; - if (node.type === TYPE_TREE && nextParentNode) { const allTreesToMove = [...this.props.selectedTrees, node.id]; // Sets group of all selected + dragged trees (and the moved tree) to the new parent group @@ -388,15 +415,31 @@ class TreeHierarchyView extends React.PureComponent { ); const isEditingDisabled = !this.props.allowUpdate; const hasSubgroup = anySatisfyDeep(node.children, (child) => child.type === TYPE_GROUP); + const labelForActiveItems = this.getLabelForActiveItems(); const createMenu: MenuProps = { items: [ { key: "create", - onClick: () => this.createGroup(id), + onClick: () => { + this.createGroup(id); + this.handleGroupDropdownMenuVisibility(id, false); + }, disabled: isEditingDisabled, icon: , label: "Create new group", }, + labelForActiveItems === "tree" || labelForActiveItems === "trees" + ? { + key: "moveHere", + onClick: () => { + this.onMoveWithContextAction(node); + this.handleGroupDropdownMenuVisibility(id, false); + }, + disabled: isEditingDisabled, + icon: , + label: `Move active ${labelForActiveItems} here`, + } + : null, { key: "delete", disabled: isEditingDisabled, @@ -408,7 +451,10 @@ class TreeHierarchyView extends React.PureComponent { ? { key: "collapseSubgroups", disabled: !hasExpandedSubgroup, - onClick: () => this.setExpansionOfAllSubgroupsTo(id, false), + onClick: () => { + this.setExpansionOfAllSubgroupsTo(id, false); + this.handleGroupDropdownMenuVisibility(id, false); + }, icon: , label: "Collapse all subgroups", } @@ -417,8 +463,11 @@ class TreeHierarchyView extends React.PureComponent { ? { key: "expandSubgroups", disabled: !hasCollapsedSubgroup, - onClick: () => this.setExpansionOfAllSubgroupsTo(id, true), - icon: , + onClick: () => { + this.setExpansionOfAllSubgroupsTo(id, true); + this.handleGroupDropdownMenuVisibility(id, false); + }, + icon: , label: "Expand all subgroups", } : null, @@ -427,6 +476,7 @@ class TreeHierarchyView extends React.PureComponent { onClick: () => { this.props.onSetActiveGroup(id); this.props.onToggleHideInactiveTrees(); + this.handleGroupDropdownMenuVisibility(id, false); }, icon: , label: "Hide/Show all other trees", @@ -553,7 +603,10 @@ class TreeHierarchyView extends React.PureComponent { }, { key: "measureSkeleton", - onClick: () => this.handleMeasureSkeletonLength(tree.treeId, tree.name), + onClick: () => { + this.handleMeasureSkeletonLength(tree.treeId, tree.name); + this.handleTreeDropdownMenuVisibility(tree.treeId, false); + }, title: "Measure Skeleton Length", icon: , label: "Measure Skeleton Length", @@ -652,6 +705,18 @@ class TreeHierarchyView extends React.PureComponent { return node.type === TYPE_GROUP; } + getLabelForActiveItems(): "trees" | "tree" | "group" | null { + // Only one type of component can be selected. It is not possible to select multiple groups. + if (this.props.selectedTrees.length > 0) { + return "trees"; + } else if (this.props.activeTreeId != null) { + return "tree"; + } else if (this.props.activeGroupId != null) { + return "group"; + } + return null; + } + render() { const { activeTreeId, activeGroupId } = this.props; return (