Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React UI: Point deletion context menu #1292

Merged
merged 11 commits into from
Mar 24, 2020
31 changes: 29 additions & 2 deletions cvat-canvas/src/typescript/canvasView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,27 @@ export class CanvasViewImpl implements CanvasView, Listener {
e.preventDefault();
}

function contextmenuHandler(e: MouseEvent): void {
const pointID = Array.prototype.indexOf
.call(((e.target as HTMLElement).parentElement as HTMLElement).children, e.target);
if (self.activeElement.clientID !== null) {
const [state] = self.controller.objects
.filter((_state: any): boolean => (
_state.clientID === self.activeElement.clientID
));
self.canvas.dispatchEvent(new CustomEvent('point.contextmenu', {
bubbles: false,
cancelable: true,
detail: {
mouseEvent: e,
objectState: state,
pointID,
},
}));
}
e.preventDefault();
}

if (value) {
(shape as any).selectize(value, {
deepSelect: true,
Expand All @@ -478,6 +499,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
});

circle.on('dblclick', dblClickHandler);
circle.on('contextmenu', contextmenuHandler);
circle.addClass('cvat_canvas_selected_point');
});

Expand All @@ -487,6 +509,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
});

circle.off('dblclick', dblClickHandler);
circle.off('contextmenu', contextmenuHandler);
circle.removeClass('cvat_canvas_selected_point');
});

Expand Down Expand Up @@ -900,7 +923,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
this.activate(activeElement);
}

