Skip to content

Commit

Permalink
Improve performance of Bounding Box Context Menus (#8059)
Browse files Browse the repository at this point in the history
* move bbox context menus to container

* close context menu after actions

* open context menu upon click anywhere on bb entry

* add comment

* fix that second scroll bar is rendered

* add changelog

* actually fix scroll bar in skeleton tab
  • Loading branch information
dieknolle3333 committed Sep 19, 2024
1 parent ae74fe3 commit d336b7c
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 15 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released

### Added
- It is now possible to focus a bounding box in the bounding box tab by clicking its edges in a viewport or via a newly added context menu entry. [#8054](https://github.com/scalableminds/webknossos/pull/8054)
### Added
- Added an assertion to the backend to ensure unique keys in the metadata info of datasets and folders. [#8068](https://github.com/scalableminds/webknossos/issues/8068)

### Changed
- Clicking on a bounding box within the bounding box tab centers it within the viewports and focusses it in the list. [#8049](https://github.com/scalableminds/webknossos/pull/8049)
- For self-hosted versions, the text in the data set upload view was updated to recommend switching to webknossos.org. [#7996](https://github.com/scalableminds/webknossos/pull/7996)
- Updated frontend package management to yarn version 4. [8061](https://github.com/scalableminds/webknossos/pull/8061)
- Updated React to version 18. Updated many peer dependencies inlcuding Redux, React-Router, antd, and FlexLayout. [#8048](https://github.com/scalableminds/webknossos/pull/8048)
- Improved the performance of context menus in the bounding box tab. [#8059](https://github.com/scalableminds/webknossos/pull/8059)

### Fixed
- The JS API v2 has been removed as it was deprecated by v3 in 2018. Please upgrade to v3 in case your scripts still use v2. [#8076](https://github.com/scalableminds/webknossos/pull/8076)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,8 @@ type UserBoundingBoxInputProps = {
isLockedByOwner: boolean;
isOwner: boolean;
visibleSegmentationLayer: APISegmentationLayer | null | undefined;
onOpenContextMenu: (menu: MenuProps, event: React.MouseEvent<HTMLDivElement>) => void;
onHideContextMenu?: () => void;
};
type State = {
isEditing: boolean;
Expand Down Expand Up @@ -487,8 +489,15 @@ class UserBoundingBoxInput extends React.PureComponent<UserBoundingBoxInputProps
const min: Vector3 = [value[0], value[1], value[2]];
const max: Vector3 = [value[0] + value[3], value[1] + value[4], value[2] + value[5]];
api.tracing.registerSegmentsForBoundingBox(min, max, name);
this.maybeCloseContextMenu();
}

maybeCloseContextMenu = () => {
if (this.props.onHideContextMenu) {
this.props.onHideContextMenu();
}
};

render() {
const { name } = this.state;
const {
Expand All @@ -501,6 +510,7 @@ class UserBoundingBoxInput extends React.PureComponent<UserBoundingBoxInputProps
disabled,
isLockedByOwner,
isOwner,
onOpenContextMenu,
} = this.props;
const upscaledColor = color.map((colorPart) => colorPart * 255) as any as Vector3;
const marginRightStyle = {
Expand Down Expand Up @@ -572,17 +582,14 @@ class UserBoundingBoxInput extends React.PureComponent<UserBoundingBoxInputProps
},
];

return (
<div onClick={(e) => e.stopPropagation()}>
<Dropdown menu={{ items }}>
<EllipsisOutlined style={marginLeftStyle} />
</Dropdown>
</div>
);
return { items };
};

return (
<div>
<div
onContextMenu={(evt) => onOpenContextMenu(getContextMenu(), evt)}
onClick={this.props.onHideContextMenu}
>
<Row
style={{
marginTop: 10,
Expand Down Expand Up @@ -622,7 +629,14 @@ class UserBoundingBoxInput extends React.PureComponent<UserBoundingBoxInputProps
</span>
</FastTooltip>
</Col>
<Col span={2}>{getContextMenu()}</Col>
<Col span={2}>
<div
onContextMenu={(evt) => onOpenContextMenu(getContextMenu(), evt)}
onClick={(evt) => onOpenContextMenu(getContextMenu(), evt)}
>
<EllipsisOutlined style={marginLeftStyle} />
</div>
</Col>
</Row>
<Row
style={{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Table, Tooltip, Typography } from "antd";
import { type MenuProps, Table, Tooltip, Typography } from "antd";
import { PlusSquareOutlined } from "@ant-design/icons";
import { useSelector, useDispatch } from "react-redux";
import React, { useEffect, useRef, useState } from "react";
import type React from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import _ from "lodash";
import UserBoundingBoxInput from "oxalis/view/components/setting_input_views";
import {
Expand All @@ -22,10 +23,13 @@ import * as Utils from "libs/utils";
import type { OxalisState, UserBoundingBox } from "oxalis/store";
import DownloadModalView from "../action-bar/download_modal_view";
import { APIJobType } from "types/api_flow_types";
import { ContextMenuContainer } from "./sidebar_context_menu";
import { getContextMenuPositionFromEvent } from "../context_menu";
import AutoSizer from "react-virtualized-auto-sizer";
import { setActiveUserBoundingBoxId } from "oxalis/model/actions/ui_actions";

const ADD_BBOX_BUTTON_HEIGHT = 32;
const CONTEXT_MENU_CLASS = "bbox-list-context-menu-overlay";

export default function BoundingBoxTab() {
const bboxTableRef: Parameters<typeof Table>[0]["ref"] = useRef(null);
Expand All @@ -40,6 +44,8 @@ export default function BoundingBoxTab() {
(state: OxalisState) => state.uiInformation.activeUserBoundingBoxId,
);
const { userBoundingBoxes } = getSomeTracing(tracing);
const [contextMenuPosition, setContextMenuPosition] = useState<[number, number] | null>(null);
const [menu, setMenu] = useState<MenuProps | null>(null);
const dispatch = useDispatch();

const setChangeBoundingBoxBounds = (id: number, boundingBox: BoundingBoxType) =>
Expand All @@ -53,7 +59,10 @@ export default function BoundingBoxTab() {

const setPosition = (position: Vector3) => dispatch(setPositionAction(position));

const deleteBoundingBox = (id: number) => dispatch(deleteUserBoundingBoxAction(id));
const deleteBoundingBox = (id: number) => {
dispatch(deleteUserBoundingBoxAction(id));
hideContextMenu();
};

const setBoundingBoxVisibility = (id: number, isVisible: boolean) =>
dispatch(
Expand All @@ -80,6 +89,11 @@ export default function BoundingBoxTab() {
setChangeBoundingBoxBounds(id, Utils.computeBoundingBoxFromArray(boundingBox));
}

function handleExportBoundingBox(bb: UserBoundingBox) {
_.partial(setSelectedBoundingBoxForExport, bb);
hideContextMenu();
}

function handleGoToBoundingBox(id: number) {
const boundingBoxEntry = userBoundingBoxes.find((bbox) => bbox.id === id);

Expand All @@ -94,6 +108,7 @@ export default function BoundingBoxTab() {
min[2] + (max[2] - min[2]) / 2,
];
setPosition(center);
hideContextMenu();
}

const isViewMode = useSelector(
Expand Down Expand Up @@ -132,14 +147,16 @@ export default function BoundingBoxTab() {
isVisible={bb.isVisible}
onBoundingChange={_.partial(handleBoundingBoxBoundingChange, bb.id)}
onDelete={_.partial(deleteBoundingBox, bb.id)}
onExport={isExportEnabled ? _.partial(setSelectedBoundingBoxForExport, bb) : () => {}}
onExport={isExportEnabled ? () => handleExportBoundingBox(bb) : () => {}}
onGoToBoundingBox={_.partial(handleGoToBoundingBox, bb.id)}
onVisibilityChange={_.partial(setBoundingBoxVisibility, bb.id)}
onNameChange={_.partial(setBoundingBoxName, bb.id)}
onColorChange={_.partial(setBoundingBoxColor, bb.id)}
disabled={!allowUpdate}
isLockedByOwner={isLockedByOwner}
isOwner={isOwner}
onOpenContextMenu={onOpenContextMenu}
onHideContextMenu={hideContextMenu}
/>
),
},
Expand All @@ -159,6 +176,30 @@ export default function BoundingBoxTab() {
</div>
) : null;

const onOpenContextMenu = (menu: MenuProps, event: React.MouseEvent<HTMLDivElement>) => {
event.preventDefault();
event.stopPropagation(); // Prevent that the bounding box gets activated when the context menu is opened.

const [x, y] = getContextMenuPositionFromEvent(event, CONTEXT_MENU_CLASS);
showContextMenuAt(x, y, menu);
};

const showContextMenuAt = useCallback((xPos: number, yPos: number, menu: MenuProps) => {
// On Windows the right click to open the context menu is also triggered for the overlay
// of the context menu. This causes the context menu to instantly close after opening.
// Therefore delay the state update to delay that the context menu is rendered.
// Thus the context overlay does not get the right click as an event and therefore does not close.
setTimeout(() => {
setContextMenuPosition([xPos, yPos]);
setMenu(menu);
}, 0);
}, []);

const hideContextMenu = useCallback(() => {
setContextMenuPosition(null);
setMenu(null);
}, []);

return (
<div
className="padded-tab-content"
Expand All @@ -167,6 +208,12 @@ export default function BoundingBoxTab() {
height: "100%",
}}
>
<ContextMenuContainer
hideContextMenu={hideContextMenu}
contextMenuPosition={contextMenuPosition}
menu={menu}
className={CONTEXT_MENU_CLASS}
/>
{/* Don't render a table in view mode. */}
{isViewMode ? null : userBoundingBoxes.length > 0 ? (
<AutoSizer defaultHeight={500}>
Expand Down Expand Up @@ -195,6 +242,7 @@ export default function BoundingBoxTab() {
// the height of the diff, the AutoSizer will always rerender the table and toggle an additional scrollbar.
onRow={(bb) => ({
onClick: () => {
hideContextMenu();
handleGoToBoundingBox(bb.id);
dispatch(setActiveUserBoundingBoxId(bb.id));
},
Expand Down
6 changes: 5 additions & 1 deletion frontend/stylesheets/trace_view/_tracing_view.less
Original file line number Diff line number Diff line change
Expand Up @@ -602,12 +602,16 @@ img.keyboard-mouse-icon:first-child {
z-index: 110;
}

.segment-list-context-menu-overlay {
.segment-list-context-menu-overlay,.bbox-list-context-menu-overlay {
// The parent has a padding of 10px on all sides which we need to subtract here.
width: calc(100% - 20px) !important;
height: calc(100% - 20px) !important;
}

.tree-list-context-menu-overlay{
width: calc(100% - 20px) !important;
height: calc(100% - var(--navbar-height) - var(--footer-height) - 30px) !important;
}

#main-tooltip {
max-width: 250px;
Expand Down

0 comments on commit d336b7c

Please sign in to comment.