diff --git a/cvat-canvas/src/typescript/editHandler.ts b/cvat-canvas/src/typescript/editHandler.ts index defdb69e..ba0ed7f6 100644 --- a/cvat-canvas/src/typescript/editHandler.ts +++ b/cvat-canvas/src/typescript/editHandler.ts @@ -96,15 +96,21 @@ export class EditHandlerImpl implements EditHandler { let mouseY: number | null = null; this.canvas.on('mousedown.edit', (e: MouseEvent): void => { - if (e.which === 1) { + if (e.button === 0) { mouseX = e.clientX; mouseY = e.clientY; + } else if (e.button === 2 && this.editLine) { + if (this.editData.state.shapeType === 'points' + || this.editLine.attr('points').split(' ').length > 2 + ) { + (this.editLine as any).draw('undo'); + } } }); this.canvas.on('mouseup.edit', (e: MouseEvent): void => { const threshold = 10; // px - if (e.which === 1) { + if (e.button === 0) { if (Math.sqrt( // l2 distance < threshold ((mouseX - e.clientX) ** 2) + ((mouseY - e.clientY) ** 2), diff --git a/cvat-core/src/annotations-collection.js b/cvat-core/src/annotations-collection.js index 309d387d..2135780c 100644 --- a/cvat-core/src/annotations-collection.js +++ b/cvat-core/src/annotations-collection.js @@ -317,7 +317,7 @@ // Push outside shape after each annotation shape // Any not outside shape rewrites it - if (!((object.frame + 1) in keyframes)) { + if (!((object.frame + 1) in keyframes) && object.frame + 1 <= this.stopFrame) { keyframes[object.frame + 1] = JSON .parse(JSON.stringify(keyframes[object.frame])); keyframes[object.frame + 1].outside = true; diff --git a/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/attribute-annotation-sidebar/attribute-editor.tsx b/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/attribute-annotation-sidebar/attribute-editor.tsx index 5b9150a2..de8a6222 100644 --- a/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/attribute-annotation-sidebar/attribute-editor.tsx +++ b/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/attribute-annotation-sidebar/attribute-editor.tsx @@ -9,7 +9,6 @@ import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox'; import Select, { SelectValue } from 'antd/lib/select'; import Radio, { RadioChangeEvent } from 'antd/lib/radio'; import Input from 'antd/lib/input'; -import InputNumber from 'antd/lib/input-number'; interface InputElementParameters { attrID: number; @@ -17,7 +16,6 @@ interface InputElementParameters { values: string[]; currentValue: string; onChange(value: string): void; - ref: React.RefObject; } function renderInputElement(parameters: InputElementParameters): JSX.Element { @@ -27,7 +25,6 @@ function renderInputElement(parameters: InputElementParameters): JSX.Element { values, currentValue, onChange, - ref, } = parameters; const renderCheckbox = (): JSX.Element => ( @@ -114,7 +111,6 @@ function renderInputElement(parameters: InputElementParameters): JSX.Element { } }} onKeyDown={handleKeydown} - ref={ref as React.RefObject} /> @@ -259,8 +255,6 @@ interface Props { function AttributeEditor(props: Props): JSX.Element { const { attribute, currentValue, onChange } = props; const { inputType, values, id: attrID } = attribute; - const ref = inputType === 'number' ? React.createRef() - : React.createRef(); return (
@@ -268,7 +262,6 @@ function AttributeEditor(props: Props): JSX.Element {
{renderInputElement({ attrID, - ref, inputType, currentValue, values, diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover.tsx index 9ee0f8a9..73786bc3 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover.tsx @@ -4,20 +4,16 @@ import React from 'react'; -import { - Row, - Col, - Select, - Button, - InputNumber, - Radio, -} from 'antd'; - -import { RadioChangeEvent } from 'antd/lib/radio'; +import { Row, Col } from 'antd/lib/grid'; +import Select from 'antd/lib/select'; +import Button from 'antd/lib/button'; +import InputNumber from 'antd/lib/input-number'; +import Radio, { RadioChangeEvent } from 'antd/lib/radio'; import Text from 'antd/lib/typography/Text'; import { RectDrawingMethod } from 'cvat-canvas'; import { ShapeType } from 'reducers/interfaces'; +import { clamp } from 'utils/math'; interface Props { shapeType: ShapeType; @@ -117,7 +113,15 @@ function DrawShapePopoverComponent(props: Props): JSX.Element { { + if (typeof (value) === 'number') { + onChangePoints(Math.floor( + clamp(value, minimumPoints, Number.MAX_SAFE_INTEGER), + )); + } else if (!value) { + onChangePoints(undefined); + } + }} className='cvat-draw-shape-popover-points-selector' min={minimumPoints} value={numberOfPoints} diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx index dc7952cf..12ccd044 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx @@ -4,26 +4,21 @@ import React from 'react'; -import { - Row, - Col, - Icon, - Select, - Radio, - Input, - Collapse, - Checkbox, - InputNumber, - Dropdown, - Menu, - Button, - Modal, - Popover, -} from 'antd'; - +import { Row, Col } from 'antd/lib/grid'; +import Icon from 'antd/lib/icon'; +import Select from 'antd/lib/select'; +import Radio, { RadioChangeEvent } from 'antd/lib/radio'; +import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox'; +import Input from 'antd/lib/input'; +import InputNumber from 'antd/lib/input-number'; +import Collapse from 'antd/lib/collapse'; +import Dropdown from 'antd/lib/dropdown'; +import Menu from 'antd/lib/menu'; +import Button from 'antd/lib/button'; +import Modal from 'antd/lib/modal'; +import Popover from 'antd/lib/popover'; import Text from 'antd/lib/typography/Text'; -import { RadioChangeEvent } from 'antd/lib/radio'; -import { CheckboxChangeEvent } from 'antd/lib/checkbox'; + import ColorChanger from 'components/annotation-page/standard-workspace/objects-side-bar/color-changer'; import { @@ -36,9 +31,8 @@ import { ForegroundIcon, } from 'icons'; -import { - ObjectType, ShapeType, -} from 'reducers/interfaces'; +import { ObjectType, ShapeType } from 'reducers/interfaces'; +import { clamp } from 'utils/math'; function ItemMenu( serverID: number | undefined, @@ -463,7 +457,7 @@ function ItemAttributeComponent(props: ItemAttributeComponentProps): JSX.Element } if (attrInputType === 'number') { - const [min, max, step] = attrValues; + const [min, max, step] = attrValues.map((value: string): number => +value); return ( <> @@ -476,15 +470,17 @@ function ItemAttributeComponent(props: ItemAttributeComponentProps): JSX.Element { - if (typeof (value) !== 'undefined') { - changeAttribute(attrID, `${value}`); + if (typeof (value) === 'number') { + changeAttribute( + attrID, `${clamp(value, min, max)}`, + ); } }} value={+attrValue} className='cvat-object-item-number-attribute' - min={+min} - max={+max} - step={+step} + min={min} + max={max} + step={step} /> diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/propagate-confirm.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/propagate-confirm.tsx index f47d5b12..66bebffa 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/propagate-confirm.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/propagate-confirm.tsx @@ -4,21 +4,21 @@ import React from 'react'; -import { - Modal, - InputNumber, -} from 'antd'; - +import Modal from 'antd/lib/modal'; +import InputNumber from 'antd/lib/input-number'; import Text from 'antd/lib/typography/Text'; +import { clamp } from 'utils/math'; interface Props { visible: boolean; propagateFrames: number; propagateUpToFrame: number; + stopFrame: number; + frameNumber: number; propagateObject(): void; cancel(): void; - changePropagateFrames(value: number | undefined): void; - changeUpToFrame(value: number | undefined): void; + changePropagateFrames(value: number): void; + changeUpToFrame(value: number): void; } export default function PropagateConfirmComponent(props: Props): JSX.Element { @@ -26,12 +26,16 @@ export default function PropagateConfirmComponent(props: Props): JSX.Element { visible, propagateFrames, propagateUpToFrame, + stopFrame, + frameNumber, propagateObject, changePropagateFrames, changeUpToFrame, cancel, } = props; + const minPropagateFrames = 1; + return (
Do you want to make a copy of the object on - + { + if (typeof (value) === 'number') { + changePropagateFrames(Math.floor( + clamp(value, minPropagateFrames, Number.MAX_SAFE_INTEGER), + )); + } + }} + /> { propagateFrames > 1 ? frames : frame } up to the - + { + if (typeof (value) === 'number') { + changeUpToFrame(Math.floor( + clamp(value, frameNumber + 1, stopFrame), + )); + } + }} + /> frame
diff --git a/cvat-ui/src/components/annotation-page/top-bar/player-navigation.tsx b/cvat-ui/src/components/annotation-page/top-bar/player-navigation.tsx index 397bf8d6..b5c846d0 100644 --- a/cvat-ui/src/components/annotation-page/top-bar/player-navigation.tsx +++ b/cvat-ui/src/components/annotation-page/top-bar/player-navigation.tsx @@ -2,27 +2,24 @@ // // SPDX-License-Identifier: MIT -import React from 'react'; +import React, { useState, useEffect } from 'react'; -import { - Row, - Col, - Icon, - Slider, - Tooltip, - InputNumber, -} from 'antd'; - -import { SliderValue } from 'antd/lib/slider'; +import { Row, Col } from 'antd/lib/grid'; +import Icon from 'antd/lib/icon'; +import Slider, { SliderValue } from 'antd/lib/slider'; +import Tooltip from 'antd/lib/tooltip'; +import InputNumber from 'antd/lib/input-number'; import Text from 'antd/lib/typography/Text'; +import { clamp } from 'utils/math'; + interface Props { startFrame: number; stopFrame: number; frameNumber: number; inputFrameRef: React.RefObject; onSliderChange(value: SliderValue): void; - onInputChange(value: number | undefined): void; + onInputChange(value: number): void; onURLIconClick(): void; } @@ -37,6 +34,14 @@ function PlayerNavigation(props: Props): JSX.Element { onURLIconClick, } = props; + const [frameInputValue, setFrameInputValue] = useState(frameNumber); + + useEffect(() => { + if (frameNumber !== frameInputValue) { + setFrameInputValue(frameNumber); + } + }, [frameNumber]); + return ( <> @@ -68,9 +73,21 @@ function PlayerNavigation(props: Props): JSX.Element { { + if (typeof (value) === 'number') { + setFrameInputValue(Math.floor( + clamp(value, startFrame, stopFrame), + )); + } + }} + onBlur={() => { + onInputChange(frameInputValue); + }} + onPressEnter={() => { + onInputChange(frameInputValue); + }} ref={inputFrameRef} /> diff --git a/cvat-ui/src/components/annotation-page/top-bar/top-bar.tsx b/cvat-ui/src/components/annotation-page/top-bar/top-bar.tsx index 370702fe..53648454 100644 --- a/cvat-ui/src/components/annotation-page/top-bar/top-bar.tsx +++ b/cvat-ui/src/components/annotation-page/top-bar/top-bar.tsx @@ -4,12 +4,8 @@ import React from 'react'; -import { - Row, - Col, - InputNumber, -} from 'antd'; - +import { Row, Col } from 'antd/lib/grid'; +import InputNumber from 'antd/lib/input-number'; import { SliderValue } from 'antd/lib/slider'; import { Workspace } from 'reducers/interfaces'; @@ -40,7 +36,7 @@ interface Props { onFirstFrame(): void; onLastFrame(): void; onSliderChange(value: SliderValue): void; - onInputChange(value: number | undefined): void; + onInputChange(value: number): void; onURLIconClick(): void; onUndoClick(): void; onRedoClick(): void; diff --git a/cvat-ui/src/components/settings-page/player-settings.tsx b/cvat-ui/src/components/settings-page/player-settings.tsx index 73f2a00a..779a03b0 100644 --- a/cvat-ui/src/components/settings-page/player-settings.tsx +++ b/cvat-ui/src/components/settings-page/player-settings.tsx @@ -4,28 +4,17 @@ import React from 'react'; -import { - Row, - Col, - Checkbox, - Slider, - Select, - InputNumber, - Icon, -} from 'antd'; - +import { Row, Col } from 'antd/lib/grid'; +import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox'; +import Slider from 'antd/lib/slider'; +import Select from 'antd/lib/select'; +import InputNumber from 'antd/lib/input-number'; +import Icon from 'antd/lib/icon'; import Text from 'antd/lib/typography/Text'; -import { CheckboxChangeEvent } from 'antd/lib/checkbox'; - -import { - BackJumpIcon, - ForwardJumpIcon, -} from 'icons'; -import { - FrameSpeed, - GridColor, -} from 'reducers/interfaces'; +import { clamp } from 'utils/math'; +import { BackJumpIcon, ForwardJumpIcon } from 'icons'; +import { FrameSpeed, GridColor } from 'reducers/interfaces'; interface Props { frameStep: number; @@ -78,18 +67,28 @@ export default function PlayerSettingsComponent(props: Props): JSX.Element { onChangeSaturationLevel, } = props; + const minFrameStep = 2; + const maxFrameStep = 1000; + const minGridSize = 5; + const maxGridSize = 1000; + + return (
Player step { - if (value) { - onChangeFrameStep(value); + if (typeof (value) === 'number') { + onChangeFrameStep( + Math.floor( + clamp(value, minFrameStep, maxFrameStep), + ), + ); } }} /> @@ -138,14 +137,15 @@ export default function PlayerSettingsComponent(props: Props): JSX.Element { Grid size { - if (value) { - onChangeGridSize(value); + if (typeof (value) === 'number') { + onChangeGridSize(Math.floor( + clamp(value, minGridSize, maxGridSize), + )); } }} /> diff --git a/cvat-ui/src/components/settings-page/workspace-settings.tsx b/cvat-ui/src/components/settings-page/workspace-settings.tsx index a7a5fd2c..52ba4a50 100644 --- a/cvat-ui/src/components/settings-page/workspace-settings.tsx +++ b/cvat-ui/src/components/settings-page/workspace-settings.tsx @@ -4,15 +4,12 @@ import React from 'react'; -import { - Row, - Col, - Checkbox, - InputNumber, -} from 'antd'; - +import { Row, Col } from 'antd/lib/grid'; +import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox'; +import InputNumber from 'antd/lib/input-number'; import Text from 'antd/lib/typography/Text'; -import { CheckboxChangeEvent } from 'antd/lib/checkbox'; + +import { clamp } from 'utils/math'; interface Props { autoSave: boolean; @@ -37,6 +34,11 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element { onSwitchShowingInterpolatedTracks, } = props; + const minAutoSaveInterval = 5; + const maxAutoSaveInterval = 60; + const minAAMMargin = 0; + const maxAAMMargin = 1000; + return (
@@ -56,13 +58,19 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element { Auto save every { - if (value) { - onChangeAutoSaveInterval(value * 60 * 1000); + if (typeof (value) === 'number') { + onChangeAutoSaveInterval(Math.floor( + clamp( + value, + minAutoSaveInterval, + maxAutoSaveInterval, + ), + ) * 60 * 1000); } }} /> @@ -89,12 +97,14 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element { Attribute annotation mode (AAM) zoom margin { if (typeof (value) === 'number') { - onChangeAAMZoomMargin(value); + onChangeAAMZoomMargin(Math.floor( + clamp(value, minAAMMargin, maxAAMMargin), + )); } }} /> 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 868b1ddb..e92c0f26 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 @@ -145,15 +145,9 @@ class DrawShapePopoverContainer extends React.PureComponent { }; private onChangePoints = (value: number | undefined): void => { - if (typeof (value) === 'undefined') { - this.setState({ - numberOfPoints: value, - }); - } else if (typeof (value) === 'number') { - this.setState({ - numberOfPoints: Math.max(value, this.minimumPoints), - }); - } + this.setState({ + numberOfPoints: value, + }); }; private onChangeLabel = (value: string): void => { diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/propagate-confirm.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/propagate-confirm.tsx index e7c4d706..f0de8809 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/propagate-confirm.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/propagate-confirm.tsx @@ -88,23 +88,20 @@ class PropagateConfirmContainer extends React.PureComponent { propagateObject(jobInstance, objectState, frameNumber + 1, propagateUpToFrame); }; - private changePropagateFrames = (value: number | undefined): void => { + private changePropagateFrames = (value: number): void => { const { changePropagateFrames } = this.props; - if (typeof (value) !== 'undefined') { - changePropagateFrames(value); - } + changePropagateFrames(value); }; - private changeUpToFrame = (value: number | undefined): void => { + private changeUpToFrame = (value: number): void => { const { stopFrame, frameNumber, changePropagateFrames, } = this.props; - if (typeof (value) !== 'undefined') { - const propagateFrames = Math.max(0, Math.min(stopFrame, value)) - frameNumber; - changePropagateFrames(propagateFrames); - } + + const propagateFrames = Math.max(0, Math.min(stopFrame, value)) - frameNumber; + changePropagateFrames(propagateFrames); }; public render(): JSX.Element { @@ -122,6 +119,8 @@ class PropagateConfirmContainer extends React.PureComponent { { onChangeFrame(value as number); }; - private onChangePlayerInputValue = (value: number | undefined): void => { + private onChangePlayerInputValue = (value: number): void => { const { onSwitchPlay, onChangeFrame, playing, + frameNumber, } = this.props; - if (typeof (value) !== 'undefined') { + if (value !== frameNumber) { if (playing) { onSwitchPlay(false); } diff --git a/cvat-ui/src/utils/math.ts b/cvat-ui/src/utils/math.ts new file mode 100644 index 00000000..960bd4d1 --- /dev/null +++ b/cvat-ui/src/utils/math.ts @@ -0,0 +1,4 @@ +/* eslint-disable-next-line import/prefer-default-export */ +export function clamp(value: number, min: number, max: number): number { + return Math.max(Math.min(value, max), min); +}