diff --git a/cvat-core/src/session.js b/cvat-core/src/session.js index c3c1ad6dd73b..e3de1cb0e17a 100644 --- a/cvat-core/src/session.js +++ b/cvat-core/src/session.js @@ -77,12 +77,6 @@ return result; }, - async hasUnsavedChanges() { - const result = await PluginRegistry - .apiWrapper.call(this, prototype.annotations.hasUnsavedChanges); - return result; - }, - async merge(objectStates) { const result = await PluginRegistry .apiWrapper.call(this, prototype.annotations.merge, objectStates); @@ -107,6 +101,12 @@ .apiWrapper.call(this, prototype.annotations.exportDataset, format); return result; }, + + hasUnsavedChanges() { + const result = prototype.annotations + .hasUnsavedChanges.implementation.call(this); + return result; + }, }, writable: true, }), @@ -381,14 +381,14 @@ * @async */ /** - * Indicate if there are any changes in + * Method indicates if there are any changes in * annotations which haven't been saved on a server + *
This function cannot be wrapped with a plugin * @method hasUnsavedChanges * @memberof Session.annotations * @returns {boolean} * @throws {module:API.cvat.exceptions.PluginError} * @instance - * @async */ /** * Export as a dataset. diff --git a/cvat-core/tests/api/annotations.js b/cvat-core/tests/api/annotations.js index bf91379f5b9a..57fe808eca94 100644 --- a/cvat-core/tests/api/annotations.js +++ b/cvat-core/tests/api/annotations.js @@ -341,11 +341,11 @@ describe('Feature: save annotations', () => { zOrder: 0, }); - expect(await task.annotations.hasUnsavedChanges()).toBe(false); + expect(task.annotations.hasUnsavedChanges()).toBe(false); await task.annotations.put([state]); - expect(await task.annotations.hasUnsavedChanges()).toBe(true); + expect(task.annotations.hasUnsavedChanges()).toBe(true); await task.annotations.save(); - expect(await task.annotations.hasUnsavedChanges()).toBe(false); + expect(task.annotations.hasUnsavedChanges()).toBe(false); annotations = await task.annotations.get(0); expect(annotations).toHaveLength(length + 1); }); @@ -354,23 +354,23 @@ describe('Feature: save annotations', () => { const task = (await window.cvat.tasks.get({ id: 101 }))[0]; const annotations = await task.annotations.get(0); - expect(await task.annotations.hasUnsavedChanges()).toBe(false); + expect(task.annotations.hasUnsavedChanges()).toBe(false); annotations[0].occluded = true; await annotations[0].save(); - expect(await task.annotations.hasUnsavedChanges()).toBe(true); + expect(task.annotations.hasUnsavedChanges()).toBe(true); await task.annotations.save(); - expect(await task.annotations.hasUnsavedChanges()).toBe(false); + expect(task.annotations.hasUnsavedChanges()).toBe(false); }); test('delete & save annotations for a task', async () => { const task = (await window.cvat.tasks.get({ id: 101 }))[0]; const annotations = await task.annotations.get(0); - expect(await task.annotations.hasUnsavedChanges()).toBe(false); + expect(task.annotations.hasUnsavedChanges()).toBe(false); await annotations[0].delete(); - expect(await task.annotations.hasUnsavedChanges()).toBe(true); + expect(task.annotations.hasUnsavedChanges()).toBe(true); await task.annotations.save(); - expect(await task.annotations.hasUnsavedChanges()).toBe(false); + expect(task.annotations.hasUnsavedChanges()).toBe(false); }); test('create & save annotations for a job', async () => { @@ -387,11 +387,11 @@ describe('Feature: save annotations', () => { zOrder: 0, }); - expect(await job.annotations.hasUnsavedChanges()).toBe(false); + expect(job.annotations.hasUnsavedChanges()).toBe(false); await job.annotations.put([state]); - expect(await job.annotations.hasUnsavedChanges()).toBe(true); + expect(job.annotations.hasUnsavedChanges()).toBe(true); await job.annotations.save(); - expect(await job.annotations.hasUnsavedChanges()).toBe(false); + expect(job.annotations.hasUnsavedChanges()).toBe(false); annotations = await job.annotations.get(0); expect(annotations).toHaveLength(length + 1); }); @@ -400,23 +400,23 @@ describe('Feature: save annotations', () => { const job = (await window.cvat.jobs.get({ jobID: 100 }))[0]; const annotations = await job.annotations.get(0); - expect(await job.annotations.hasUnsavedChanges()).toBe(false); + expect(job.annotations.hasUnsavedChanges()).toBe(false); annotations[0].points = [0, 100, 200, 300]; await annotations[0].save(); - expect(await job.annotations.hasUnsavedChanges()).toBe(true); + expect(job.annotations.hasUnsavedChanges()).toBe(true); await job.annotations.save(); - expect(await job.annotations.hasUnsavedChanges()).toBe(false); + expect(job.annotations.hasUnsavedChanges()).toBe(false); }); test('delete & save annotations for a job', async () => { const job = (await window.cvat.jobs.get({ jobID: 100 }))[0]; const annotations = await job.annotations.get(0); - expect(await job.annotations.hasUnsavedChanges()).toBe(false); + expect(job.annotations.hasUnsavedChanges()).toBe(false); await annotations[0].delete(); - expect(await job.annotations.hasUnsavedChanges()).toBe(true); + expect(job.annotations.hasUnsavedChanges()).toBe(true); await job.annotations.save(); - expect(await job.annotations.hasUnsavedChanges()).toBe(false); + expect(job.annotations.hasUnsavedChanges()).toBe(false); }); test('delete & save annotations for a job when there are a track and a shape with the same id', async () => { @@ -658,11 +658,11 @@ describe('Feature: clear annotations', () => { expect(annotations.length).not.toBe(0); annotations[0].occluded = true; await annotations[0].save(); - expect(await task.annotations.hasUnsavedChanges()).toBe(true); + expect(task.annotations.hasUnsavedChanges()).toBe(true); await task.annotations.clear(true); annotations = await task.annotations.get(0); expect(annotations.length).not.toBe(0); - expect(await task.annotations.hasUnsavedChanges()).toBe(false); + expect(task.annotations.hasUnsavedChanges()).toBe(false); }); test('clear annotations with reload in a job', async () => { @@ -671,11 +671,11 @@ describe('Feature: clear annotations', () => { expect(annotations.length).not.toBe(0); annotations[0].occluded = true; await annotations[0].save(); - expect(await job.annotations.hasUnsavedChanges()).toBe(true); + expect(job.annotations.hasUnsavedChanges()).toBe(true); await job.annotations.clear(true); annotations = await job.annotations.get(0); expect(annotations.length).not.toBe(0); - expect(await job.annotations.hasUnsavedChanges()).toBe(false); + expect(job.annotations.hasUnsavedChanges()).toBe(false); }); test('clear annotations with bad reload parameter', async () => { diff --git a/cvat-ui/src/actions/annotation-actions.ts b/cvat-ui/src/actions/annotation-actions.ts index 8459c0145902..2279da3d6344 100644 --- a/cvat-ui/src/actions/annotation-actions.ts +++ b/cvat-ui/src/actions/annotation-actions.ts @@ -12,6 +12,7 @@ import { ShapeType, ObjectType, Task, + FrameSpeed, } from 'reducers/interfaces'; import getCore from 'cvat-core'; @@ -27,7 +28,8 @@ function getStore(): Store { return store; } -function receiveAnnotationsParameters(): { filters: string[]; frame: number } { +function receiveAnnotationsParameters(): +{ filters: string[]; frame: number; showAllInterpolationTracks: boolean } { if (store === null) { store = getCVATStore(); } @@ -35,10 +37,11 @@ function receiveAnnotationsParameters(): { filters: string[]; frame: number } { const state: CombinedState = getStore().getState(); const { filters } = state.annotation.annotations; const frame = state.annotation.player.frame.number; - + const { showAllInterpolationTracks } = state.settings.workspace; return { filters, frame, + showAllInterpolationTracks, }; } @@ -57,6 +60,7 @@ export enum AnnotationActionTypes { GET_JOB = 'GET_JOB', GET_JOB_SUCCESS = 'GET_JOB_SUCCESS', GET_JOB_FAILED = 'GET_JOB_FAILED', + CLOSE_JOB = 'CLOSE_JOB', CHANGE_FRAME = 'CHANGE_FRAME', CHANGE_FRAME_SUCCESS = 'CHANGE_FRAME_SUCCESS', CHANGE_FRAME_FAILED = 'CHANGE_FRAME_FAILED', @@ -143,8 +147,9 @@ export function fetchAnnotationsAsync(sessionInstance: any): ThunkAction, {}, {}, AnyAction> { return async (dispatch: ActionCreator): Promise => { try { - const { filters, frame } = receiveAnnotationsParameters(); - const states = await sessionInstance.annotations.get(frame, false, filters); + const { filters, frame, showAllInterpolationTracks } = receiveAnnotationsParameters(); + const states = await sessionInstance.annotations + .get(frame, showAllInterpolationTracks, filters); const [minZ, maxZ] = computeZRange(states); dispatch({ @@ -179,12 +184,13 @@ export function undoActionAsync(sessionInstance: any, frame: number): ThunkAction, {}, {}, AnyAction> { return async (dispatch: ActionCreator): Promise => { try { - const { filters } = receiveAnnotationsParameters(); + const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters(); // TODO: use affected IDs as an optimization await sessionInstance.actions.undo(); const history = await sessionInstance.actions.get(); - const states = await sessionInstance.annotations.get(frame, false, filters); + const states = await sessionInstance.annotations + .get(frame, showAllInterpolationTracks, filters); const [minZ, maxZ] = computeZRange(states); dispatch({ @@ -211,12 +217,13 @@ export function redoActionAsync(sessionInstance: any, frame: number): ThunkAction, {}, {}, AnyAction> { return async (dispatch: ActionCreator): Promise => { try { - const { filters } = receiveAnnotationsParameters(); + const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters(); // TODO: use affected IDs as an optimization await sessionInstance.actions.redo(); const history = await sessionInstance.actions.get(); - const states = await sessionInstance.annotations.get(frame, false, filters); + const states = await sessionInstance.annotations + .get(frame, showAllInterpolationTracks, filters); const [minZ, maxZ] = computeZRange(states); dispatch({ @@ -280,7 +287,7 @@ ThunkAction, {}, {}, AnyAction> { return async (dispatch: ActionCreator): Promise => { try { const state: CombinedState = getStore().getState(); - const { filters } = receiveAnnotationsParameters(); + const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters(); if (state.tasks.activities.loads[job.task.id]) { throw Error('Annotations is being uploaded for the task'); @@ -314,7 +321,7 @@ ThunkAction, {}, {}, AnyAction> { await job.annotations.clear(true); await job.actions.clear(); const history = await job.actions.get(); - const states = await job.annotations.get(frame, false, filters); + const states = await job.annotations.get(frame, showAllInterpolationTracks, filters); setTimeout(() => { dispatch({ @@ -422,6 +429,7 @@ export function propagateObjectAsync( ? objectState.objectType : ObjectType.SHAPE, shapeType: objectState.shapeType, label: objectState.label, + zOrder: objectState.zOrder, frame: from, }; @@ -583,7 +591,7 @@ ThunkAction, {}, {}, AnyAction> { return async (dispatch: ActionCreator): Promise => { const state: CombinedState = getStore().getState(); const { instance: job } = state.annotation.job; - const { filters, frame } = receiveAnnotationsParameters(); + const { filters, frame, showAllInterpolationTracks } = receiveAnnotationsParameters(); try { if (toFrame < job.startFrame || toFrame > job.stopFrame) { @@ -610,8 +618,25 @@ ThunkAction, {}, {}, AnyAction> { }); const data = await job.frames.get(toFrame); - const states = await job.annotations.get(toFrame, false, filters); + const states = await job.annotations.get(toFrame, showAllInterpolationTracks, filters); const [minZ, maxZ] = computeZRange(states); + const currentTime = new Date().getTime(); + let frameSpeed; + switch (state.settings.player.frameSpeed) { + case (FrameSpeed.Fast): { + frameSpeed = (FrameSpeed.Fast as number) / 2; + break; + } + case (FrameSpeed.Fastest): { + frameSpeed = (FrameSpeed.Fastest as number) / 3; + break; + } + default: { + frameSpeed = state.settings.player.frameSpeed as number; + } + } + const delay = Math.max(0, Math.round(1000 / frameSpeed) + - currentTime + (state.annotation.player.frame.changeTime as number)); dispatch({ type: AnnotationActionTypes.CHANGE_FRAME_SUCCESS, payload: { @@ -620,6 +645,8 @@ ThunkAction, {}, {}, AnyAction> { states, minZ, maxZ, + changeTime: currentTime + delay, + delay, }, }); } catch (error) { @@ -681,8 +708,16 @@ export function getJobAsync( try { const state: CombinedState = getStore().getState(); const filters = initialFilters; + const { showAllInterpolationTracks } = state.settings.workspace; + + // Check if already loaded job is different from asking one + if (state.annotation.job.instance && state.annotation.job.instance.id !== jid) { + dispatch({ + type: AnnotationActionTypes.CLOSE_JOB, + }); + } - // First check state if the task is already there + // Check state if the task is already there let task = state.tasks.current .filter((_task: Task) => _task.instance.id === tid) .map((_task: Task) => _task.instance)[0]; @@ -701,7 +736,8 @@ export function getJobAsync( const frameNumber = Math.max(Math.min(job.stopFrame, initialFrame), job.startFrame); const frameData = await job.frames.get(frameNumber); - const states = await job.annotations.get(frameNumber, false, filters); + const states = await job.annotations + .get(frameNumber, showAllInterpolationTracks, filters); const [minZ, maxZ] = computeZRange(states); const colors = [...cvat.enums.colors]; @@ -845,8 +881,9 @@ ThunkAction, {}, {}, AnyAction> { }, }); } catch (error) { - const { filters } = receiveAnnotationsParameters(); - const states = await sessionInstance.annotations.get(frame, false, filters); + const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters(); + const states = await sessionInstance.annotations + .get(frame, showAllInterpolationTracks, filters); dispatch({ type: AnnotationActionTypes.UPDATE_ANNOTATIONS_FAILED, payload: { @@ -862,9 +899,10 @@ export function createAnnotationsAsync(sessionInstance: any, frame: number, stat ThunkAction, {}, {}, AnyAction> { return async (dispatch: ActionCreator): Promise => { try { - const { filters } = receiveAnnotationsParameters(); + const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters(); await sessionInstance.annotations.put(statesToCreate); - const states = await sessionInstance.annotations.get(frame, false, filters); + const states = await sessionInstance.annotations + .get(frame, showAllInterpolationTracks, filters); const history = await sessionInstance.actions.get(); dispatch({ @@ -889,9 +927,10 @@ export function mergeAnnotationsAsync(sessionInstance: any, frame: number, state ThunkAction, {}, {}, AnyAction> { return async (dispatch: ActionCreator): Promise => { try { - const { filters } = receiveAnnotationsParameters(); + const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters(); await sessionInstance.annotations.merge(statesToMerge); - const states = await sessionInstance.annotations.get(frame, false, filters); + const states = await sessionInstance.annotations + .get(frame, showAllInterpolationTracks, filters); const history = await sessionInstance.actions.get(); dispatch({ @@ -916,9 +955,10 @@ export function groupAnnotationsAsync(sessionInstance: any, frame: number, state ThunkAction, {}, {}, AnyAction> { return async (dispatch: ActionCreator): Promise => { try { - const { filters } = receiveAnnotationsParameters(); + const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters(); await sessionInstance.annotations.group(statesToGroup); - const states = await sessionInstance.annotations.get(frame, false, filters); + const states = await sessionInstance.annotations + .get(frame, showAllInterpolationTracks, filters); const history = await sessionInstance.actions.get(); dispatch({ @@ -942,10 +982,11 @@ ThunkAction, {}, {}, AnyAction> { export function splitAnnotationsAsync(sessionInstance: any, frame: number, stateToSplit: any): ThunkAction, {}, {}, AnyAction> { return async (dispatch: ActionCreator): Promise => { - const { filters } = receiveAnnotationsParameters(); + const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters(); try { await sessionInstance.annotations.split(stateToSplit, frame); - const states = await sessionInstance.annotations.get(frame, false, filters); + const states = await sessionInstance.annotations + .get(frame, showAllInterpolationTracks, filters); const history = await sessionInstance.actions.get(); dispatch({ @@ -974,10 +1015,11 @@ export function changeLabelColorAsync( ): ThunkAction, {}, {}, AnyAction> { return async (dispatch: ActionCreator): Promise => { try { - const { filters } = receiveAnnotationsParameters(); + const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters(); const updatedLabel = label; updatedLabel.color = color; - const states = await sessionInstance.annotations.get(frameNumber, false, filters); + const states = await sessionInstance.annotations + .get(frameNumber, showAllInterpolationTracks, filters); const history = await sessionInstance.actions.get(); dispatch({ diff --git a/cvat-ui/src/actions/settings-actions.ts b/cvat-ui/src/actions/settings-actions.ts index e1614cdfb421..f2de7dcb3ac6 100644 --- a/cvat-ui/src/actions/settings-actions.ts +++ b/cvat-ui/src/actions/settings-actions.ts @@ -14,6 +14,16 @@ export enum SettingsActionTypes { CHANGE_SELECTED_SHAPES_OPACITY = 'CHANGE_SELECTED_SHAPES_OPACITY', CHANGE_SHAPES_COLOR_BY = 'CHANGE_SHAPES_COLOR_BY', CHANGE_SHAPES_BLACK_BORDERS = 'CHANGE_SHAPES_BLACK_BORDERS', + CHANGE_FRAME_STEP = 'CHANGE_FRAME_STEP', + CHANGE_FRAME_SPEED = 'CHANGE_FRAME_SPEED', + SWITCH_RESET_ZOOM = 'SWITCH_RESET_ZOOM', + CHANGE_BRIGHTNESS_LEVEL = 'CHANGE_BRIGHTNESS_LEVEL', + CHANGE_CONTRAST_LEVEL = 'CHANGE_CONTRAST_LEVEL', + CHANGE_SATURATION_LEVEL = 'CHANGE_SATURATION_LEVEL', + SWITCH_AUTO_SAVE = 'SWITCH_AUTO_SAVE', + CHANGE_AUTO_SAVE_INTERVAL = 'CHANGE_AUTO_SAVE_INTERVAL', + CHANGE_AAM_ZOOM_MARGIN = 'CHANGE_AAM_ZOOM_MARGIN', + SWITCH_SHOWNIG_INTERPOLATED_TRACKS = 'SWITCH_SHOWNIG_INTERPOLATED_TRACKS', } export function changeShapesOpacity(opacity: number): AnyAction { @@ -96,3 +106,93 @@ export function changeGridOpacity(gridOpacity: number): AnyAction { }, }; } + +export function changeFrameStep(frameStep: number): AnyAction { + return { + type: SettingsActionTypes.CHANGE_FRAME_STEP, + payload: { + frameStep, + }, + }; +} + +export function changeFrameSpeed(frameSpeed: number): AnyAction { + return { + type: SettingsActionTypes.CHANGE_FRAME_SPEED, + payload: { + frameSpeed, + }, + }; +} + +export function switchResetZoom(resetZoom: boolean): AnyAction { + return { + type: SettingsActionTypes.SWITCH_RESET_ZOOM, + payload: { + resetZoom, + }, + }; +} + +export function changeBrightnessLevel(level: number): AnyAction { + return { + type: SettingsActionTypes.CHANGE_BRIGHTNESS_LEVEL, + payload: { + level, + }, + }; +} + +export function changeContrastLevel(level: number): AnyAction { + return { + type: SettingsActionTypes.CHANGE_CONTRAST_LEVEL, + payload: { + level, + }, + }; +} + +export function changeSaturationLevel(level: number): AnyAction { + return { + type: SettingsActionTypes.CHANGE_SATURATION_LEVEL, + payload: { + level, + }, + }; +} + +export function switchAutoSave(autoSave: boolean): AnyAction { + return { + type: SettingsActionTypes.SWITCH_AUTO_SAVE, + payload: { + autoSave, + }, + }; +} + +export function changeAutoSaveInterval(autoSaveInterval: number): AnyAction { + return { + type: SettingsActionTypes.CHANGE_AUTO_SAVE_INTERVAL, + payload: { + autoSaveInterval, + }, + }; +} + +export function changeAAMZoomMargin(aamZoomMargin: number): AnyAction { + return { + type: SettingsActionTypes.CHANGE_AAM_ZOOM_MARGIN, + payload: { + aamZoomMargin, + }, + }; +} + +export function switchShowingInterpolatedTracks(showAllInterpolationTracks: boolean): AnyAction { + return { + type: SettingsActionTypes.SWITCH_SHOWNIG_INTERPOLATED_TRACKS, + payload: { + showAllInterpolationTracks, + }, + }; +} diff --git a/cvat-ui/src/components/annotation-page/annotation-page.tsx b/cvat-ui/src/components/annotation-page/annotation-page.tsx index 82c5bcff8353..d0313fc8fdcf 100644 --- a/cvat-ui/src/components/annotation-page/annotation-page.tsx +++ b/cvat-ui/src/components/annotation-page/annotation-page.tsx @@ -17,6 +17,7 @@ interface Props { getJob(): void; } + export default function AnnotationPageComponent(props: Props): JSX.Element { const { job, @@ -24,6 +25,7 @@ export default function AnnotationPageComponent(props: Props): JSX.Element { getJob, } = props; + if (job === null) { if (!fetching) { getJob(); diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx index 3bcf6a596e4d..ae69dd1744cd 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx @@ -47,6 +47,10 @@ interface Props { curZLayer: number; minZLayer: number; maxZLayer: number; + brightnessLevel: number; + contrastLevel: number; + saturationLevel: number; + resetZoom: boolean; onSetupCanvas: () => void; onDragCanvas: (enabled: boolean) => void; onZoomCanvas: (enabled: boolean) => void; @@ -102,6 +106,7 @@ export default class CanvasWrapperComponent extends React.PureComponent { sidebarCollapsed, activatedStateID, curZLayer, + resetZoom, } = this.props; if (prevProps.sidebarCollapsed !== sidebarCollapsed) { @@ -156,6 +161,12 @@ export default class CanvasWrapperComponent extends React.PureComponent { this.updateShapesView(); } + if (prevProps.frame !== frameData.number && resetZoom) { + canvasInstance.html().addEventListener('canvas.setup', () => { + canvasInstance.fit(); + }, { once: true }); + } + if (prevProps.curZLayer !== curZLayer) { canvasInstance.setZLayer(curZLayer); } @@ -339,6 +350,9 @@ export default class CanvasWrapperComponent extends React.PureComponent { onActivateObject, onUpdateContextMenu, onEditShape, + brightnessLevel, + contrastLevel, + saturationLevel, } = this.props; // Size @@ -357,6 +371,12 @@ export default class CanvasWrapperComponent extends React.PureComponent { } canvasInstance.grid(gridSize, gridSize); + // Filters + const backgroundElement = window.document.getElementById('cvat_canvas_background'); + if (backgroundElement) { + backgroundElement.style.filter = `brightness(${brightnessLevel / 100}) contrast(${contrastLevel / 100}) saturate(${saturationLevel / 100})`; + } + // Events canvasInstance.html().addEventListener('mousedown', (e: MouseEvent): void => { const { diff --git a/cvat-ui/src/components/cvat-app.tsx b/cvat-ui/src/components/cvat-app.tsx index ec65c8294a58..76906a5e73d0 100644 --- a/cvat-ui/src/components/cvat-app.tsx +++ b/cvat-ui/src/components/cvat-app.tsx @@ -13,7 +13,7 @@ import { notification, } from 'antd'; -import SettingsPageComponent from 'components/settings-page/settings-page'; +import SettingsPageContainer from 'containers/settings-page/settings-page'; import TasksPageContainer from 'containers/tasks-page/tasks-page'; import CreateTaskPageContainer from 'containers/create-task-page/create-task-page'; import TaskPageContainer from 'containers/task-page/task-page'; @@ -204,7 +204,7 @@ export default class CVATApplication extends React.PureComponent { - + diff --git a/cvat-ui/src/components/settings-page/player-settings.tsx b/cvat-ui/src/components/settings-page/player-settings.tsx index 1e419679a11a..9c416f29a638 100644 --- a/cvat-ui/src/components/settings-page/player-settings.tsx +++ b/cvat-ui/src/components/settings-page/player-settings.tsx @@ -61,11 +61,17 @@ export default function PlayerSettingsComponent(props: Props): JSX.Element { brightnessLevel, contrastLevel, saturationLevel, + onChangeFrameStep, + onChangeFrameSpeed, + onSwitchResetZoom, onSwitchRotateAll, onSwitchGrid, onChangeGridSize, onChangeGridColor, onChangeGridOpacity, + onChangeBrightnessLevel, + onChangeContrastLevel, + onChangeSaturationLevel, } = props; return ( @@ -73,7 +79,16 @@ export default function PlayerSettingsComponent(props: Props): JSX.Element { Player step - + { + if (value) { + onChangeFrameStep(value); + } + }} + /> @@ -87,7 +102,12 @@ export default function PlayerSettingsComponent(props: Props): JSX.Element { Player speed - { + onChangeFrameSpeed(speed); + }} + > Fastest Fast Usual @@ -118,6 +138,7 @@ export default function PlayerSettingsComponent(props: Props): JSX.Element { max={1000} step={1} value={gridSize} + disabled={!grid} onChange={(value: number | undefined): void => { if (value) { onChangeGridSize(value); @@ -129,6 +150,7 @@ export default function PlayerSettingsComponent(props: Props): JSX.Element { Grid color