Skip to content

Commit

Permalink
React UI: Added annotation menus, added shape context menu, added som…
Browse files Browse the repository at this point in the history
…e confirmations before dangerous actions (#1123)

* Annotation menu, modified tasks menu

* Removed extra styles

* Context menu using side panel

* Mousewheel on draw

* Added more cursor icons

* Do not check .svg & .scss by eslint
  • Loading branch information
bsekachev authored Feb 6, 2020
1 parent 42614c2 commit 939de86
Show file tree
Hide file tree
Showing 31 changed files with 1,341 additions and 463 deletions.
17 changes: 16 additions & 1 deletion cvat-canvas/src/typescript/canvasView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,8 @@ export class CanvasViewImpl implements CanvasView, Listener {
});
}
}

e.preventDefault();
}

if (value) {
Expand Down Expand Up @@ -615,9 +617,10 @@ export class CanvasViewImpl implements CanvasView, Listener {
if ([1, 2].includes(event.which)) {
if ([Mode.DRAG_CANVAS, Mode.IDLE].includes(this.mode)) {
self.controller.enableDrag(event.clientX, event.clientY);
} else if (this.mode === Mode.ZOOM_CANVAS && event.which === 2) {
} else if ([Mode.ZOOM_CANVAS, Mode.DRAW].includes(this.mode) && event.which === 2) {
self.controller.enableDrag(event.clientX, event.clientY);
}
event.preventDefault();
}
});

Expand Down Expand Up @@ -751,25 +754,37 @@ export class CanvasViewImpl implements CanvasView, Listener {
} else if (reason === UpdateReasons.DRAW) {
const data: DrawData = this.controller.drawData;
if (data.enabled) {
this.canvas.style.cursor = 'crosshair';
this.mode = Mode.DRAW;
} else {
this.canvas.style.cursor = '';
}
this.drawHandler.draw(data, this.geometry);
} else if (reason === UpdateReasons.MERGE) {
const data: MergeData = this.controller.mergeData;
if (data.enabled) {
this.canvas.style.cursor = 'copy';
this.mode = Mode.MERGE;
} else {
this.canvas.style.cursor = '';
}
this.mergeHandler.merge(data);
} else if (reason === UpdateReasons.SPLIT) {
const data: SplitData = this.controller.splitData;
if (data.enabled) {
this.canvas.style.cursor = 'copy';
this.mode = Mode.SPLIT;
} else {
this.canvas.style.cursor = '';
}
this.splitHandler.split(data);
} else if (reason === UpdateReasons.GROUP) {
const data: GroupData = this.controller.groupData;
if (data.enabled) {
this.canvas.style.cursor = 'copy';
this.mode = Mode.GROUP;
} else {
this.canvas.style.cursor = '';
}
this.groupHandler.group(data);
} else if (reason === UpdateReasons.SELECT) {
Expand Down
11 changes: 7 additions & 4 deletions cvat-canvas/src/typescript/splitHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ export class SplitHandlerImpl implements SplitHandler {
private release(): void {
if (this.initialized) {
this.resetShape();
this.canvas.node.removeEventListener('mousemove', this.onFindObject);
this.canvas.node.removeEventListener('mousemove', this.findObject);
this.initialized = false;
}
}

private initSplitting(): void {
this.canvas.node.addEventListener('mousemove', this.onFindObject);
this.canvas.node.addEventListener('mousemove', this.findObject);
this.initialized = true;
this.splitDone = false;
}
Expand All @@ -47,6 +47,11 @@ export class SplitHandlerImpl implements SplitHandler {
this.release();
}

private findObject = (e: MouseEvent): void => {
this.resetShape();
this.onFindObject(e);
};

public constructor(
onSplitDone: (object: any) => void,
onFindObject: (event: MouseEvent) => void,
Expand Down Expand Up @@ -83,8 +88,6 @@ export class SplitHandlerImpl implements SplitHandler {
once: true,
});
}
} else {
this.resetShape();
}
}