if (state.points
if (state.points.length !== drawnState.points.length || state.points
.some((p: number, id: number): boolean => p !== drawnState.points[id])
) {
const translatedPoints: number[] = translate(state.points);
Expand Down Expand Up @@ -1185,7 +1208,11 @@ export class CanvasViewImpl implements CanvasView, Listener {

let shapeSizeElement: ShapeSizeElement | null = null;
let resized = false;
(shape as any).resize().on('resizestart', (): void => {
(shape as any).resize().on('resizestart', (e: any): void => {
if (e.detail.event.detail.event.button === 2) {
e.preventDefault();
return;
}
this.mode = Mode.RESIZE;
if (state.shapeType === 'rectangle') {
shapeSizeElement = displayShapeSize(this.adoptedContent, this.adoptedText);
Expand Down
11 changes: 10 additions & 1 deletion cvat-ui/src/actions/annotation-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
Task,
FrameSpeed,
Rotation,
ContextMenuType,
Workspace,
} from 'reducers/interfaces';

Expand Down Expand Up @@ -365,13 +366,21 @@ ThunkAction<Promise<void>, {}, {}, AnyAction> {
};
}

export function updateCanvasContextMenu(visible: boolean, left: number, top: number): AnyAction {
export function updateCanvasContextMenu(
visible: boolean,
left: number,
top: number,
type?: ContextMenuType,
pointID?: number,
): AnyAction {
return {
type: AnnotationActionTypes.UPDATE_CANVAS_CONTEXT_MENU,
payload: {
visible,
left,
top,
type,
pointID,
},
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT

import React from 'react';
import ReactDOM from 'react-dom';

import {
Button,
} from 'antd';

interface Props {
activatedStateID: number | null;
visible: boolean;
left: number;
top: number;
onPointDelete(): void;
}

export default function CanvasPointContextMenu(props: Props): JSX.Element | null {
const {
onPointDelete,
activatedStateID,
visible,
left,
top,
} = props;

if (!visible || activatedStateID === null) {
return null;
}

return ReactDOM.createPortal(
<div className='cvat-canvas-point-context-menu' style={{ top, left }}>
<Button type='link' icon='delete' onClick={onPointDelete}>
Delete point
</Button>
</div>,
window.document.body,
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,26 @@

import React from 'react';
import { GlobalHotKeys, KeyMap } from 'react-hotkeys';
import Slider, { SliderValue } from 'antd/lib/slider';
import Layout from 'antd/lib/layout';
import Icon from 'antd/lib/icon';
import Tooltip from 'antd/lib/tooltip';

import { LogType } from 'cvat-logger';
import { Canvas } from 'cvat-canvas';
import getCore from 'cvat-core';
import {
Layout,
Slider,
Icon,
Tooltip,
} from 'antd';

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's import exact components in future code
Not from antd wrapper
It common recommendation to reduce bundle size

import { SliderValue } from 'antd/lib/slider';
import {
ColorBy,
GridColor,
ObjectType,
ContextMenuType,
Workspace,
ShapeType,
} from 'reducers/interfaces';
import { LogType } from 'cvat-logger';
import { Canvas } from 'cvat-canvas';
import getCore from 'cvat-core';

const cvat = getCore();

Expand Down Expand Up @@ -51,6 +57,8 @@ interface Props {
contrastLevel: number;
saturationLevel: number;
resetZoom: boolean;
contextVisible: boolean;
contextType: ContextMenuType;
aamZoomMargin: number;
workspace: Workspace;
onSetupCanvas: () => void;
Expand All @@ -69,7 +77,8 @@ interface Props {
onSplitAnnotations(sessionInstance: any, frame: number, state: any): void;
onActivateObject(activatedStateID: number | null): void;
onSelectObjects(selectedStatesID: number[]): void;
onUpdateContextMenu(visible: boolean, left: number, top: number): void;
onUpdateContextMenu(visible: boolean, left: number, top: number, type: ContextMenuType,
pointID?: number): void;
onAddZLayer(): void;
onSwitchZLayer(cur: number): void;
onChangeBrightnessLevel(level: number): void;
Expand Down Expand Up @@ -223,7 +232,9 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
canvasInstance.html().removeEventListener('canvas.drawn', this.onCanvasShapeDrawn);
canvasInstance.html().removeEventListener('canvas.merged', this.onCanvasObjectsMerged);
canvasInstance.html().removeEventListener('canvas.groupped', this.onCanvasObjectsGroupped);
canvasInstance.html().addEventListener('canvas.splitted', this.onCanvasTrackSplitted);
canvasInstance.html().removeEventListener('canvas.splitted', this.onCanvasTrackSplitted);

canvasInstance.html().removeEventListener('point.contextmenu', this.onCanvasPointContextMenu);

window.removeEventListener('resize', this.fitCanvas);
}
Expand Down Expand Up @@ -327,8 +338,16 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
};

private onCanvasContextMenu = (e: MouseEvent): void => {
const { activatedStateID, onUpdateContextMenu } = this.props;
onUpdateContextMenu(activatedStateID !== null, e.clientX, e.clientY);
const {
activatedStateID,
onUpdateContextMenu,
contextType,
} = this.props;

if (contextType !== ContextMenuType.CANVAS_SHAPE_POINT) {
onUpdateContextMenu(activatedStateID !== null, e.clientX, e.clientY,
ContextMenuType.CANVAS_SHAPE);
}
};

private onCanvasShapeDragged = (e: any): void => {
Expand Down Expand Up @@ -476,6 +495,20 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
}
};

private onCanvasPointContextMenu = (e: any): void => {
const {
activatedStateID,
onUpdateContextMenu,
annotations,
} = this.props;

const [state] = annotations.filter((el: any) => (el.clientID === activatedStateID));
if (state.shapeType !== ShapeType.RECTANGLE) {
onUpdateContextMenu(activatedStateID !== null, e.detail.mouseEvent.clientX,
e.detail.mouseEvent.clientY, ContextMenuType.CANVAS_SHAPE_POINT, e.detail.pointID);
}
};

private activateOnCanvas(): void {
const {
activatedStateID,
Expand Down Expand Up @@ -619,6 +652,8 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
canvasInstance.html().addEventListener('canvas.merged', this.onCanvasObjectsMerged);
canvasInstance.html().addEventListener('canvas.groupped', this.onCanvasObjectsGroupped);
canvasInstance.html().addEventListener('canvas.splitted', this.onCanvasTrackSplitted);

canvasInstance.html().addEventListener('point.contextmenu', this.onCanvasPointContextMenu);
}

public render(): JSX.Element {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import ControlsSideBarContainer from 'containers/annotation-page/standard-worksp
import ObjectSideBarContainer from 'containers/annotation-page/standard-workspace/objects-side-bar/objects-side-bar';
import PropagateConfirmContainer from 'containers/annotation-page/standard-workspace/propagate-confirm';
import CanvasContextMenuContainer from 'containers/annotation-page/standard-workspace/canvas-context-menu';
import CanvasPointContextMenuContainer from 'containers/annotation-page/standard-workspace/canvas-point-context-menu';

export default function StandardWorkspaceComponent(): JSX.Element {
return (
Expand All @@ -23,6 +24,7 @@ export default function StandardWorkspaceComponent(): JSX.Element {
<ObjectSideBarContainer />
<PropagateConfirmContainer />
<CanvasContextMenuContainer />
<CanvasPointContextMenuContainer />
</Layout>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,77 @@
margin: 0px 5px;
}
}

.cvat-canvas-context-menu {
opacity: 0.6;
position: fixed;
width: 300px;
z-index: 10;
max-height: 50%;
overflow-y: auto;

&:hover {
opacity: 1;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why have you duplicated style from src/components/annotation-page/styles.scss?


.cvat-canvas-point-context-menu {
opacity: 0.6;
position: fixed;
width: 135px;
z-index: 10;
max-height: 50%;
overflow-y: auto;
background-color: #ffffff;
border-radius: 4px;

&:hover {
opacity: 1;
}
}

.cvat-canvas-z-axis-wrapper {
position: absolute;
background: $background-color-2;
bottom: 10px;
right: 10px;
height: 150px;
z-index: 100;
border-radius: 6px;
opacity: 0.5;
border: 1px solid $border-color-3;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 3px;

&:hover {
opacity: 1;
}

> .ant-slider {
height: 75%;
margin: 5px 3px;

> .ant-slider-rail {
background-color: #979797;
}

> .ant-slider-handle {
transform: none !important;
}
}

> i {
opacity: 0.7;
color: $objects-bar-icons-color;

&:hover {
opacity: 1;
}

&:active {
opacity: 0.7;
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more duplicate

Loading