[React] Added shortcuts & ability to search a label (#1683)

* Fixed remark-linter errors in CONTRIBUTING.md, added python3-tk to the installation guide

* Added labels search

* Added shortcuts to change color and split

* Updated version

* Updated CHANGELOG.md

* Added title

* Added shortcut hint for change color, added ability to change color of group/label using the shortcut

* Updated CONTRIBUTING.md
main
Boris Sekachev 6 years ago committed by GitHub
parent 9320baeeb1
commit 27e43daf84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,9 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [1.1.0-alpha] - Unreleased
### Added
- Throttling policy for unauthenticated users (<https://github.com/opencv/cvat/pull/1531>)
- Added default label color table for mask export (https://github.com/opencv/cvat/pull/1549)
- Added default label color table for mask export (<https://github.com/opencv/cvat/pull/1549>)
- Added environment variables for Redis and Postgres hosts for Kubernetes deployment support (<https://github.com/opencv/cvat/pull/1641>)
- Added visual identification for unavailable formats (https://github.com/opencv/cvat/pull/1567)
- Added visual identification for unavailable formats (<https://github.com/opencv/cvat/pull/1567>)
- Shortcut to change color of an activated shape in new UI (Enter) (<https://github.com/opencv/cvat/pull/1683>)
- Shortcut to switch split mode (<https://github.com/opencv/cvat/pull/1683>)
- Built-in search for labels when create an object or change a label (<https://github.com/opencv/cvat/pull/1683>)
### Changed
- Removed information about e-mail from the basic user information (<https://github.com/opencv/cvat/pull/1627>)
@ -22,15 +25,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
-
### Fixed
- Fixed dataset filter item representation for imageless dataset items (https://github.com/opencv/cvat/pull/1593)
- Fixed interpreter crash when trying to import `tensorflow` with no AVX instructions available (https://github.com/opencv/cvat/pull/1567)
- Fixed dataset filter item representation for imageless dataset items (<https://github.com/opencv/cvat/pull/1593>)
- Fixed interpreter crash when trying to import `tensorflow` with no AVX instructions available (<https://github.com/opencv/cvat/pull/1567>)
- Kibana wrong working time calculation with new annotation UI use (<https://github.com/opencv/cvat/pull/1654>)
- Wrong rexex for account name validation (<https://github.com/opencv/cvat/pull/1667>)
- Wrong description on register view for the username field (<https://github.com/opencv/cvat/pull/1667>)
- Wrong resolution for resizing a shape (<https://github.com/opencv/cvat/pull/1667>)
### Security
- SQL injection in Django `CVE-2020-9402` (https://github.com/opencv/cvat/pull/1657)
- SQL injection in Django `CVE-2020-9402` (<https://github.com/opencv/cvat/pull/1657>)
## [1.0.0] - 2020-05-29
### Added

@ -14,7 +14,7 @@ Next steps should work on clear Ubuntu 18.04.
- Install necessary dependencies:
```sh
$ sudo apt-get update && sudo apt-get --no-install-recommends install -y ffmpeg build-essential curl redis-server python3-dev python3-pip python3-venv libldap2-dev libsasl2-dev
$ sudo apt-get update && sudo apt-get --no-install-recommends install -y ffmpeg build-essential curl redis-server python3-dev python3-pip python3-venv python3-tk libldap2-dev libsasl2-dev
```
Also please make sure that you have installed ffmpeg with all necessary libav* libraries and pkg-config package.
```sh
@ -60,6 +60,7 @@ for development
- Install npm packages for UI and start UI debug server (run the following command from CVAT root directory):
```sh
npm install && \
cd cvat-core && npm install && \
cd ../cvat-ui && npm install && npm start
```
@ -83,12 +84,13 @@ for development
You have done! Now it is possible to insert breakpoints and debug server and client of the tool.
## How to setup additional components in development environment
## Setup additional components in development environment
### Automatic annotation
- Install OpenVINO on your host machine according to instructions from
- Install OpenVINO on your host machine according to instructions from
[OpenVINO website](https://docs.openvinotoolkit.org/latest/index.html)
- Add some environment variables (copy code below to the end of ``.env/bin/activate`` file):
- Add some environment variables (copy code below to the end of ``.env/bin/activate`` file):
```sh
source /opt/intel/openvino/bin/setupvars.sh
@ -161,9 +163,10 @@ litle exception - we prefere 4 spaces for indentation of nested blocks and state
The project uses [a successful Git branching model](https://nvie.com/posts/a-successful-git-branching-model).
Thus it has a couple of branches. Some of them are described below:
- `origin/master` to be the main branch where the source code of
HEAD always reflects a production-ready state.
- `origin/develop` to be the main branch where the source code of
- `origin/master` to be the main branch where the source code of
HEAD always reflects a production-ready state
- `origin/develop` to be the main branch where the source code of
HEAD always reflects a state with the latest delivered development
changes for the next release. Some would call this the “integration branch”.
@ -173,13 +176,13 @@ The issue tracker is the preferred channel for [bug reports](#bugs),
[features requests](#features) and [submitting pull
requests](#pull-requests), but please respect the following restrictions:
* Please **do not** use the issue tracker for personal support requests (use
- Please **do not** use the issue tracker for personal support requests (use
[Stack Overflow](http://stackoverflow.com)).
* Please **do not** derail or troll issues. Keep the discussion on topic and
- Please **do not** derail or troll issues. Keep the discussion on topic and
respect the opinions of others.
<a name="bugs"></a>
<a name="bugs"></a>
## Bug reports
A bug is a _demonstrable problem_ that is caused by the code in the repository.
@ -187,13 +190,13 @@ Good bug reports are extremely helpful - thank you!
Guidelines for bug reports:
1. **Use the GitHub issue search** &mdash; check if the issue has already been
1. **Use the GitHub issue search** &mdash; check if the issue has already been
reported.
2. **Check if the issue has been fixed** &mdash; try to reproduce it using the
1. **Check if the issue has been fixed** &mdash; try to reproduce it using the
latest `develop` branch in the repository.
3. **Isolate the problem** &mdash; ideally create a reduced test case.
1. **Isolate the problem** &mdash; ideally create a reduced test case.
A good bug report shouldn't leave others needing to chase you up for more
information. Please try to be as detailed as possible in your report. What is
@ -209,9 +212,8 @@ Example:
> suitable, include the steps required to reproduce the bug.
>
> 1. This is the first step
> 2. This is the second step
> 3. Further steps, etc.
>
> 1. This is the second step
> 1. Further steps, etc.
>
> Any other information you want to share that is relevant to the issue being
> reported. This might include the lines of code that you have identified as
@ -222,7 +224,7 @@ Example:
## Feature requests
Feature requests are welcome. But take a moment to find out whether your idea
fits with the scope and aims of the project. It's up to *you* to make a strong
fits with the scope and aims of the project. It's up to _you_ to make a strong
case to convince the project's developers of the merits of this feature. Please
provide as much detail and context as possible.
@ -244,51 +246,51 @@ accurate comments, etc.) and any other requirements (such as test coverage).
Follow this process if you'd like your work considered for inclusion in the
project:
1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork,
and configure the remotes:
1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork,
and configure the remotes:
```bash
# Clone your fork of the repo into the current directory
git clone https://github.com/<your-username>/<repo-name>
# Navigate to the newly cloned directory
cd <repo-name>
# Assign the original repo to a remote called "upstream"
git remote add upstream https://github.com/<upstream-owner>/<repo-name>
```
```bash
# Clone your fork of the repo into the current directory
git clone https://github.com/<your-username>/<repo-name>
# Navigate to the newly cloned directory
cd <repo-name>
# Assign the original repo to a remote called "upstream"
git remote add upstream https://github.com/<upstream-owner>/<repo-name>
```
2. If you cloned a while ago, get the latest changes from upstream:
1. If you cloned a while ago, get the latest changes from upstream:
```bash
git checkout <dev-branch>
git pull upstream <dev-branch>
```
```bash
git checkout <dev-branch>
git pull upstream <dev-branch>
```
3. Create a new topic branch (off the main project development branch) to
contain your feature, change, or fix:
1. Create a new topic branch (off the main project development branch) to
contain your feature, change, or fix:
```bash
git checkout -b <topic-branch-name>
```
```bash
git checkout -b <topic-branch-name>
```
4. Commit your changes in logical chunks. Please adhere to these [git commit
1. Commit your changes in logical chunks. Please adhere to these [git commit
message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
or your code is unlikely be merged into the main project. Use Git's
[interactive rebase](https://help.github.com/articles/interactive-rebase)
feature to tidy up your commits before making them public.
5. Locally merge (or rebase) the upstream development branch into your topic branch:
1. Locally merge (or rebase) the upstream development branch into your topic branch:
```bash
git pull [--rebase] upstream <dev-branch>
```
```bash
git pull [--rebase] upstream <dev-branch>
```
6. Push your topic branch up to your fork:
1. Push your topic branch up to your fork:
```bash
git push origin <topic-branch-name>
```
```bash
git push origin <topic-branch-name>
```
7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
1. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
with a clear title and description.
**IMPORTANT**: By submitting a patch, you agree to allow the project owner to

@ -1,6 +1,6 @@
{
"name": "cvat-ui",
"version": "1.2.2",
"version": "1.3.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

@ -1,6 +1,6 @@
{
"name": "cvat-ui",
"version": "1.2.2",
"version": "1.3.0",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {

@ -1288,19 +1288,23 @@ ThunkAction<Promise<void>, {}, {}, AnyAction> {
}
export function changeLabelColorAsync(
sessionInstance: any,
frameNumber: number,
label: any,
color: string,
): ThunkAction<Promise<void>, {}, {}, AnyAction> {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters();
const {
filters,
showAllInterpolationTracks,
jobInstance,
frame,
} = receiveAnnotationsParameters();
const updatedLabel = label;
updatedLabel.color = color;
const states = await sessionInstance.annotations
.get(frameNumber, showAllInterpolationTracks, filters);
const history = await sessionInstance.actions.get();
const states = await jobInstance.annotations
.get(frame, showAllInterpolationTracks, filters);
const history = await jobInstance.actions.get();
dispatch({
type: AnnotationActionTypes.CHANGE_LABEL_COLOR_SUCCESS,

@ -65,6 +65,7 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
PASTE_SHAPE: keyMap.PASTE_SHAPE,
SWITCH_DRAW_MODE: keyMap.SWITCH_DRAW_MODE,
SWITCH_MERGE_MODE: keyMap.SWITCH_MERGE_MODE,
SWITCH_SPLIT_MODE: keyMap.SWITCH_SPLIT_MODE,
SWITCH_GROUP_MODE: keyMap.SWITCH_GROUP_MODE,
RESET_GROUP: keyMap.RESET_GROUP,
CANCEL: keyMap.CANCEL,
@ -102,6 +103,15 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
canvasInstance.merge({ enabled: !merging });
mergeObjects(!merging);
},
SWITCH_SPLIT_MODE: (event: KeyboardEvent | undefined) => {
preventDefault(event);
const splitting = activeControl === ActiveControl.SPLIT;
if (!splitting) {
canvasInstance.cancel();
}
canvasInstance.split({ enabled: !splitting });
splitTrack(!splitting);
},
SWITCH_GROUP_MODE: (event: KeyboardEvent | undefined) => {
preventDefault(event);
const grouping = activeControl === ActiveControl.GROUP;
@ -205,6 +215,7 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
/>
<SplitControl
canvasInstance={canvasInstance}
switchSplitShortcut={normalizedKeyMap.SWITCH_SPLIT_MODE}
activeControl={activeControl}
splitTrack={splitTrack}
/>

@ -4,7 +4,7 @@
import React from 'react';
import { Row, Col } from 'antd/lib/grid';
import Select from 'antd/lib/select';
import Select, { OptionProps } from 'antd/lib/select';
import Button from 'antd/lib/button';
import InputNumber from 'antd/lib/input-number';
import Radio, { RadioChangeEvent } from 'antd/lib/radio';
@ -70,6 +70,15 @@ function DrawShapePopoverComponent(props: Props): JSX.Element {
<Row type='flex' justify='center'>
<Col span={24}>
<Select
showSearch
filterOption={(input: string, option: React.ReactElement<OptionProps>) => {
const { children } = option.props;
if (typeof (children) === 'string') {
return children.toLowerCase().includes(input.toLowerCase());
}
return false;
}}
value={`${selectedLabeID}`}
onChange={onChangeLabel}
>

@ -13,12 +13,13 @@ import { ActiveControl } from 'reducers/interfaces';
interface Props {
canvasInstance: Canvas;
activeControl: ActiveControl;
switchSplitShortcut: string;
splitTrack(enabled: boolean): void;
}
function SplitControl(props: Props): JSX.Element {
const {
switchSplitShortcut,
activeControl,
canvasInstance,
splitTrack,
@ -40,7 +41,7 @@ function SplitControl(props: Props): JSX.Element {
};
return (
<Tooltip title='Split a track' placement='right'>
<Tooltip title={`Split a track ${switchSplitShortcut}`} placement='right'>
<Icon {...dynamicIconProps} component={SplitIcon} />
</Tooltip>
);

@ -5,17 +5,16 @@
import React from 'react';
import { Row, Col } from 'antd/lib/grid';
import Button from 'antd/lib/button';
import Text from 'antd/lib/typography/Text';
interface Props {
shortcut: string;
colors: string[];
onChange(color: string): void;
}
function ColorChanger(props: Props): JSX.Element {
const {
colors,
onChange,
} = props;
const { shortcut, colors, onChange } = props;
const cols = 6;
const rows = Math.ceil(colors.length / cols);
@ -47,9 +46,12 @@ function ColorChanger(props: Props): JSX.Element {
}
return (
<>
<div>
<Text>
{`Press ${shortcut} to set a random color`}
</Text>
{antdRows}
</>
</div>
);
}

@ -18,6 +18,7 @@ interface Props {
visible: boolean;
statesHidden: boolean;
statesLocked: boolean;
changeColorShortcut: string;
hideStates(): void;
showStates(): void;
lockStates(): void;
@ -38,6 +39,7 @@ function LabelItemComponent(props: Props): JSX.Element {
lockStates,
unlockStates,
changeColor,
changeColorShortcut,
} = props;
return (
@ -54,6 +56,7 @@ function LabelItemComponent(props: Props): JSX.Element {
trigger='click'
content={(
<ColorChanger
shortcut={changeColorShortcut}
onChange={changeColor}
colors={labelColors}
/>

@ -5,7 +5,7 @@
import React from 'react';
import { Row, Col } from 'antd/lib/grid';
import Icon from 'antd/lib/icon';
import Select from 'antd/lib/select';
import Select, { OptionProps } from 'antd/lib/select';
import Radio, { RadioChangeEvent } from 'antd/lib/radio';
import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox';
import Input from 'antd/lib/input';
@ -86,7 +86,7 @@ function ItemMenu(
{shapeType === ShapeType.CUBOID && (
<Menu.Item>
<Button type='link' onClick={resetCuboidPerspective}>
<Icon component={ResetPerspectiveIcon}/>
<Icon component={ResetPerspectiveIcon} />
Reset perspective
</Button>
</Menu.Item>
@ -200,7 +200,20 @@ function ItemTopComponent(props: ItemTopComponentProps): JSX.Element {
</Col>
<Col span={12}>
<Tooltip title='Change current label'>
<Select size='small' value={`${labelID}`} onChange={changeLabel}>
<Select
size='small'
value={`${labelID}`}
onChange={changeLabel}
showSearch
filterOption={(input: string, option: React.ReactElement<OptionProps>) => {
const { children } = option.props;
if (typeof (children) === 'string') {
return children.toLowerCase().includes(input.toLowerCase());
}
return false;
}}
>
{ labels.map((label: any): JSX.Element => (
<Select.Option key={label.id} value={`${label.id}`}>
{label.name}
@ -366,7 +379,7 @@ function ItemButtonsComponent(props: ItemButtonsComponentProps): JSX.Element {
<Col>
<Tooltip title={`Switch lock property ${switchLockShortcut}`}>
{ locked
? <Icon type='lock' onClick={unlock} theme='filled'/>
? <Icon type='lock' onClick={unlock} theme='filled' />
: <Icon type='unlock' onClick={lock} />}
</Tooltip>
</Col>
@ -433,7 +446,7 @@ function ItemButtonsComponent(props: ItemButtonsComponentProps): JSX.Element {
<Col>
<Tooltip title={`Switch lock property ${switchLockShortcut}`}>
{ locked
? <Icon type='lock' onClick={unlock} theme='filled'/>
? <Icon type='lock' onClick={unlock} theme='filled' />
: <Icon type='unlock' onClick={lock} />}
</Tooltip>
</Col>
@ -851,6 +864,7 @@ function ObjectItemComponent(props: Props): JSX.Element {
trigger='click'
content={(
<ColorChanger
shortcut={normalizedKeyMap.CHANGE_OBJECT_COLOR}
onChange={changeColor}
colors={colors}
/>

@ -23,6 +23,7 @@ interface StateToProps {
labelName: string;
labelColor: string;
labelColors: string[];
changeColorShortcut: string;
objectStates: any[];
jobInstance: any;
frameNumber: any;
@ -30,7 +31,7 @@ interface StateToProps {
interface DispatchToProps {
updateAnnotations(states: any[]): void;
changeLabelColor(sessionInstance: any, frameNumber: number, label: any, color: string): void;
changeLabelColor(label: any, color: string): void;
}
function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
@ -50,6 +51,9 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
},
colors: labelColors,
},
shortcuts: {
normalizedKeyMap,
},
} = state;
const [label] = labels
@ -63,6 +67,7 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
objectStates,
jobInstance,
frameNumber,
changeColorShortcut: normalizedKeyMap.CHANGE_OBJECT_COLOR,
};
}
@ -72,12 +77,10 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
dispatch(updateAnnotationsAsync(states));
},
changeLabelColor(
sessionInstance: any,
frameNumber: number,
label: any,
color: string,
): void {
dispatch(changeLabelColorAsync(sessionInstance, frameNumber, label, color));
dispatch(changeLabelColorAsync(label, color));
},
};
}
@ -152,11 +155,9 @@ class LabelItemContainer extends React.PureComponent<Props, State> {
const {
changeLabelColor,
label,
frameNumber,
jobInstance,
} = this.props;
changeLabelColor(jobInstance, frameNumber, label, color);
changeLabelColor(label, color);
};
private switchHidden(value: boolean): void {
@ -196,6 +197,7 @@ class LabelItemContainer extends React.PureComponent<Props, State> {
labelName,
labelColor,
labelColors,
changeColorShortcut,
} = this.props;
return (
@ -206,6 +208,7 @@ class LabelItemContainer extends React.PureComponent<Props, State> {
visible={visible}
statesHidden={statesHidden}
statesLocked={statesLocked}
changeColorShortcut={changeColorShortcut}
hideStates={this.hideStates}
showStates={this.showStates}
lockStates={this.lockStates}

@ -24,8 +24,7 @@ import {
} from 'actions/annotation-actions';
import ObjectStateItemComponent from 'components/annotation-page/standard-workspace/objects-side-bar/object-item';
import {shift} from 'utils/math';
import { shift } from 'utils/math';
interface OwnProps {
clientID: number;
@ -58,7 +57,7 @@ interface DispatchToProps {
removeObject: (sessionInstance: any, objectState: any) => void;
copyShape: (objectState: any) => void;
propagateObject: (objectState: any) => void;
changeLabelColor(sessionInstance: any, frameNumber: number, label: any, color: string): void;
changeLabelColor(label: any, color: string): void;
changeGroupColor(group: number, color: string): void;
}
@ -155,12 +154,10 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
dispatch(propagateObjectAction(objectState));
},
changeLabelColor(
sessionInstance: any,
frameNumber: number,
label: any,
color: string,
): void {
dispatch(changeLabelColorAsync(sessionInstance, frameNumber, label, color));
dispatch(changeLabelColorAsync(label, color));
},
changeGroupColor(group: number, color: string): void {
dispatch(changeGroupColorAsync(group, color));
@ -357,12 +354,10 @@ class ObjectItemContainer extends React.PureComponent<Props> {
private changeColor = (color: string): void => {
const {
jobInstance,
objectState,
colorBy,
changeLabelColor,
changeGroupColor,
frameNumber,
} = this.props;
if (colorBy === ColorBy.INSTANCE) {
@ -371,7 +366,7 @@ class ObjectItemContainer extends React.PureComponent<Props> {
} else if (colorBy === ColorBy.GROUP) {
changeGroupColor(objectState.group.id, color);
} else if (colorBy === ColorBy.LABEL) {
changeLabelColor(jobInstance, frameNumber, objectState.label, color);
changeLabelColor(objectState.label, color);
}
};
@ -399,19 +394,13 @@ class ObjectItemContainer extends React.PureComponent<Props> {
this.commit();
};
private changeFrame(frame: number): void {
const { changeFrame, canvasInstance } = this.props;
if (isAbleToChangeFrame(canvasInstance)) {
changeFrame(frame);
}
}
private switchCuboidOrientation = (): void => {
function cuboidOrientationIsLeft(points: number[]): boolean {
return points[12] > points[0];
}
const {objectState} = this.props;
const { objectState } = this.props;
this.resetCuboidPerspective(false);
@ -419,19 +408,19 @@ class ObjectItemContainer extends React.PureComponent<Props> {
cuboidOrientationIsLeft(objectState.points) ? 4 : -4);
this.commit();
}
};
private resetCuboidPerspective = (commit: boolean = true): void => {
private resetCuboidPerspective = (commit = true): void => {
function cuboidOrientationIsLeft(points: number[]): boolean {
return points[12] > points[0];
}
const {objectState} = this.props;
const {points} = objectState;
const { objectState } = this.props;
const { points } = objectState;
const minD = {
x: (points[6] - points[2])*0.001,
y: (points[3] - points[1])*0.001,
}
x: (points[6] - points[2]) * 0.001,
y: (points[3] - points[1]) * 0.001,
};
if (cuboidOrientationIsLeft(points)) {
points[14] = points[10] + points[2] - points[6] + minD.x;
@ -451,6 +440,13 @@ class ObjectItemContainer extends React.PureComponent<Props> {
objectState.points = points;
if (commit) this.commit();
};
private changeFrame(frame: number): void {
const { changeFrame, canvasInstance } = this.props;
if (isAbleToChangeFrame(canvasInstance)) {
changeFrame(frame);
}
}
private commit(): void {

@ -14,9 +14,16 @@ import {
collapseObjectItems,
copyShape as copyShapeAction,
propagateObject as propagateObjectAction,
changeGroupColorAsync,
changeLabelColorAsync,
} from 'actions/annotation-actions';
import { Canvas, isAbleToChangeFrame } from 'cvat-canvas-wrapper';
import { CombinedState, StatesOrdering, ObjectType } from 'reducers/interfaces';
import {
CombinedState,
StatesOrdering,
ObjectType,
ColorBy,
} from 'reducers/interfaces';
interface StateToProps {
jobInstance: any;
@ -27,6 +34,8 @@ interface StateToProps {
statesCollapsed: boolean;
objectStates: any[];
annotationsFilters: string[];
colors: string[];
colorBy: ColorBy;
activatedStateID: number | null;
minZLayer: number;
maxZLayer: number;
@ -43,6 +52,8 @@ interface DispatchToProps {
copyShape: (objectState: any) => void;
propagateObject: (objectState: any) => void;
changeFrame(frame: number): void;
changeGroupColor(group: number, color: string): void;
changeLabelColor(label: any, color: string): void;
}
function mapStateToProps(state: CombinedState): StateToProps {
@ -71,6 +82,12 @@ function mapStateToProps(state: CombinedState): StateToProps {
instance: canvasInstance,
},
tabContentHeight: listHeight,
colors,
},
settings: {
shapes: {
colorBy,
},
},
shortcuts: {
keyMap,
@ -103,6 +120,8 @@ function mapStateToProps(state: CombinedState): StateToProps {
frameNumber,
jobInstance,
annotationsFilters,
colors,
colorBy,
activatedStateID,
minZLayer,
maxZLayer,
@ -133,6 +152,12 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
changeFrame(frame: number): void {
dispatch(changeFrameAsync(frame));
},
changeGroupColor(group: number, color: string): void {
dispatch(changeGroupColorAsync(group, color));
},
changeLabelColor(label: any, color: string): void {
dispatch(changeLabelColorAsync(label, color));
},
};
}
@ -252,6 +277,8 @@ class ObjectsListContainer extends React.PureComponent<Props, State> {
objectStates,
jobInstance,
updateAnnotations,
changeGroupColor,
changeLabelColor,
removeObject,
copyShape,
propagateObject,
@ -261,6 +288,8 @@ class ObjectsListContainer extends React.PureComponent<Props, State> {
keyMap,
normalizedKeyMap,
canvasInstance,
colors,
colorBy,
} = this.props;
const {
sortedStatesID,
@ -282,6 +311,7 @@ class ObjectsListContainer extends React.PureComponent<Props, State> {
PROPAGATE_OBJECT: keyMap.PROPAGATE_OBJECT,
NEXT_KEY_FRAME: keyMap.NEXT_KEY_FRAME,
PREV_KEY_FRAME: keyMap.PREV_KEY_FRAME,
CHANGE_OBJECT_COLOR: keyMap.CHANGE_OBJECT_COLOR,
};
const preventDefault = (event: KeyboardEvent | undefined): void => {
@ -359,6 +389,27 @@ class ObjectsListContainer extends React.PureComponent<Props, State> {
removeObject(jobInstance, state, event ? event.shiftKey : false);
}
},
CHANGE_OBJECT_COLOR: (event: KeyboardEvent | undefined) => {
preventDefault(event);
const state = activatedStated();
if (state) {
if (colorBy === ColorBy.GROUP) {
const colorID = (colors.indexOf(state.group.color) + 1) % colors.length;
changeGroupColor(state.group.id, colors[colorID]);
return;
}
if (colorBy === ColorBy.LABEL) {
const colorID = (colors.indexOf(state.label.color) + 1) % colors.length;
changeLabelColor(state.label, colors[colorID]);
return;
}
const colorID = (colors.indexOf(state.color) + 1) % colors.length;
state.color = colors[colorID];
updateAnnotations([state]);
}
},
TO_BACKGROUND: (event: KeyboardEvent | undefined) => {
preventDefault(event);
const state = activatedStated();

@ -217,6 +217,12 @@ const defaultKeyMap = {
sequences: ['m'],
action: 'keydown',
},
SWITCH_SPLIT_MODE: {
name: 'Split mode',
description: 'Activate or deactivate mode to splitting shapes',
sequences: ['alt+m'],
action: 'keydown',
},
SWITCH_GROUP_MODE: {
name: 'Group mode',
description: 'Activate or deactivate mode to grouping shapes',
@ -320,6 +326,12 @@ const defaultKeyMap = {
sequences: ['Control'],
action: 'keydown',
},
CHANGE_OBJECT_COLOR: {
name: 'Change color',
description: 'Set the next color for an activated shape',
sequences: ['Enter'],
action: 'keydown',
},
} as any as Record<string, ExtendedKeyMapOptions>;

Loading…
Cancel
Save