Expand Down
1 change: 1 addition & 0 deletions cvat-ui/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module.exports = {
'@typescript-eslint',
'import',
],
'ignorePatterns': ['*.svg', '*.scss'],
'extends': [
'plugin:@typescript-eslint/recommended',
'airbnb-typescript',
Expand Down
99 changes: 99 additions & 0 deletions cvat-ui/src/actions/annotation-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,105 @@ export enum AnnotationActionTypes {
CHANGE_JOB_STATUS = 'CHANGE_JOB_STATUS',
CHANGE_JOB_STATUS_SUCCESS = 'CHANGE_JOB_STATUS_SUCCESS',
CHANGE_JOB_STATUS_FAILED = 'CHANGE_JOB_STATUS_FAILED',
UPLOAD_JOB_ANNOTATIONS = 'UPLOAD_JOB_ANNOTATIONS',
UPLOAD_JOB_ANNOTATIONS_SUCCESS = 'UPLOAD_JOB_ANNOTATIONS_SUCCESS',
UPLOAD_JOB_ANNOTATIONS_FAILED = 'UPLOAD_JOB_ANNOTATIONS_FAILED',
REMOVE_JOB_ANNOTATIONS_SUCCESS = 'REMOVE_JOB_ANNOTATIONS_SUCCESS',
REMOVE_JOB_ANNOTATIONS_FAILED = 'REMOVE_JOB_ANNOTATIONS_FAILED',
UPDATE_CANVAS_CONTEXT_MENU = 'UPDATE_CANVAS_CONTEXT_MENU',
}

export function updateCanvasContextMenu(visible: boolean, left: number, top: number): AnyAction {
return {
type: AnnotationActionTypes.UPDATE_CANVAS_CONTEXT_MENU,
payload: {
visible,
left,
top,
},
};
}

export function removeAnnotationsAsync(sessionInstance: any):
ThunkAction<Promise<void>, {}, {}, AnyAction> {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
sessionInstance.annotations.clear();
dispatch({
type: AnnotationActionTypes.REMOVE_JOB_ANNOTATIONS_SUCCESS,
payload: {
sessionInstance,
},
});
} catch (error) {
dispatch({
type: AnnotationActionTypes.REMOVE_JOB_ANNOTATIONS_FAILED,
payload: {
error,
},
});
}
};
}


export function uploadJobAnnotationsAsync(job: any, loader: any, file: File):
ThunkAction<Promise<void>, {}, {}, AnyAction> {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
const store = getCVATStore();
const state: CombinedState = store.getState();
if (state.tasks.activities.loads[job.task.id]) {
throw Error('Annotations is being uploaded for the task');
}
if (state.annotation.activities.loads[job.id]) {
throw Error('Only one uploading of annotations for a job allowed at the same time');
}

dispatch({
type: AnnotationActionTypes.UPLOAD_JOB_ANNOTATIONS,
payload: {
job,
loader,
},
});

const frame = state.annotation.player.frame.number;
await job.annotations.upload(file, loader);

// One more update to escape some problems
// in canvas when shape with the same
// clientID has different type (polygon, rectangle) for example
dispatch({
type: AnnotationActionTypes.UPLOAD_JOB_ANNOTATIONS_SUCCESS,
payload: {
job,
states: [],
},
});

await job.annotations.clear(true);
const states = await job.annotations.get(frame);

setTimeout(() => {
dispatch({
type: AnnotationActionTypes.UPLOAD_JOB_ANNOTATIONS_SUCCESS,
payload: {
job,
states,
},
});
});
} catch (error) {
dispatch({
type: AnnotationActionTypes.UPLOAD_JOB_ANNOTATIONS_FAILED,
payload: {
job,
error,
},
});
}
};
}

export function changeJobStatusAsync(jobInstance: any, status: string):
Expand Down
11 changes: 10 additions & 1 deletion cvat-ui/src/actions/tasks-actions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { AnyAction, Dispatch, ActionCreator } from 'redux';
import { ThunkAction } from 'redux-thunk';
import { TasksQuery } from 'reducers/interfaces';
import {
TasksQuery,
CombinedState,
} from 'reducers/interfaces';
import { getCVATStore } from 'cvat-store';
import getCore from 'cvat-core';
import { getInferenceStatusAsync } from './models-actions';

Expand Down Expand Up @@ -213,6 +217,11 @@ export function loadAnnotationsAsync(task: any, loader: any, file: File):
ThunkAction<Promise<void>, {}, {}, AnyAction> {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
const store = getCVATStore();
const state: CombinedState = store.getState();
if (state.tasks.activities.loads[task.id]) {
throw Error('Only one loading of annotations for a task allowed at the same time');
}
dispatch(loadAnnotations(task, loader));
await task.annotations.upload(file, loader);
} catch (error) {
Expand Down
Loading

0 comments on commit 939de86

Please sign in to comment.