From f416caf872d809a1a3ed26d6e868b6d0fa316c01 Mon Sep 17 00:00:00 2001 From: Dmitry Kalinin Date: Sat, 3 Apr 2021 02:23:22 +0300 Subject: [PATCH 01/10] temp commit --- .../objects-side-bar/label-item.tsx | 22 +++++++++++++---- .../objects-side-bar/styles.scss | 8 +++++++ .../objects-side-bar/label-item.tsx | 24 +++++++++++++++---- cvat-ui/src/reducers/annotation-reducer.ts | 7 ++++++ cvat-ui/src/reducers/interfaces.ts | 3 +++ cvat-ui/src/reducers/shortcuts-reducer.ts | 9 +++++++ 6 files changed, 63 insertions(+), 10 deletions(-) diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx index fc038b391663..9cc37de218d2 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2020-2021 Intel Corporation // // SPDX-License-Identifier: MIT @@ -13,9 +13,13 @@ import { interface Props { labelName: string; labelColor: string; + labelId: number; visible: boolean; statesHidden: boolean; statesLocked: boolean; + label2NumberMap: { + [key: number]: number; + }; hideStates(): void; showStates(): void; lockStates(): void; @@ -26,9 +30,11 @@ function LabelItemComponent(props: Props): JSX.Element { const { labelName, labelColor, + labelId, visible, statesHidden, statesLocked, + label2NumberMap, hideStates, showStates, lockStates, @@ -46,23 +52,29 @@ function LabelItemComponent(props: Props): JSX.Element { }, }; + const labelNumberPair = Object.entries(label2NumberMap).filter((pair) => pair[1] === labelId); + return ( - + - + {labelName} + + + {statesLocked ? ( diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss index 8b7a7425f4d8..bd90c8f9e2e3 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss @@ -309,6 +309,10 @@ &:hover { @extend .cvat-objects-sidebar-label-active-item; } + + .ant-btn-dashed { + border-color: $objects-bar-icons-color; + } } .cvat-label-item-color-button { @@ -361,3 +365,7 @@ margin-right: $grid-unit-size; } } + +.cvat-objects-sidebar-label-item-disabled { + opacity: 0.4; +} diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx index 8e89e244a019..e959578c6b49 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2020-2021 Intel Corporation // // SPDX-License-Identifier: MIT @@ -18,9 +18,13 @@ interface StateToProps { label: any; labelName: string; labelColor: string; + labelId: number; objectStates: any[]; jobInstance: any; frameNumber: any; + label2NumberMap: { + [key: number]: number; + }; } interface DispatchToProps { @@ -35,6 +39,7 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps { player: { frame: { number: frameNumber }, }, + label2NumberMap, }, } = state; @@ -44,9 +49,11 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps { label, labelColor: label.color, labelName: label.name, + labelId: label.id, objectStates, jobInstance, frameNumber, + label2NumberMap, }; } @@ -127,8 +134,10 @@ class LabelItemContainer extends React.PureComponent { private switchHidden(value: boolean): void { const { updateAnnotations } = this.props; + const { ownObjectStates, visible } = this.state; + + if (!visible) return; - const { ownObjectStates } = this.state; for (const state of ownObjectStates) { state.hidden = value; } @@ -138,8 +147,10 @@ class LabelItemContainer extends React.PureComponent { private switchLock(value: boolean): void { const { updateAnnotations } = this.props; + const { ownObjectStates, visible } = this.state; + + if (!visible) return; - const { ownObjectStates } = this.state; for (const state of ownObjectStates) { state.lock = value; } @@ -148,17 +159,20 @@ class LabelItemContainer extends React.PureComponent { } public render(): JSX.Element { + const { + labelName, labelColor, labelId, label2NumberMap, + } = this.props; const { visible, statesHidden, statesLocked } = this.state; - const { labelName, labelColor } = this.props; - return ( { if (job.task.dimension === DimensionType.DIM_3D) { workspaceSelected = Workspace.STANDARD3D; } + + const label2NumberMap: any = {}; + job.task.labels.slice(0, 10).forEach((label: any, idx: number) => { + label2NumberMap[idx] = label.id; + }); return { ...state, job: { @@ -189,6 +195,7 @@ export default (state = defaultState, action: AnyAction): AnnotationState => { instance: job.task.dimension === DimensionType.DIM_2D ? new Canvas() : new Canvas3d(), }, colors, + label2NumberMap, workspace: isReview ? Workspace.REVIEW_WORKSPACE : workspaceSelected, }; } diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts index 5c3f48c63023..70615eeeada3 100644 --- a/cvat-ui/src/reducers/interfaces.ts +++ b/cvat-ui/src/reducers/interfaces.ts @@ -454,6 +454,9 @@ export interface AnnotationState { visible: boolean; data: any; }; + label2NumberMap: { + [lid: number]: number; + }; colors: any[]; filtersPanelVisible: boolean; requestReviewDialogVisible: boolean; diff --git a/cvat-ui/src/reducers/shortcuts-reducer.ts b/cvat-ui/src/reducers/shortcuts-reducer.ts index 98baa9aa4419..7751a80a6d50 100644 --- a/cvat-ui/src/reducers/shortcuts-reducer.ts +++ b/cvat-ui/src/reducers/shortcuts-reducer.ts @@ -293,6 +293,15 @@ const defaultKeyMap = ({ }, } as any) as KeyMap; +for (let i = 0; i < 10; i++) { + defaultKeyMap[`SWITCH_LABEL_${i}`] = { + name: `Switch label ${i}`, + description: `Switch label to label mapped to key ${i}`, + sequences: [`${i}`], + action: 'keydown', + }; +} + const defaultState: ShortcutsState = { visibleShortcutsHelp: false, keyMap: defaultKeyMap, From 6f0e2bd1751eb8ce3bc2e3b963f4b123205ed120 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 6 Apr 2021 15:32:56 +0300 Subject: [PATCH 02/10] Added ability to change default label and object label by using Ctrl+{number} --- cvat-ui/src/actions/annotation-actions.ts | 32 +++++----- .../objects-side-bar/label-item.tsx | 16 +---- .../objects-side-bar/labels-list.tsx | 61 ++++++++++++++++--- .../objects-side-bar/objects-side-bar.tsx | 8 +-- .../objects-side-bar/styles.scss | 2 + .../tag-annotation-sidebar.tsx | 2 +- .../controls-side-bar/draw-shape-popover.tsx | 10 ++- .../controls-side-bar/setup-tag-popover.tsx | 4 +- .../objects-side-bar/label-item.tsx | 48 +++++++-------- .../objects-side-bar/labels-list.tsx | 29 --------- cvat-ui/src/reducers/annotation-reducer.ts | 23 +++---- cvat-ui/src/reducers/interfaces.ts | 4 +- cvat-ui/src/reducers/shortcuts-reducer.ts | 17 +++--- cvat-ui/src/utils/mousetrap-react.tsx | 2 +- 14 files changed, 126 insertions(+), 132 deletions(-) delete mode 100644 cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx diff --git a/cvat-ui/src/actions/annotation-actions.ts b/cvat-ui/src/actions/annotation-actions.ts index 8e5fd184df86..c2868a5df522 100644 --- a/cvat-ui/src/actions/annotation-actions.ts +++ b/cvat-ui/src/actions/annotation-actions.ts @@ -1130,35 +1130,31 @@ export function saveAnnotationsAsync(sessionInstance: any, afterSave?: () => voi } // used to reproduce the latest drawing (in case of tags just creating) by using N -export function rememberObject( - objectType: ObjectType, - labelID: number, - shapeType?: ShapeType, - points?: number, - rectDrawingMethod?: RectDrawingMethod, -): AnyAction { - let activeControl = ActiveControl.CURSOR; - if (shapeType === ShapeType.RECTANGLE) { +export function rememberObject(createParams: { + activeObjectType?: ObjectType; + activeLabelID?: number; + activeShapeType?: ShapeType; + activeNumOfPoints?: number; + activeRectDrawingMethod?: RectDrawingMethod; +}): AnyAction { + let activeControl = createParams.activeObjectType ? ActiveControl.CURSOR : null; + if (createParams.activeShapeType === ShapeType.RECTANGLE) { activeControl = ActiveControl.DRAW_RECTANGLE; - } else if (shapeType === ShapeType.POLYGON) { + } else if (createParams.activeShapeType === ShapeType.POLYGON) { activeControl = ActiveControl.DRAW_POLYGON; - } else if (shapeType === ShapeType.POLYLINE) { + } else if (createParams.activeShapeType === ShapeType.POLYLINE) { activeControl = ActiveControl.DRAW_POLYLINE; - } else if (shapeType === ShapeType.POINTS) { + } else if (createParams.activeShapeType === ShapeType.POINTS) { activeControl = ActiveControl.DRAW_POINTS; - } else if (shapeType === ShapeType.CUBOID) { + } else if (createParams.activeShapeType === ShapeType.CUBOID) { activeControl = ActiveControl.DRAW_CUBOID; } return { type: AnnotationActionTypes.REMEMBER_CREATED_OBJECT, payload: { - shapeType, - labelID, - objectType, - points, + createParams, activeControl, - rectDrawingMethod, }, }; } diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx index 9cc37de218d2..912d28593ee1 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx @@ -17,9 +17,6 @@ interface Props { visible: boolean; statesHidden: boolean; statesLocked: boolean; - label2NumberMap: { - [key: number]: number; - }; hideStates(): void; showStates(): void; lockStates(): void; @@ -30,11 +27,9 @@ function LabelItemComponent(props: Props): JSX.Element { const { labelName, labelColor, - labelId, visible, statesHidden, statesLocked, - label2NumberMap, hideStates, showStates, lockStates, @@ -52,29 +47,22 @@ function LabelItemComponent(props: Props): JSX.Element { }, }; - const labelNumberPair = Object.entries(label2NumberMap).filter((pair) => pair[1] === labelId); - return ( - + - + {labelName} - - - {statesLocked ? ( diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx index 827ff3e7dd0d..33a39b9d6812 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx @@ -1,21 +1,64 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2020-2021 Intel Corporation // // SPDX-License-Identifier: MIT -import React from 'react'; +import React, { useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import message from 'antd/lib/message'; +import { CombinedState } from 'reducers/interfaces'; +import { rememberObject, updateAnnotationsAsync } from 'actions/annotation-actions'; import LabelItemContainer from 'containers/annotation-page/standard-workspace/objects-side-bar/label-item'; +import GlobalHotKeys from 'utils/mousetrap-react'; -interface Props { - labelIDs: number[]; - listHeight: number; -} +function LabelsListComponent(): JSX.Element { + const dispatch = useDispatch(); + const { + annotation: { + job: { labels }, + tabContentHeight: listHeight, + annotations: { activatedStateID, states }, + }, + shortcuts: { keyMap }, + } = useSelector((state: CombinedState) => state); + + const labelIDs = labels.map((label: any): number => label.id); + + const [keyMapping] = useState>( + Object.fromEntries( + labelIDs.slice(0, 10).map((labelID: number, idx: number) => [idx + 1 > 9 ? 0 : idx + 1, labelID]), + ), + ); + + const subKeyMap = { + SWITCH_LABEL: keyMap.SWITCH_LABEL, + }; -export default function LabelsListComponent(props: Props): JSX.Element { - const { listHeight, labelIDs } = props; + const handlers = { + SWITCH_LABEL: (event: KeyboardEvent | undefined, shortcut: string) => { + if (event) event.preventDefault(); + const key = +shortcut.split('+')[1]; + const labelID = keyMapping[key]; + const label = labels.filter((_label: any) => _label.id === labelID)[0]; + if (Number.isInteger(labelID) && label) { + if (Number.isInteger(activatedStateID)) { + const activatedState = states.filter((state: any) => state.clientID === activatedStateID)[0]; + if (activatedState) { + activatedState.label = label; + dispatch(updateAnnotationsAsync([activatedState])); + } + } else { + dispatch(rememberObject({ activeLabelID: labelID })); + message.destroy(); + message.success(`Default label was changed to "${label.name}"`); + } + } + }, + }; return (
+ {labelIDs.map( (labelID: number): JSX.Element => ( @@ -24,3 +67,5 @@ export default function LabelsListComponent(props: Props): JSX.Element {
); } + +export default React.memo(LabelsListComponent); diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/objects-side-bar.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/objects-side-bar.tsx index c5288a9b7132..6d15c3ce8bf7 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/objects-side-bar.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/objects-side-bar.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2020-2021 Intel Corporation // // SPDX-License-Identifier: MIT @@ -13,7 +13,7 @@ import Layout from 'antd/lib/layout'; import { Canvas } from 'cvat-canvas-wrapper'; import { CombinedState } from 'reducers/interfaces'; -import LabelsListContainer from 'containers/annotation-page/standard-workspace/objects-side-bar/labels-list'; +import LabelsList from 'components/annotation-page/standard-workspace/objects-side-bar/labels-list'; import { collapseSidebar as collapseSidebarAction, updateTabContentHeight as updateTabContentHeightAction, @@ -123,8 +123,8 @@ function ObjectsSideBar(props: StateToProps & DispatchToProps & OwnProps): JSX.E Objects} key='objects'> {objectsList} - Labels} key='labels'> - + Labels} key='labels'> + Issues} key='issues'> diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss index bd90c8f9e2e3..76ec27b50bf7 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss @@ -294,6 +294,7 @@ height: 2.5em; border-bottom: 1px solid $border-color-1; padding: 5px; + display: flex; span { @extend .cvat-object-sidebar-icon; @@ -368,4 +369,5 @@ .cvat-objects-sidebar-label-item-disabled { opacity: 0.4; + pointer-events: none; } diff --git a/cvat-ui/src/components/annotation-page/tag-annotation-workspace/tag-annotation-sidebar/tag-annotation-sidebar.tsx b/cvat-ui/src/components/annotation-page/tag-annotation-workspace/tag-annotation-sidebar/tag-annotation-sidebar.tsx index 7b5e44563d44..2292c48ee42d 100644 --- a/cvat-ui/src/components/annotation-page/tag-annotation-workspace/tag-annotation-sidebar/tag-annotation-sidebar.tsx +++ b/cvat-ui/src/components/annotation-page/tag-annotation-workspace/tag-annotation-sidebar/tag-annotation-sidebar.tsx @@ -82,7 +82,7 @@ function mapDispatchToProps(dispatch: ThunkDispatch): dispatch(removeObjectAsync(jobInstance, objectState, true)); }, onRememberObject(labelID: number): void { - dispatch(rememberObject(ObjectType.TAG, labelID)); + dispatch(rememberObject({ activeObjectType: ObjectType.TAG, activeLabelID: labelID })); }, }; } diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover.tsx index f88c752df838..9cd2669cd065 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover.tsx @@ -41,7 +41,15 @@ function mapDispatchToProps(dispatch: any): DispatchToProps { points?: number, rectDrawingMethod?: RectDrawingMethod, ): void { - dispatch(rememberObject(objectType, labelID, shapeType, points, rectDrawingMethod)); + dispatch( + rememberObject({ + activeObjectType: objectType, + activeShapeType: shapeType, + activeLabelID: labelID, + activeNumOfPoints: points, + activeRectDrawingMethod: rectDrawingMethod, + }), + ); }, }; } diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/setup-tag-popover.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/setup-tag-popover.tsx index 5c0c5b8b7300..7004505f87cc 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/setup-tag-popover.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/setup-tag-popover.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2020-2021 Intel Corporation // // SPDX-License-Identifier: MIT @@ -32,7 +32,7 @@ function mapDispatchToProps(dispatch: any): DispatchToProps { dispatch(createAnnotationsAsync(sessionInstance, frame, states)); }, onRememberObject(labelID: number): void { - dispatch(rememberObject(ObjectType.TAG, labelID)); + dispatch(rememberObject({ activeObjectType: ObjectType.TAG, activeLabelID: labelID })); }, }; } diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx index e959578c6b49..89754a7d3287 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { connect } from 'react-redux'; -import { updateAnnotationsAsync } from 'actions/annotation-actions'; +import { updateAnnotationsAsync, rememberObject as rememberObjectAction } from 'actions/annotation-actions'; import LabelItemComponent from 'components/annotation-page/standard-workspace/objects-side-bar/label-item'; import { CombinedState, ObjectType } from 'reducers/interfaces'; @@ -21,25 +21,23 @@ interface StateToProps { labelId: number; objectStates: any[]; jobInstance: any; - frameNumber: any; - label2NumberMap: { - [key: number]: number; - }; + frameNumber: number; + activatedStateID: number | null; } interface DispatchToProps { updateAnnotations(states: any[]): void; + rememberObject(labelID: number): void; } function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps { const { annotation: { - annotations: { states: objectStates }, + annotations: { states: objectStates, activatedStateID }, job: { labels, instance: jobInstance }, player: { frame: { number: frameNumber }, }, - label2NumberMap, }, } = state; @@ -53,7 +51,7 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps { objectStates, jobInstance, frameNumber, - label2NumberMap, + activatedStateID, }; } @@ -62,6 +60,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps { updateAnnotations(states: any[]): void { dispatch(updateAnnotationsAsync(states)); }, + rememberObject(labelID: number): void { + dispatch(rememberObjectAction({ activeLabelID: labelID })); + }, }; } @@ -69,6 +70,7 @@ type Props = StateToProps & DispatchToProps & OwnProps; interface State { objectStates: any[]; ownObjectStates: any[]; + switchLabelMapping: Record; // key, labelID visible: boolean; statesHidden: boolean; statesLocked: boolean; @@ -80,6 +82,7 @@ class LabelItemContainer extends React.PureComponent { this.state = { objectStates: [], ownObjectStates: [], + switchLabelMapping: {}, visible: true, statesHidden: false, statesLocked: false, @@ -134,34 +137,28 @@ class LabelItemContainer extends React.PureComponent { private switchHidden(value: boolean): void { const { updateAnnotations } = this.props; - const { ownObjectStates, visible } = this.state; - - if (!visible) return; + const { ownObjectStates } = this.state; - for (const state of ownObjectStates) { - state.hidden = value; + if (ownObjectStates.length) { + // false alarm + // eslint-disable-next-line + updateAnnotations(ownObjectStates.map((state: any) => ((state.hidden = value), state))); } - - updateAnnotations(ownObjectStates); } private switchLock(value: boolean): void { const { updateAnnotations } = this.props; - const { ownObjectStates, visible } = this.state; - - if (!visible) return; + const { ownObjectStates } = this.state; - for (const state of ownObjectStates) { - state.lock = value; + if (ownObjectStates.length) { + // false alarm + // eslint-disable-next-line + updateAnnotations(ownObjectStates.map((state: any) => ((state.lock = value), state))); } - - updateAnnotations(ownObjectStates); } public render(): JSX.Element { - const { - labelName, labelColor, labelId, label2NumberMap, - } = this.props; + const { labelName, labelColor, labelId } = this.props; const { visible, statesHidden, statesLocked } = this.state; return ( @@ -172,7 +169,6 @@ class LabelItemContainer extends React.PureComponent { visible={visible} statesHidden={statesHidden} statesLocked={statesLocked} - label2NumberMap={label2NumberMap} hideStates={this.hideStates} showStates={this.showStates} lockStates={this.lockStates} diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx deleted file mode 100644 index 16cd97678ea1..000000000000 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2020 Intel Corporation -// -// SPDX-License-Identifier: MIT - -import { connect } from 'react-redux'; - -import LabelsListComponent from 'components/annotation-page/standard-workspace/objects-side-bar/labels-list'; -import { CombinedState } from 'reducers/interfaces'; - -interface StateToProps { - labelIDs: number[]; - listHeight: number; -} - -function mapStateToProps(state: CombinedState): StateToProps { - const { - annotation: { - job: { labels }, - tabContentHeight: listHeight, - }, - } = state; - - return { - labelIDs: labels.map((label: any): number => label.id), - listHeight, - }; -} - -export default connect(mapStateToProps)(LabelsListComponent); diff --git a/cvat-ui/src/reducers/annotation-reducer.ts b/cvat-ui/src/reducers/annotation-reducer.ts index e0f3fe5f74f3..270265b02391 100644 --- a/cvat-ui/src/reducers/annotation-reducer.ts +++ b/cvat-ui/src/reducers/annotation-reducer.ts @@ -101,7 +101,6 @@ const defaultState: AnnotationState = { collecting: false, data: null, }, - label2NumberMap: {}, aiToolsRef: React.createRef(), colors: [], sidebarCollapsed: false, @@ -117,6 +116,10 @@ const defaultState: AnnotationState = { projectScore: 0, fetching: false, annotatedFrames: [], + timeRemaining: 0, + progress: 0, + annotationAmount: 0, + mediaAmount: 0, }, workspace: Workspace.STANDARD, }; @@ -156,10 +159,6 @@ export default (state = defaultState, action: AnyAction): AnnotationState => { workspaceSelected = Workspace.STANDARD3D; } - const label2NumberMap: any = {}; - job.task.labels.slice(0, 10).forEach((label: any, idx: number) => { - label2NumberMap[idx] = label.id; - }); return { ...state, job: { @@ -206,7 +205,6 @@ export default (state = defaultState, action: AnyAction): AnnotationState => { instance: job.task.dimension === DimensionType.DIM_2D ? new Canvas() : new Canvas3d(), }, colors, - label2NumberMap, workspace: isReview ? Workspace.REVIEW_WORKSPACE : workspaceSelected, }; } @@ -463,9 +461,7 @@ export default (state = defaultState, action: AnyAction): AnnotationState => { }; } case AnnotationActionTypes.REMEMBER_CREATED_OBJECT: { - const { - shapeType, labelID, objectType, points, activeControl, rectDrawingMethod, - } = action.payload; + const { createParams, activeControl } = action.payload; return { ...state, @@ -475,15 +471,12 @@ export default (state = defaultState, action: AnyAction): AnnotationState => { }, canvas: { ...state.canvas, - activeControl, + activeControl: activeControl === null ? state.canvas.activeControl : activeControl, }, drawing: { + ...state.drawing, + ...createParams, activeInteractor: undefined, - activeLabelID: labelID, - activeNumOfPoints: points, - activeObjectType: objectType, - activeShapeType: shapeType, - activeRectDrawingMethod: rectDrawingMethod, }, }; } diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts index dec9f89f2dea..4ddeb301135c 100644 --- a/cvat-ui/src/reducers/interfaces.ts +++ b/cvat-ui/src/reducers/interfaces.ts @@ -381,6 +381,7 @@ export interface PredictorState { fetching: boolean; annotationAmount: number; mediaAmount: number; + annotatedFrames: number[]; } export interface AnnotationState { @@ -471,9 +472,6 @@ export interface AnnotationState { visible: boolean; data: any; }; - label2NumberMap: { - [lid: number]: number; - }; colors: any[]; filtersPanelVisible: boolean; requestReviewDialogVisible: boolean; diff --git a/cvat-ui/src/reducers/shortcuts-reducer.ts b/cvat-ui/src/reducers/shortcuts-reducer.ts index 7751a80a6d50..3863f3b71652 100644 --- a/cvat-ui/src/reducers/shortcuts-reducer.ts +++ b/cvat-ui/src/reducers/shortcuts-reducer.ts @@ -287,20 +287,17 @@ const defaultKeyMap = ({ }, TOGGLE_LAYOUT_GRID: { name: 'Toggle layout grid', - description: 'Is used in development', + description: 'The grid is used to UI development', sequences: ['ctrl+alt+enter'], action: 'keydown', }, -} as any) as KeyMap; - -for (let i = 0; i < 10; i++) { - defaultKeyMap[`SWITCH_LABEL_${i}`] = { - name: `Switch label ${i}`, - description: `Switch label to label mapped to key ${i}`, - sequences: [`${i}`], + SWITCH_LABEL: { + name: 'Switch label', + description: 'Changes a label for an activated object or for the next drawn object if no objects are activated', + sequences: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'].map((val: string): string => `ctrl+${val}`), action: 'keydown', - }; -} + }, +} as any) as KeyMap; const defaultState: ShortcutsState = { visibleShortcutsHelp: false, diff --git a/cvat-ui/src/utils/mousetrap-react.tsx b/cvat-ui/src/utils/mousetrap-react.tsx index c51fe5fcd786..e8452e3d5562 100644 --- a/cvat-ui/src/utils/mousetrap-react.tsx +++ b/cvat-ui/src/utils/mousetrap-react.tsx @@ -17,7 +17,7 @@ export interface KeyMap { } export interface Handlers { - [index: string]: (event: KeyboardEvent) => void; + [index: string]: (event: KeyboardEvent, shortcut: string) => void; } interface Props { From 83db5beb5d9908987153c55b26e5f5c13d81c630 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 6 Apr 2021 15:39:39 +0300 Subject: [PATCH 03/10] Removed extra changes --- .../objects-side-bar/label-item.tsx | 1 - .../objects-side-bar/label-item.tsx | 17 +++-------------- cvat-ui/src/reducers/annotation-reducer.ts | 5 ----- 3 files changed, 3 insertions(+), 20 deletions(-) diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx index 912d28593ee1..1ed4a362dda6 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx @@ -13,7 +13,6 @@ import { interface Props { labelName: string; labelColor: string; - labelId: number; visible: boolean; statesHidden: boolean; statesLocked: boolean; diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx index 89754a7d3287..41b5c371f61d 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { connect } from 'react-redux'; -import { updateAnnotationsAsync, rememberObject as rememberObjectAction } from 'actions/annotation-actions'; +import { updateAnnotationsAsync } from 'actions/annotation-actions'; import LabelItemComponent from 'components/annotation-page/standard-workspace/objects-side-bar/label-item'; import { CombinedState, ObjectType } from 'reducers/interfaces'; @@ -18,22 +18,19 @@ interface StateToProps { label: any; labelName: string; labelColor: string; - labelId: number; objectStates: any[]; jobInstance: any; frameNumber: number; - activatedStateID: number | null; } interface DispatchToProps { updateAnnotations(states: any[]): void; - rememberObject(labelID: number): void; } function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps { const { annotation: { - annotations: { states: objectStates, activatedStateID }, + annotations: { states: objectStates }, job: { labels, instance: jobInstance }, player: { frame: { number: frameNumber }, @@ -47,11 +44,9 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps { label, labelColor: label.color, labelName: label.name, - labelId: label.id, objectStates, jobInstance, frameNumber, - activatedStateID, }; } @@ -60,9 +55,6 @@ function mapDispatchToProps(dispatch: any): DispatchToProps { updateAnnotations(states: any[]): void { dispatch(updateAnnotationsAsync(states)); }, - rememberObject(labelID: number): void { - dispatch(rememberObjectAction({ activeLabelID: labelID })); - }, }; } @@ -70,7 +62,6 @@ type Props = StateToProps & DispatchToProps & OwnProps; interface State { objectStates: any[]; ownObjectStates: any[]; - switchLabelMapping: Record; // key, labelID visible: boolean; statesHidden: boolean; statesLocked: boolean; @@ -82,7 +73,6 @@ class LabelItemContainer extends React.PureComponent { this.state = { objectStates: [], ownObjectStates: [], - switchLabelMapping: {}, visible: true, statesHidden: false, statesLocked: false, @@ -158,14 +148,13 @@ class LabelItemContainer extends React.PureComponent { } public render(): JSX.Element { - const { labelName, labelColor, labelId } = this.props; + const { labelName, labelColor } = this.props; const { visible, statesHidden, statesLocked } = this.state; return ( { if (job.task.dimension === DimensionType.DIM_3D) { workspaceSelected = Workspace.STANDARD3D; } - return { ...state, job: { From 396f5ded2a8fe0136dd9f35a0d4bed68f6f2223a Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 6 Apr 2021 15:55:19 +0300 Subject: [PATCH 04/10] Minor refactoring --- cvat-ui/src/actions/annotation-actions.ts | 18 +--------------- .../objects-side-bar/label-item.tsx | 7 +++++-- .../objects-side-bar/styles.scss | 5 ----- cvat-ui/src/reducers/annotation-reducer.ts | 21 ++++++++++++++++--- 4 files changed, 24 insertions(+), 27 deletions(-) diff --git a/cvat-ui/src/actions/annotation-actions.ts b/cvat-ui/src/actions/annotation-actions.ts index c2868a5df522..a7afc3b1bd9f 100644 --- a/cvat-ui/src/actions/annotation-actions.ts +++ b/cvat-ui/src/actions/annotation-actions.ts @@ -1137,25 +1137,9 @@ export function rememberObject(createParams: { activeNumOfPoints?: number; activeRectDrawingMethod?: RectDrawingMethod; }): AnyAction { - let activeControl = createParams.activeObjectType ? ActiveControl.CURSOR : null; - if (createParams.activeShapeType === ShapeType.RECTANGLE) { - activeControl = ActiveControl.DRAW_RECTANGLE; - } else if (createParams.activeShapeType === ShapeType.POLYGON) { - activeControl = ActiveControl.DRAW_POLYGON; - } else if (createParams.activeShapeType === ShapeType.POLYLINE) { - activeControl = ActiveControl.DRAW_POLYLINE; - } else if (createParams.activeShapeType === ShapeType.POINTS) { - activeControl = ActiveControl.DRAW_POINTS; - } else if (createParams.activeShapeType === ShapeType.CUBOID) { - activeControl = ActiveControl.DRAW_CUBOID; - } - return { type: AnnotationActionTypes.REMEMBER_CREATED_OBJECT, - payload: { - createParams, - activeControl, - }, + payload: createParams, }; } diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx index 1ed4a362dda6..62fc09b17661 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx @@ -48,9 +48,12 @@ function LabelItemComponent(props: Props): JSX.Element { return ( - - - - {labelName} - - - - {statesLocked ? ( - - ) : ( - - )} - - - {statesHidden ? ( - - ) : ( - - )} - - +
+
+ + + + + {labelName} + + + + + + + + {statesLocked ? ( + + ) : ( + + )} + + + {statesHidden ? ( + + ) : ( + + )} + + +
); } diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx index 33a39b9d6812..3510a171040d 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx @@ -24,7 +24,7 @@ function LabelsListComponent(): JSX.Element { const labelIDs = labels.map((label: any): number => label.id); - const [keyMapping] = useState>( + const [keyToLabelMapping, setKeyToLabelMapping] = useState>( Object.fromEntries( labelIDs.slice(0, 10).map((labelID: number, idx: number) => [idx + 1 > 9 ? 0 : idx + 1, labelID]), ), @@ -37,8 +37,7 @@ function LabelsListComponent(): JSX.Element { const handlers = { SWITCH_LABEL: (event: KeyboardEvent | undefined, shortcut: string) => { if (event) event.preventDefault(); - const key = +shortcut.split('+')[1]; - const labelID = keyMapping[key]; + const labelID = keyToLabelMapping[shortcut.split('+')[1].trim()]; const label = labels.filter((_label: any) => _label.id === labelID)[0]; if (Number.isInteger(labelID) && label) { if (Number.isInteger(activatedStateID)) { @@ -56,12 +55,53 @@ function LabelsListComponent(): JSX.Element { }, }; + // create reversed mapping just to receive key easily + const labelToKeyMapping: Record = Object.fromEntries( + Object.entries(keyToLabelMapping).map(([key, labelID]) => [labelID, key]), + ); + return (
{labelIDs.map( (labelID: number): JSX.Element => ( - + { + // unassign any keys assigned to the current labels + const keyToLabelMappingCopy = { ...keyToLabelMapping }; + for (const shortKey of Object.keys(keyToLabelMappingCopy)) { + if (keyToLabelMappingCopy[shortKey] === labelID) { + delete keyToLabelMappingCopy[shortKey]; + } + } + + if (key === '—') { + setKeyToLabelMapping(keyToLabelMappingCopy); + return; + } + + // check if this key is assigned to another label + if (key in keyToLabelMappingCopy) { + // try to find a new key for the other label + for (let i = 0; i < 10; i++) { + const adjustedI = i + 1 > 9 ? 0 : i + 1; + if (!(adjustedI in keyToLabelMappingCopy)) { + keyToLabelMappingCopy[adjustedI] = keyToLabelMappingCopy[key]; + break; + } + } + // delete assigning to the other label + delete keyToLabelMappingCopy[key]; + } + + // assigning to the current label + keyToLabelMappingCopy[key] = labelID; + setKeyToLabelMapping(keyToLabelMappingCopy); + }} + /> ), )}
diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss index 34aef1bb95ed..857900e5d56e 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss @@ -167,7 +167,8 @@ padding: 3px 1px 1px 3px; } -.cvat-objects-sidebar-state-item-color { +.cvat-objects-sidebar-state-item-color, +.cvat-objects-sidebar-label-item-color { border: 1px solid $object-item-border-color; width: 7px; opacity: 1; @@ -287,19 +288,23 @@ } .cvat-objects-sidebar-label-active-item { - background: $active-label-background-color; + border-top: 2px solid $object-item-border-color; + border-right: 2px solid $object-item-border-color; + border-bottom: 2px solid $object-item-border-color; + padding: 3px 1px 1px 3px; } .cvat-objects-sidebar-label-item { height: 2.5em; + width: 100%; border-bottom: 1px solid $border-color-1; - padding: 5px; + padding: 5px 3px 3px 3px; span { @extend .cvat-object-sidebar-icon; } - > div:nth-child(2) { + > div:nth-child(1) { text-overflow: ellipsis; overflow: hidden; max-height: 1.5em; @@ -311,10 +316,8 @@ } } -.cvat-label-item-color-button { - width: 30px; - height: 20px; - border-radius: 5px; +.cvat-objects-sidebar-label-item-wrapper { + display: flex; } .cvat-objects-appearance-content { @@ -363,6 +366,5 @@ } .cvat-objects-sidebar-label-item-disabled { - opacity: 0.4; - pointer-events: none; + opacity: 0.5; } diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx index 41b5c371f61d..17e872949502 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx @@ -12,6 +12,8 @@ import { CombinedState, ObjectType } from 'reducers/interfaces'; interface OwnProps { labelID: number; + labelShortcutKey?: string; + updateLabelShortcutKey(labelShortcut: string): void; } interface StateToProps { @@ -148,13 +150,16 @@ class LabelItemContainer extends React.PureComponent { } public render(): JSX.Element { - const { labelName, labelColor } = this.props; + const { + labelName, labelColor, labelShortcutKey, updateLabelShortcutKey, + } = this.props; const { visible, statesHidden, statesLocked } = this.state; return ( { showStates={this.showStates} lockStates={this.lockStates} unlockStates={this.unlockStates} + updateLabelShortcutKey={updateLabelShortcutKey} /> ); } From 2008b0318af6699a24518cf0367e42ae2ead3332 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 6 Apr 2021 20:10:54 +0300 Subject: [PATCH 06/10] Redesigned popover --- cvat-ui/src/base.scss | 1 + .../objects-side-bar/issues-list.tsx | 2 +- .../objects-side-bar/label-item.tsx | 113 +++++++++--------- .../label-key-selector-popover.tsx | 73 +++++++++++ .../objects-side-bar/labels-list.tsx | 7 +- .../objects-side-bar/styles.scss | 39 +++++- .../objects-side-bar/label-item.tsx | 9 +- 7 files changed, 173 insertions(+), 71 deletions(-) create mode 100644 cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-key-selector-popover.tsx diff --git a/cvat-ui/src/base.scss b/cvat-ui/src/base.scss index f447e5e58561..9ef0e0ac60c5 100644 --- a/cvat-ui/src/base.scss +++ b/cvat-ui/src/base.scss @@ -31,6 +31,7 @@ $ok-icon-color: #61c200; $info-icon-color: #0074d9; $objects-bar-tabs-color: #bebebe; $objects-bar-icons-color: #242424; // #6e6e6e +$active-label-background-color: #d8ecff; $object-item-border-color: rgba(0, 0, 0, 0.7); $slider-color: #1890ff; diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/issues-list.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/issues-list.tsx index f9fb7070176c..ffd78023c500 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/issues-list.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/issues-list.tsx @@ -4,7 +4,6 @@ import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; -import { CombinedState } from 'reducers/interfaces'; import { LeftOutlined, RightOutlined, EyeInvisibleFilled, EyeOutlined, } from '@ant-design/icons'; @@ -14,6 +13,7 @@ import { Row, Col } from 'antd/lib/grid'; import { changeFrameAsync } from 'actions/annotation-actions'; import { reviewActions } from 'actions/review-actions'; import CVATTooltip from 'components/common/cvat-tooltip'; +import { CombinedState } from 'reducers/interfaces'; export default function LabelsListComponent(): JSX.Element { const dispatch = useDispatch(); diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx index 8eb66988f036..5b88cb8ee00a 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx @@ -4,36 +4,39 @@ import React from 'react'; import { Row, Col } from 'antd/lib/grid'; -import Select from 'antd/lib/select'; +import Button from 'antd/lib/button'; import Text from 'antd/lib/typography/Text'; import { LockFilled, UnlockOutlined, EyeInvisibleFilled, EyeOutlined, } from '@ant-design/icons'; import CVATTooltip from 'components/common/cvat-tooltip'; +import LabelKeySelectorPopover from './label-key-selector-popover'; interface Props { labelName: string; labelColor: string; - labelShortcutKey: string; + labelID: number; visible: boolean; statesHidden: boolean; statesLocked: boolean; + keyToLabelMapping: Record; hideStates(): void; showStates(): void; lockStates(): void; unlockStates(): void; - updateLabelShortcutKey(labelShortcut: string): void; + updateLabelShortcutKey(updatedKey: string): void; } function LabelItemComponent(props: Props): JSX.Element { const { labelName, labelColor, + labelID, + keyToLabelMapping, visible, statesHidden, statesLocked, - labelShortcutKey, hideStates, showStates, lockStates, @@ -41,6 +44,11 @@ function LabelItemComponent(props: Props): JSX.Element { updateLabelShortcutKey, } = props; + // create reversed mapping just to receive key easily + const labelToKeyMapping: Record = Object.fromEntries( + Object.entries(keyToLabelMapping).map(([key, _labelID]) => [_labelID, key]), + ); + const labelShortcutKey = labelToKeyMapping[labelID]; const classes = { lock: { enabled: { className: 'cvat-label-item-button-lock cvat-label-item-button-lock-enabled' }, @@ -53,58 +61,51 @@ function LabelItemComponent(props: Props): JSX.Element { }; return ( -
-
- - - - - {labelName} - - - - - - - - {statesLocked ? ( - - ) : ( - - )} - - - {statesHidden ? ( - - ) : ( - - )} - - -
+ + +
+ {' '} +
+ + + + + {labelName} + + + + + + + + + + {statesLocked ? ( + + ) : ( + + )} + + + {statesHidden ? ( + + ) : ( + + )} + +
); } diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-key-selector-popover.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-key-selector-popover.tsx new file mode 100644 index 000000000000..7b885725d911 --- /dev/null +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-key-selector-popover.tsx @@ -0,0 +1,73 @@ +// Copyright (C) 2021 Intel Corporation +// +// SPDX-License-Identifier: MIT + +import React from 'react'; +import { useSelector } from 'react-redux'; +import Popover from 'antd/lib/popover'; +import Button from 'antd/lib/button'; +import { Row, Col } from 'antd/lib/grid'; +import Text from 'antd/lib/typography/Text'; + +import { CombinedState } from 'reducers/interfaces'; +import CVATTooltip from 'components/common/cvat-tooltip'; + +interface LabelKeySelectorPopoverProps { + updateLabelShortcutKey(updatedKey: string): void; + keyToLabelMapping: Record; + children: JSX.Element; +} + +interface LabelKeySelectorPopoverContentProps { + updateLabelShortcutKey(updatedKey: string): void; + keyToLabelMapping: Record; +} + +function PopoverContent(props: LabelKeySelectorPopoverContentProps): JSX.Element { + const { keyToLabelMapping, updateLabelShortcutKey } = props; + const labels = useSelector((state: CombinedState) => state.annotation.job.labels); + + return ( +
+ {[['1', '2', '3'], ['4', '5', '6'], ['7', '8', '9'], ['0']].map((arr, i_) => ( + + {arr.map((i) => { + const labelID = keyToLabelMapping[i]; + const labelName = Number.isInteger(labelID) ? + labels.filter((label: any): boolean => label.id === labelID)[0]?.name || 'undefined' : + 'None'; + + return ( + + + + + + ); + })} + + ))} +
+ ); +} + +export default function LabelKeySelectorPopover(props: LabelKeySelectorPopoverProps): JSX.Element { + const { children, updateLabelShortcutKey, keyToLabelMapping } = props; + + return ( + + } + placement='left' + > + {children} + + ); +} diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx index 3510a171040d..7a58739d3a5d 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx @@ -55,11 +55,6 @@ function LabelsListComponent(): JSX.Element { }, }; - // create reversed mapping just to receive key easily - const labelToKeyMapping: Record = Object.fromEntries( - Object.entries(keyToLabelMapping).map(([key, labelID]) => [labelID, key]), - ); - return (
@@ -68,7 +63,7 @@ function LabelsListComponent(): JSX.Element { { // unassign any keys assigned to the current labels const keyToLabelMappingCopy = { ...keyToLabelMapping }; diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss index 857900e5d56e..c66316031256 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss @@ -288,6 +288,7 @@ } .cvat-objects-sidebar-label-active-item { + background: $active-label-background-color; border-top: 2px solid $object-item-border-color; border-right: 2px solid $object-item-border-color; border-bottom: 2px solid $object-item-border-color; @@ -296,7 +297,6 @@ .cvat-objects-sidebar-label-item { height: 2.5em; - width: 100%; border-bottom: 1px solid $border-color-1; padding: 5px 3px 3px 3px; @@ -304,7 +304,7 @@ @extend .cvat-object-sidebar-icon; } - > div:nth-child(1) { + > div:nth-child(2) { text-overflow: ellipsis; overflow: hidden; max-height: 1.5em; @@ -316,8 +316,39 @@ } } -.cvat-objects-sidebar-label-item-wrapper { - display: flex; +.cvat-label-item-color { + background: rgb(25, 184, 14); + height: 80%; + width: 90%; + border-radius: $grid-unit-size / 2; +} + +.cvat-label-item-setup-shortcut-button { + border-color: $objects-bar-icons-color; +} + +.cvat-label-item-setup-shortcut-popover { + margin-top: -$grid-unit-size; + margin-bottom: -$grid-unit-size; + + > div { + padding-top: $grid-unit-size; + padding-bottom: $grid-unit-size; + + > div { + display: flex; + justify-content: center; + + > button { + width: $grid-unit-size * 15; + overflow-x: hidden; + + span:first-child { + margin-right: $grid-unit-size; + } + } + } + } } .cvat-objects-appearance-content { diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx index 17e872949502..126aef29c291 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/label-item.tsx @@ -12,8 +12,8 @@ import { CombinedState, ObjectType } from 'reducers/interfaces'; interface OwnProps { labelID: number; - labelShortcutKey?: string; - updateLabelShortcutKey(labelShortcut: string): void; + keyToLabelMapping: Record; + updateLabelShortcutKey(updatedKey: string): void; } interface StateToProps { @@ -151,7 +151,7 @@ class LabelItemContainer extends React.PureComponent { public render(): JSX.Element { const { - labelName, labelColor, labelShortcutKey, updateLabelShortcutKey, + labelName, labelColor, keyToLabelMapping, labelID, updateLabelShortcutKey, } = this.props; const { visible, statesHidden, statesLocked } = this.state; @@ -159,7 +159,8 @@ class LabelItemContainer extends React.PureComponent { Date: Tue, 6 Apr 2021 21:32:16 +0300 Subject: [PATCH 07/10] Added changelog record & updated version --- CHANGELOG.md | 8 +++++++- cvat-ui/package-lock.json | 2 +- cvat-ui/package.json | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 799d9fc2832b..217545cb63b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,23 +8,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.4.0] - Unreleased ### Added + - Documentation on mask annotation () +- Hotkeys to switch a label of existing object or to change default label (for objects created with N) () ### Changed + - ### Deprecated + - ### Removed + - ### Fixed + - Export of instance masks with holes () ### Security -- +- ## [1.3.0] - 3/31/2021 diff --git a/cvat-ui/package-lock.json b/cvat-ui/package-lock.json index 27c93845c188..f4cc563d1794 100644 --- a/cvat-ui/package-lock.json +++ b/cvat-ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.18.1", + "version": "1.19.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/cvat-ui/package.json b/cvat-ui/package.json index caf6ba255fde..caae68129325 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.18.1", + "version": "1.19.0", "description": "CVAT single-page application", "main": "src/index.tsx", "scripts": { From 770dbd0ff78e0de2631984836327398178bab049 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 6 Apr 2021 22:12:24 +0300 Subject: [PATCH 08/10] Added memoization --- .../objects-side-bar/label-item.tsx | 7 +- .../label-key-selector-popover.tsx | 37 ++++++---- .../objects-side-bar/labels-list.tsx | 72 ++++++++++--------- .../objects-side-bar/label-item.tsx | 2 +- 4 files changed, 68 insertions(+), 50 deletions(-) diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx index 5b88cb8ee00a..fc3e1dade8f5 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx @@ -25,7 +25,7 @@ interface Props { showStates(): void; lockStates(): void; unlockStates(): void; - updateLabelShortcutKey(updatedKey: string): void; + updateLabelShortcutKey(updatedKey: string, labelID: number): void; } function LabelItemComponent(props: Props): JSX.Element { @@ -48,7 +48,7 @@ function LabelItemComponent(props: Props): JSX.Element { const labelToKeyMapping: Record = Object.fromEntries( Object.entries(keyToLabelMapping).map(([key, _labelID]) => [_labelID, key]), ); - const labelShortcutKey = labelToKeyMapping[labelID]; + const labelShortcutKey = labelToKeyMapping[labelID] || '\u2205'; const classes = { lock: { enabled: { className: 'cvat-label-item-button-lock cvat-label-item-button-lock-enabled' }, @@ -83,8 +83,9 @@ function LabelItemComponent(props: Props): JSX.Element { @@ -68,6 +66,7 @@ function LabelKeySelectorPopover(props: LabelKeySelectorPopoverProps): JSX.Eleme return ( Date: Wed, 7 Apr 2021 10:44:01 +0300 Subject: [PATCH 10/10] Applied comments --- .../standard-workspace/objects-side-bar/label-item.tsx | 4 ++-- .../standard-workspace/objects-side-bar/labels-list.tsx | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx index f9572718f11d..45f47c712d40 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/label-item.tsx @@ -74,7 +74,7 @@ function LabelItemComponent(props: Props): JSX.Element { {' '}
- + {labelName} @@ -92,7 +92,7 @@ function LabelItemComponent(props: Props): JSX.Element { - + {statesLocked ? ( ) : ( diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx index d2b10833b07c..b0955de0bed7 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/labels-list.tsx @@ -24,9 +24,7 @@ function LabelsListComponent(): JSX.Element { const labelIDs = labels.map((label: any): number => label.id); const [keyToLabelMapping, setKeyToLabelMapping] = useState>( - Object.fromEntries( - labelIDs.slice(0, 10).map((labelID: number, idx: number) => [idx + 1 > 9 ? 0 : idx + 1, labelID]), - ), + Object.fromEntries(labelIDs.slice(0, 10).map((labelID: number, idx: number) => [(idx + 1) % 10, labelID])), ); const updateLabelShortcutKey = useCallback( @@ -48,7 +46,7 @@ function LabelsListComponent(): JSX.Element { if (key in keyToLabelMappingCopy) { // try to find a new key for the other label for (let i = 0; i < 10; i++) { - const adjustedI = i + 1 > 9 ? 0 : i + 1; + const adjustedI = (i + 1) % 10; if (!(adjustedI in keyToLabelMappingCopy)) { keyToLabelMappingCopy[adjustedI] = keyToLabelMappingCopy[key]; break;