Merge pull request #1314 from opencv/bs/batch_of_fixes

React UI: Batch of fixes
main
Dmitry Kalinin 6 years ago committed by GitHub
commit 3f4d6fc8d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -96,15 +96,21 @@ export class EditHandlerImpl implements EditHandler {
let mouseY: number | null = null; let mouseY: number | null = null;
this.canvas.on('mousedown.edit', (e: MouseEvent): void => { this.canvas.on('mousedown.edit', (e: MouseEvent): void => {
if (e.which === 1) { if (e.button === 0) {
mouseX = e.clientX; mouseX = e.clientX;
mouseY = e.clientY; 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 => { this.canvas.on('mouseup.edit', (e: MouseEvent): void => {
const threshold = 10; // px const threshold = 10; // px
if (e.which === 1) { if (e.button === 0) {
if (Math.sqrt( // l2 distance < threshold if (Math.sqrt( // l2 distance < threshold
((mouseX - e.clientX) ** 2) ((mouseX - e.clientX) ** 2)
+ ((mouseY - e.clientY) ** 2), + ((mouseY - e.clientY) ** 2),

@ -317,7 +317,7 @@
// Push outside shape after each annotation shape // Push outside shape after each annotation shape
// Any not outside shape rewrites it // 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 keyframes[object.frame + 1] = JSON
.parse(JSON.stringify(keyframes[object.frame])); .parse(JSON.stringify(keyframes[object.frame]));
keyframes[object.frame + 1].outside = true; keyframes[object.frame + 1].outside = true;

@ -9,7 +9,6 @@ import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox';
import Select, { SelectValue } from 'antd/lib/select'; import Select, { SelectValue } from 'antd/lib/select';
import Radio, { RadioChangeEvent } from 'antd/lib/radio'; import Radio, { RadioChangeEvent } from 'antd/lib/radio';
import Input from 'antd/lib/input'; import Input from 'antd/lib/input';
import InputNumber from 'antd/lib/input-number';
interface InputElementParameters { interface InputElementParameters {
attrID: number; attrID: number;
@ -17,7 +16,6 @@ interface InputElementParameters {
values: string[]; values: string[];
currentValue: string; currentValue: string;
onChange(value: string): void; onChange(value: string): void;
ref: React.RefObject<Input | InputNumber>;
} }
function renderInputElement(parameters: InputElementParameters): JSX.Element { function renderInputElement(parameters: InputElementParameters): JSX.Element {
@ -27,7 +25,6 @@ function renderInputElement(parameters: InputElementParameters): JSX.Element {
values, values,
currentValue, currentValue,
onChange, onChange,
ref,
} = parameters; } = parameters;
const renderCheckbox = (): JSX.Element => ( const renderCheckbox = (): JSX.Element => (
@ -114,7 +111,6 @@ function renderInputElement(parameters: InputElementParameters): JSX.Element {
} }
}} }}
onKeyDown={handleKeydown} onKeyDown={handleKeydown}
ref={ref as React.RefObject<Input>}
/> />
</div> </div>
</> </>
@ -259,8 +255,6 @@ interface Props {
function AttributeEditor(props: Props): JSX.Element { function AttributeEditor(props: Props): JSX.Element {
const { attribute, currentValue, onChange } = props; const { attribute, currentValue, onChange } = props;
const { inputType, values, id: attrID } = attribute; const { inputType, values, id: attrID } = attribute;
const ref = inputType === 'number' ? React.createRef<InputNumber>()
: React.createRef<Input>();
return ( return (
<div> <div>
@ -268,7 +262,6 @@ function AttributeEditor(props: Props): JSX.Element {
<hr /> <hr />
{renderInputElement({ {renderInputElement({
attrID, attrID,
ref,
inputType, inputType,
currentValue, currentValue,
values, values,

@ -4,20 +4,16 @@
import React from 'react'; import React from 'react';
import { import { Row, Col } from 'antd/lib/grid';
Row, import Select from 'antd/lib/select';
Col, import Button from 'antd/lib/button';
Select, import InputNumber from 'antd/lib/input-number';
Button, import Radio, { RadioChangeEvent } from 'antd/lib/radio';
InputNumber,
Radio,
} from 'antd';
import { RadioChangeEvent } from 'antd/lib/radio';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import { RectDrawingMethod } from 'cvat-canvas'; import { RectDrawingMethod } from 'cvat-canvas';
import { ShapeType } from 'reducers/interfaces'; import { ShapeType } from 'reducers/interfaces';
import { clamp } from 'utils/math';
interface Props { interface Props {
shapeType: ShapeType; shapeType: ShapeType;
@ -117,7 +113,15 @@ function DrawShapePopoverComponent(props: Props): JSX.Element {
</Col> </Col>
<Col span={10}> <Col span={10}>
<InputNumber <InputNumber
onChange={onChangePoints} onChange={(value: number | undefined) => {
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' className='cvat-draw-shape-popover-points-selector'
min={minimumPoints} min={minimumPoints}
value={numberOfPoints} value={numberOfPoints}

@ -4,26 +4,21 @@
import React from 'react'; import React from 'react';
import { import { Row, Col } from 'antd/lib/grid';
Row, import Icon from 'antd/lib/icon';
Col, import Select from 'antd/lib/select';
Icon, import Radio, { RadioChangeEvent } from 'antd/lib/radio';
Select, import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox';
Radio, import Input from 'antd/lib/input';
Input, import InputNumber from 'antd/lib/input-number';
Collapse, import Collapse from 'antd/lib/collapse';
Checkbox, import Dropdown from 'antd/lib/dropdown';
InputNumber, import Menu from 'antd/lib/menu';
Dropdown, import Button from 'antd/lib/button';
Menu, import Modal from 'antd/lib/modal';
Button, import Popover from 'antd/lib/popover';
Modal,
Popover,
} from 'antd';
import Text from 'antd/lib/typography/Text'; 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 ColorChanger from 'components/annotation-page/standard-workspace/objects-side-bar/color-changer';
import { import {
@ -36,9 +31,8 @@ import {
ForegroundIcon, ForegroundIcon,
} from 'icons'; } from 'icons';
import { import { ObjectType, ShapeType } from 'reducers/interfaces';
ObjectType, ShapeType, import { clamp } from 'utils/math';
} from 'reducers/interfaces';
function ItemMenu( function ItemMenu(
serverID: number | undefined, serverID: number | undefined,
@ -463,7 +457,7 @@ function ItemAttributeComponent(props: ItemAttributeComponentProps): JSX.Element
} }
if (attrInputType === 'number') { if (attrInputType === 'number') {
const [min, max, step] = attrValues; const [min, max, step] = attrValues.map((value: string): number => +value);
return ( return (
<> <>
@ -476,15 +470,17 @@ function ItemAttributeComponent(props: ItemAttributeComponentProps): JSX.Element
<InputNumber <InputNumber
size='small' size='small'
onChange={(value: number | undefined): void => { onChange={(value: number | undefined): void => {
if (typeof (value) !== 'undefined') { if (typeof (value) === 'number') {
changeAttribute(attrID, `${value}`); changeAttribute(
attrID, `${clamp(value, min, max)}`,
);
} }
}} }}
value={+attrValue} value={+attrValue}
className='cvat-object-item-number-attribute' className='cvat-object-item-number-attribute'
min={+min} min={min}
max={+max} max={max}
step={+step} step={step}
/> />
</Col> </Col>
</> </>

@ -4,21 +4,21 @@
import React from 'react'; import React from 'react';
import { import Modal from 'antd/lib/modal';
Modal, import InputNumber from 'antd/lib/input-number';
InputNumber,
} from 'antd';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import { clamp } from 'utils/math';
interface Props { interface Props {
visible: boolean; visible: boolean;
propagateFrames: number; propagateFrames: number;
propagateUpToFrame: number; propagateUpToFrame: number;
stopFrame: number;
frameNumber: number;
propagateObject(): void; propagateObject(): void;
cancel(): void; cancel(): void;
changePropagateFrames(value: number | undefined): void; changePropagateFrames(value: number): void;
changeUpToFrame(value: number | undefined): void; changeUpToFrame(value: number): void;
} }
export default function PropagateConfirmComponent(props: Props): JSX.Element { export default function PropagateConfirmComponent(props: Props): JSX.Element {
@ -26,12 +26,16 @@ export default function PropagateConfirmComponent(props: Props): JSX.Element {
visible, visible,
propagateFrames, propagateFrames,
propagateUpToFrame, propagateUpToFrame,
stopFrame,
frameNumber,
propagateObject, propagateObject,
changePropagateFrames, changePropagateFrames,
changeUpToFrame, changeUpToFrame,
cancel, cancel,
} = props; } = props;
const minPropagateFrames = 1;
return ( return (
<Modal <Modal
okType='primary' okType='primary'
@ -44,14 +48,37 @@ export default function PropagateConfirmComponent(props: Props): JSX.Element {
> >
<div className='cvat-propagate-confirm'> <div className='cvat-propagate-confirm'>
<Text>Do you want to make a copy of the object on</Text> <Text>Do you want to make a copy of the object on</Text>
<InputNumber size='small' min={1} value={propagateFrames} onChange={changePropagateFrames} /> <InputNumber
size='small'
min={minPropagateFrames}
value={propagateFrames}
onChange={(value: number | undefined) => {
if (typeof (value) === 'number') {
changePropagateFrames(Math.floor(
clamp(value, minPropagateFrames, Number.MAX_SAFE_INTEGER),
));
}
}}
/>
{ {
propagateFrames > 1 propagateFrames > 1
? <Text> frames </Text> ? <Text> frames </Text>
: <Text> frame </Text> : <Text> frame </Text>
} }
<Text>up to the </Text> <Text>up to the </Text>
<InputNumber size='small' value={propagateUpToFrame} onChange={changeUpToFrame} /> <InputNumber
size='small'
value={propagateUpToFrame}
min={frameNumber + 1}
max={stopFrame}
onChange={(value: number | undefined) => {
if (typeof (value) === 'number') {
changeUpToFrame(Math.floor(
clamp(value, frameNumber + 1, stopFrame),
));
}
}}
/>
<Text>frame</Text> <Text>frame</Text>
</div> </div>
</Modal> </Modal>

@ -2,27 +2,24 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React, { useState, useEffect } from 'react';
import { import { Row, Col } from 'antd/lib/grid';
Row, import Icon from 'antd/lib/icon';
Col, import Slider, { SliderValue } from 'antd/lib/slider';
Icon, import Tooltip from 'antd/lib/tooltip';
Slider, import InputNumber from 'antd/lib/input-number';
Tooltip,
InputNumber,
} from 'antd';
import { SliderValue } from 'antd/lib/slider';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import { clamp } from 'utils/math';
interface Props { interface Props {
startFrame: number; startFrame: number;
stopFrame: number; stopFrame: number;
frameNumber: number; frameNumber: number;
inputFrameRef: React.RefObject<InputNumber>; inputFrameRef: React.RefObject<InputNumber>;
onSliderChange(value: SliderValue): void; onSliderChange(value: SliderValue): void;
onInputChange(value: number | undefined): void; onInputChange(value: number): void;
onURLIconClick(): void; onURLIconClick(): void;
} }
@ -37,6 +34,14 @@ function PlayerNavigation(props: Props): JSX.Element {
onURLIconClick, onURLIconClick,
} = props; } = props;
const [frameInputValue, setFrameInputValue] = useState<number>(frameNumber);
useEffect(() => {
if (frameNumber !== frameInputValue) {
setFrameInputValue(frameNumber);
}
}, [frameNumber]);
return ( return (
<> <>
<Col className='cvat-player-controls'> <Col className='cvat-player-controls'>
@ -68,9 +73,21 @@ function PlayerNavigation(props: Props): JSX.Element {
<InputNumber <InputNumber
className='cvat-player-frame-selector' className='cvat-player-frame-selector'
type='number' type='number'
value={frameNumber || 0} value={frameInputValue}
// https://stackoverflow.com/questions/38256332/in-react-whats-the-difference-between-onchange-and-oninput // https://stackoverflow.com/questions/38256332/in-react-whats-the-difference-between-onchange-and-oninput
onChange={onInputChange} onChange={(value: number | undefined) => {
if (typeof (value) === 'number') {
setFrameInputValue(Math.floor(
clamp(value, startFrame, stopFrame),
));
}
}}
onBlur={() => {
onInputChange(frameInputValue);
}}
onPressEnter={() => {
onInputChange(frameInputValue);
}}
ref={inputFrameRef} ref={inputFrameRef}
/> />
</Col> </Col>

@ -4,12 +4,8 @@
import React from 'react'; import React from 'react';
import { import { Row, Col } from 'antd/lib/grid';
Row, import InputNumber from 'antd/lib/input-number';
Col,
InputNumber,
} from 'antd';
import { SliderValue } from 'antd/lib/slider'; import { SliderValue } from 'antd/lib/slider';
import { Workspace } from 'reducers/interfaces'; import { Workspace } from 'reducers/interfaces';
@ -40,7 +36,7 @@ interface Props {
onFirstFrame(): void; onFirstFrame(): void;
onLastFrame(): void; onLastFrame(): void;
onSliderChange(value: SliderValue): void; onSliderChange(value: SliderValue): void;
onInputChange(value: number | undefined): void; onInputChange(value: number): void;
onURLIconClick(): void; onURLIconClick(): void;
onUndoClick(): void; onUndoClick(): void;
onRedoClick(): void; onRedoClick(): void;

@ -4,28 +4,17 @@
import React from 'react'; import React from 'react';
import { import { Row, Col } from 'antd/lib/grid';
Row, import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox';
Col, import Slider from 'antd/lib/slider';
Checkbox, import Select from 'antd/lib/select';
Slider, import InputNumber from 'antd/lib/input-number';
Select, import Icon from 'antd/lib/icon';
InputNumber,
Icon,
} from 'antd';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import { CheckboxChangeEvent } from 'antd/lib/checkbox';
import {
BackJumpIcon,
ForwardJumpIcon,
} from 'icons';
import { import { clamp } from 'utils/math';
FrameSpeed, import { BackJumpIcon, ForwardJumpIcon } from 'icons';
GridColor, import { FrameSpeed, GridColor } from 'reducers/interfaces';
} from 'reducers/interfaces';
interface Props { interface Props {
frameStep: number; frameStep: number;
@ -78,18 +67,28 @@ export default function PlayerSettingsComponent(props: Props): JSX.Element {
onChangeSaturationLevel, onChangeSaturationLevel,
} = props; } = props;
const minFrameStep = 2;
const maxFrameStep = 1000;
const minGridSize = 5;
const maxGridSize = 1000;
return ( return (
<div className='cvat-player-settings'> <div className='cvat-player-settings'>
<Row type='flex' align='bottom' className='cvat-player-settings-step'> <Row type='flex' align='bottom' className='cvat-player-settings-step'>
<Col> <Col>
<Text className='cvat-text-color'> Player step </Text> <Text className='cvat-text-color'> Player step </Text>
<InputNumber <InputNumber
min={2} min={minFrameStep}
max={1000} max={maxFrameStep}
value={frameStep} value={frameStep}
onChange={(value: number | undefined): void => { onChange={(value: number | undefined): void => {
if (value) { if (typeof (value) === 'number') {
onChangeFrameStep(value); onChangeFrameStep(
Math.floor(
clamp(value, minFrameStep, maxFrameStep),
),
);
} }
}} }}
/> />
@ -138,14 +137,15 @@ export default function PlayerSettingsComponent(props: Props): JSX.Element {
<Col span={8} className='cvat-player-settings-grid-size'> <Col span={8} className='cvat-player-settings-grid-size'>
<Text className='cvat-text-color'> Grid size </Text> <Text className='cvat-text-color'> Grid size </Text>
<InputNumber <InputNumber
min={5} min={minGridSize}
max={1000} max={maxGridSize}
step={1}
value={gridSize} value={gridSize}
disabled={!grid} disabled={!grid}
onChange={(value: number | undefined): void => { onChange={(value: number | undefined): void => {
if (value) { if (typeof (value) === 'number') {
onChangeGridSize(value); onChangeGridSize(Math.floor(
clamp(value, minGridSize, maxGridSize),
));
} }
}} }}
/> />

@ -4,15 +4,12 @@
import React from 'react'; import React from 'react';
import { import { Row, Col } from 'antd/lib/grid';
Row, import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox';
Col, import InputNumber from 'antd/lib/input-number';
Checkbox,
InputNumber,
} from 'antd';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import { CheckboxChangeEvent } from 'antd/lib/checkbox';
import { clamp } from 'utils/math';
interface Props { interface Props {
autoSave: boolean; autoSave: boolean;
@ -37,6 +34,11 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element {
onSwitchShowingInterpolatedTracks, onSwitchShowingInterpolatedTracks,
} = props; } = props;
const minAutoSaveInterval = 5;
const maxAutoSaveInterval = 60;
const minAAMMargin = 0;
const maxAAMMargin = 1000;
return ( return (
<div className='cvat-workspace-settings'> <div className='cvat-workspace-settings'>
<Row type='flex'> <Row type='flex'>
@ -56,13 +58,19 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element {
<Col className='cvat-workspace-settings-auto-save-interval'> <Col className='cvat-workspace-settings-auto-save-interval'>
<Text type='secondary'> Auto save every </Text> <Text type='secondary'> Auto save every </Text>
<InputNumber <InputNumber
min={5} min={minAutoSaveInterval}
max={60} max={maxAutoSaveInterval}
step={1} step={1}
value={Math.round(autoSaveInterval / (60 * 1000))} value={Math.round(autoSaveInterval / (60 * 1000))}
onChange={(value: number | undefined): void => { onChange={(value: number | undefined): void => {
if (value) { if (typeof (value) === 'number') {
onChangeAutoSaveInterval(value * 60 * 1000); onChangeAutoSaveInterval(Math.floor(
clamp(
value,
minAutoSaveInterval,
maxAutoSaveInterval,
),
) * 60 * 1000);
} }
}} }}
/> />
@ -89,12 +97,14 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element {
<Col> <Col>
<Text className='cvat-text-color'> Attribute annotation mode (AAM) zoom margin </Text> <Text className='cvat-text-color'> Attribute annotation mode (AAM) zoom margin </Text>
<InputNumber <InputNumber
min={0} min={minAAMMargin}
max={1000} max={maxAAMMargin}
value={aamZoomMargin} value={aamZoomMargin}
onChange={(value: number | undefined): void => { onChange={(value: number | undefined): void => {
if (typeof (value) === 'number') { if (typeof (value) === 'number') {
onChangeAAMZoomMargin(value); onChangeAAMZoomMargin(Math.floor(
clamp(value, minAAMMargin, maxAAMMargin),
));
} }
}} }}
/> />

@ -145,15 +145,9 @@ class DrawShapePopoverContainer extends React.PureComponent<Props, State> {
}; };
private onChangePoints = (value: number | undefined): void => { private onChangePoints = (value: number | undefined): void => {
if (typeof (value) === 'undefined') { this.setState({
this.setState({ numberOfPoints: value,
numberOfPoints: value, });
});
} else if (typeof (value) === 'number') {
this.setState({
numberOfPoints: Math.max(value, this.minimumPoints),
});
}
}; };
private onChangeLabel = (value: string): void => { private onChangeLabel = (value: string): void => {

@ -88,23 +88,20 @@ class PropagateConfirmContainer extends React.PureComponent<Props> {
propagateObject(jobInstance, objectState, frameNumber + 1, propagateUpToFrame); propagateObject(jobInstance, objectState, frameNumber + 1, propagateUpToFrame);
}; };
private changePropagateFrames = (value: number | undefined): void => { private changePropagateFrames = (value: number): void => {
const { changePropagateFrames } = this.props; const { changePropagateFrames } = this.props;
if (typeof (value) !== 'undefined') { changePropagateFrames(value);
changePropagateFrames(value);
}
}; };
private changeUpToFrame = (value: number | undefined): void => { private changeUpToFrame = (value: number): void => {
const { const {
stopFrame, stopFrame,
frameNumber, frameNumber,
changePropagateFrames, changePropagateFrames,
} = this.props; } = this.props;
if (typeof (value) !== 'undefined') {
const propagateFrames = Math.max(0, Math.min(stopFrame, value)) - frameNumber; const propagateFrames = Math.max(0, Math.min(stopFrame, value)) - frameNumber;
changePropagateFrames(propagateFrames); changePropagateFrames(propagateFrames);
}
}; };
public render(): JSX.Element { public render(): JSX.Element {
@ -122,6 +119,8 @@ class PropagateConfirmContainer extends React.PureComponent<Props> {
<PropagateConfirmComponent <PropagateConfirmComponent
visible={objectState !== null} visible={objectState !== null}
propagateUpToFrame={propagateUpToFrame} propagateUpToFrame={propagateUpToFrame}
stopFrame={stopFrame}
frameNumber={frameNumber}
propagateFrames={propagateUpToFrame - frameNumber} propagateFrames={propagateUpToFrame - frameNumber}
propagateObject={this.propagateObject} propagateObject={this.propagateObject}
changePropagateFrames={this.changePropagateFrames} changePropagateFrames={this.changePropagateFrames}

@ -10,7 +10,7 @@ import { withRouter } from 'react-router';
import { RouteComponentProps } from 'react-router-dom'; import { RouteComponentProps } from 'react-router-dom';
import { GlobalHotKeys, KeyMap } from 'react-hotkeys'; import { GlobalHotKeys, KeyMap } from 'react-hotkeys';
import { InputNumber } from 'antd'; import InputNumber from 'antd/lib/input-number';
import { SliderValue } from 'antd/lib/slider'; import { SliderValue } from 'antd/lib/slider';
import { import {
@ -403,14 +403,15 @@ class AnnotationTopBarContainer extends React.PureComponent<Props> {
onChangeFrame(value as number); onChangeFrame(value as number);
}; };
private onChangePlayerInputValue = (value: number | undefined): void => { private onChangePlayerInputValue = (value: number): void => {
const { const {
onSwitchPlay, onSwitchPlay,
onChangeFrame, onChangeFrame,
playing, playing,
frameNumber,
} = this.props; } = this.props;
if (typeof (value) !== 'undefined') { if (value !== frameNumber) {
if (playing) { if (playing) {
onSwitchPlay(false); onSwitchPlay(false);
} }

@ -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);
}
Loading…
Cancel
Save