[CVAT-UI]: Appearance settings in AAM, keyframe navigation and other buttons in AAM (#1820)
parent
76280be4ad
commit
757f0ade17
@ -0,0 +1,214 @@
|
||||
// Copyright (C) 2020 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import React, { Dispatch } from 'react';
|
||||
import { AnyAction } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import Text from 'antd/lib/typography/Text';
|
||||
import Radio, { RadioChangeEvent } from 'antd/lib/radio';
|
||||
import Slider, { SliderValue } from 'antd/lib/slider';
|
||||
import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox';
|
||||
import Collapse from 'antd/lib/collapse';
|
||||
|
||||
import { ColorBy, CombinedState } from 'reducers/interfaces';
|
||||
import {
|
||||
collapseAppearance as collapseAppearanceAction,
|
||||
updateTabContentHeight as updateTabContentHeightAction,
|
||||
} from 'actions/annotation-actions';
|
||||
import {
|
||||
changeShapesColorBy as changeShapesColorByAction,
|
||||
changeShapesOpacity as changeShapesOpacityAction,
|
||||
changeSelectedShapesOpacity as changeSelectedShapesOpacityAction,
|
||||
changeShapesBlackBorders as changeShapesBlackBordersAction,
|
||||
changeShowBitmap as changeShowBitmapAction,
|
||||
changeShowProjections as changeShowProjectionsAction,
|
||||
} from 'actions/settings-actions';
|
||||
|
||||
interface StateToProps {
|
||||
appearanceCollapsed: boolean;
|
||||
colorBy: ColorBy;
|
||||
opacity: number;
|
||||
selectedOpacity: number;
|
||||
blackBorders: boolean;
|
||||
showBitmap: boolean;
|
||||
showProjections: boolean;
|
||||
}
|
||||
|
||||
interface DispatchToProps {
|
||||
collapseAppearance(): void;
|
||||
changeShapesColorBy(event: RadioChangeEvent): void;
|
||||
changeShapesOpacity(event: SliderValue): void;
|
||||
changeSelectedShapesOpacity(event: SliderValue): void;
|
||||
changeShapesBlackBorders(event: CheckboxChangeEvent): void;
|
||||
changeShowBitmap(event: CheckboxChangeEvent): void;
|
||||
changeShowProjections(event: CheckboxChangeEvent): void;
|
||||
}
|
||||
|
||||
export function computeHeight(): number {
|
||||
const [sidebar] = window.document.getElementsByClassName('cvat-objects-sidebar');
|
||||
const [appearance] = window.document.getElementsByClassName('cvat-objects-appearance-collapse');
|
||||
const [tabs] = Array.from(
|
||||
window.document.querySelectorAll('.cvat-objects-sidebar-tabs > .ant-tabs-card-bar'),
|
||||
);
|
||||
|
||||
if (sidebar && appearance && tabs) {
|
||||
const maxHeight = sidebar ? sidebar.clientHeight : 0;
|
||||
const appearanceHeight = appearance ? appearance.clientHeight : 0;
|
||||
const tabsHeight = tabs ? tabs.clientHeight : 0;
|
||||
return maxHeight - appearanceHeight - tabsHeight;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function mapStateToProps(state: CombinedState): StateToProps {
|
||||
const {
|
||||
annotation: {
|
||||
appearanceCollapsed,
|
||||
},
|
||||
settings: {
|
||||
shapes: {
|
||||
colorBy,
|
||||
opacity,
|
||||
selectedOpacity,
|
||||
blackBorders,
|
||||
showBitmap,
|
||||
showProjections,
|
||||
},
|
||||
},
|
||||
} = state;
|
||||
|
||||
return {
|
||||
appearanceCollapsed,
|
||||
colorBy,
|
||||
opacity,
|
||||
selectedOpacity,
|
||||
blackBorders,
|
||||
showBitmap,
|
||||
showProjections,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function mapDispatchToProps(dispatch: Dispatch<AnyAction>): DispatchToProps {
|
||||
return {
|
||||
collapseAppearance(): void {
|
||||
dispatch(collapseAppearanceAction());
|
||||
const [collapser] = window.document
|
||||
.getElementsByClassName('cvat-objects-appearance-collapse');
|
||||
|
||||
if (collapser) {
|
||||
const listener = (event: Event): void => {
|
||||
if ((event as TransitionEvent).propertyName === 'height') {
|
||||
const height = computeHeight();
|
||||
dispatch(updateTabContentHeightAction(height));
|
||||
collapser.removeEventListener('transitionend', listener);
|
||||
}
|
||||
};
|
||||
|
||||
collapser.addEventListener('transitionend', listener);
|
||||
}
|
||||
},
|
||||
changeShapesColorBy(event: RadioChangeEvent): void {
|
||||
dispatch(changeShapesColorByAction(event.target.value));
|
||||
},
|
||||
changeShapesOpacity(value: SliderValue): void {
|
||||
dispatch(changeShapesOpacityAction(value as number));
|
||||
},
|
||||
changeSelectedShapesOpacity(value: SliderValue): void {
|
||||
dispatch(changeSelectedShapesOpacityAction(value as number));
|
||||
},
|
||||
changeShapesBlackBorders(event: CheckboxChangeEvent): void {
|
||||
dispatch(changeShapesBlackBordersAction(event.target.checked));
|
||||
},
|
||||
changeShowBitmap(event: CheckboxChangeEvent): void {
|
||||
dispatch(changeShowBitmapAction(event.target.checked));
|
||||
},
|
||||
changeShowProjections(event: CheckboxChangeEvent): void {
|
||||
dispatch(changeShowProjectionsAction(event.target.checked));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
type Props = StateToProps & DispatchToProps;
|
||||
|
||||
function AppearanceBlock(props: Props): JSX.Element {
|
||||
const {
|
||||
appearanceCollapsed,
|
||||
colorBy,
|
||||
opacity,
|
||||
selectedOpacity,
|
||||
blackBorders,
|
||||
showBitmap,
|
||||
showProjections,
|
||||
collapseAppearance,
|
||||
changeShapesColorBy,
|
||||
changeShapesOpacity,
|
||||
changeSelectedShapesOpacity,
|
||||
changeShapesBlackBorders,
|
||||
changeShowBitmap,
|
||||
changeShowProjections,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Collapse
|
||||
onChange={collapseAppearance}
|
||||
activeKey={appearanceCollapsed ? [] : ['appearance']}
|
||||
className='cvat-objects-appearance-collapse'
|
||||
>
|
||||
<Collapse.Panel
|
||||
header={
|
||||
<Text strong>Appearance</Text>
|
||||
}
|
||||
key='appearance'
|
||||
>
|
||||
<div className='cvat-objects-appearance-content'>
|
||||
<Text type='secondary'>Color by</Text>
|
||||
<Radio.Group value={colorBy} onChange={changeShapesColorBy}>
|
||||
<Radio.Button value={ColorBy.INSTANCE}>{ColorBy.INSTANCE}</Radio.Button>
|
||||
<Radio.Button value={ColorBy.GROUP}>{ColorBy.GROUP}</Radio.Button>
|
||||
<Radio.Button value={ColorBy.LABEL}>{ColorBy.LABEL}</Radio.Button>
|
||||
</Radio.Group>
|
||||
<Text type='secondary'>Opacity</Text>
|
||||
<Slider
|
||||
onChange={changeShapesOpacity}
|
||||
value={opacity}
|
||||
min={0}
|
||||
max={100}
|
||||
/>
|
||||
<Text type='secondary'>Selected opacity</Text>
|
||||
<Slider
|
||||
onChange={changeSelectedShapesOpacity}
|
||||
value={selectedOpacity}
|
||||
min={0}
|
||||
max={100}
|
||||
/>
|
||||
<Checkbox
|
||||
onChange={changeShapesBlackBorders}
|
||||
checked={blackBorders}
|
||||
>
|
||||
Black borders
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
onChange={changeShowBitmap}
|
||||
checked={showBitmap}
|
||||
>
|
||||
Show bitmap
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
onChange={changeShowProjections}
|
||||
checked={showProjections}
|
||||
>
|
||||
Show projections
|
||||
</Checkbox>
|
||||
</div>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(React.memo(AppearanceBlock));
|
||||
@ -1,107 +0,0 @@
|
||||
// Copyright (C) 2020 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import React from 'react';
|
||||
import Text from 'antd/lib/typography/Text';
|
||||
import Radio, { RadioChangeEvent } from 'antd/lib/radio';
|
||||
import Slider, { SliderValue } from 'antd/lib/slider';
|
||||
import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox';
|
||||
import Collapse from 'antd/lib/collapse';
|
||||
|
||||
import { ColorBy } from 'reducers/interfaces';
|
||||
|
||||
interface Props {
|
||||
appearanceCollapsed: boolean;
|
||||
colorBy: ColorBy;
|
||||
opacity: number;
|
||||
selectedOpacity: number;
|
||||
blackBorders: boolean;
|
||||
showBitmap: boolean;
|
||||
showProjections: boolean;
|
||||
|
||||
collapseAppearance(): void;
|
||||
changeShapesColorBy(event: RadioChangeEvent): void;
|
||||
changeShapesOpacity(event: SliderValue): void;
|
||||
changeSelectedShapesOpacity(event: SliderValue): void;
|
||||
changeShapesBlackBorders(event: CheckboxChangeEvent): void;
|
||||
changeShowBitmap(event: CheckboxChangeEvent): void;
|
||||
changeShowProjections(event: CheckboxChangeEvent): void;
|
||||
}
|
||||
|
||||
function AppearanceBlock(props: Props): JSX.Element {
|
||||
const {
|
||||
appearanceCollapsed,
|
||||
colorBy,
|
||||
opacity,
|
||||
selectedOpacity,
|
||||
blackBorders,
|
||||
showBitmap,
|
||||
showProjections,
|
||||
collapseAppearance,
|
||||
changeShapesColorBy,
|
||||
changeShapesOpacity,
|
||||
changeSelectedShapesOpacity,
|
||||
changeShapesBlackBorders,
|
||||
changeShowBitmap,
|
||||
changeShowProjections,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Collapse
|
||||
onChange={collapseAppearance}
|
||||
activeKey={appearanceCollapsed ? [] : ['appearance']}
|
||||
className='cvat-objects-appearance-collapse'
|
||||
>
|
||||
<Collapse.Panel
|
||||
header={
|
||||
<Text strong>Appearance</Text>
|
||||
}
|
||||
key='appearance'
|
||||
>
|
||||
<div className='cvat-objects-appearance-content'>
|
||||
<Text type='secondary'>Color by</Text>
|
||||
<Radio.Group value={colorBy} onChange={changeShapesColorBy}>
|
||||
<Radio.Button value={ColorBy.INSTANCE}>{ColorBy.INSTANCE}</Radio.Button>
|
||||
<Radio.Button value={ColorBy.GROUP}>{ColorBy.GROUP}</Radio.Button>
|
||||
<Radio.Button value={ColorBy.LABEL}>{ColorBy.LABEL}</Radio.Button>
|
||||
</Radio.Group>
|
||||
<Text type='secondary'>Opacity</Text>
|
||||
<Slider
|
||||
onChange={changeShapesOpacity}
|
||||
value={opacity}
|
||||
min={0}
|
||||
max={100}
|
||||
/>
|
||||
<Text type='secondary'>Selected opacity</Text>
|
||||
<Slider
|
||||
onChange={changeSelectedShapesOpacity}
|
||||
value={selectedOpacity}
|
||||
min={0}
|
||||
max={100}
|
||||
/>
|
||||
<Checkbox
|
||||
onChange={changeShapesBlackBorders}
|
||||
checked={blackBorders}
|
||||
>
|
||||
Black borders
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
onChange={changeShowBitmap}
|
||||
checked={showBitmap}
|
||||
>
|
||||
Show bitmap
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
onChange={changeShowProjections}
|
||||
checked={showProjections}
|
||||
>
|
||||
Show projections
|
||||
</Checkbox>
|
||||
</div>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(AppearanceBlock);
|
||||
@ -0,0 +1,175 @@
|
||||
// Copyright (C) 2020 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import React from 'react';
|
||||
import { Col } from 'antd/lib/grid';
|
||||
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 Text from 'antd/lib/typography/Text';
|
||||
|
||||
import consts from 'consts';
|
||||
import { clamp } from 'utils/math';
|
||||
|
||||
interface Props {
|
||||
attrInputType: string;
|
||||
attrValues: string[];
|
||||
attrValue: string;
|
||||
attrName: string;
|
||||
attrID: number;
|
||||
changeAttribute(attrID: number, value: string): void;
|
||||
}
|
||||
|
||||
function attrIsTheSame(
|
||||
prevProps: Props,
|
||||
nextProps: Props,
|
||||
): boolean {
|
||||
return nextProps.attrID === prevProps.attrID
|
||||
&& nextProps.attrValue === prevProps.attrValue
|
||||
&& nextProps.attrName === prevProps.attrName
|
||||
&& nextProps.attrInputType === prevProps.attrInputType
|
||||
&& nextProps.attrValues
|
||||
.map((value: string, id: number): boolean => prevProps.attrValues[id] === value)
|
||||
.every((value: boolean): boolean => value);
|
||||
}
|
||||
|
||||
function ItemAttributeComponent(props: Props): JSX.Element {
|
||||
const {
|
||||
attrInputType,
|
||||
attrValues,
|
||||
attrValue,
|
||||
attrName,
|
||||
attrID,
|
||||
changeAttribute,
|
||||
} = props;
|
||||
|
||||
if (attrInputType === 'checkbox') {
|
||||
return (
|
||||
<Col span={24}>
|
||||
<Checkbox
|
||||
className='cvat-object-item-checkbox-attribute'
|
||||
checked={attrValue === 'true'}
|
||||
onChange={(event: CheckboxChangeEvent): void => {
|
||||
const value = event.target.checked ? 'true' : 'false';
|
||||
changeAttribute(attrID, value);
|
||||
}}
|
||||
>
|
||||
<Text strong className='cvat-text'>
|
||||
{attrName}
|
||||
</Text>
|
||||
</Checkbox>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
|
||||
if (attrInputType === 'radio') {
|
||||
return (
|
||||
<Col span={24}>
|
||||
<fieldset className='cvat-object-item-radio-attribute'>
|
||||
<legend>
|
||||
<Text strong className='cvat-text'>{attrName}</Text>
|
||||
</legend>
|
||||
<Radio.Group
|
||||
size='small'
|
||||
value={attrValue}
|
||||
onChange={(event: RadioChangeEvent): void => {
|
||||
changeAttribute(attrID, event.target.value);
|
||||
}}
|
||||
>
|
||||
{ attrValues.map((value: string): JSX.Element => (
|
||||
<Radio key={value} value={value}>
|
||||
{value === consts.UNDEFINED_ATTRIBUTE_VALUE
|
||||
? consts.NO_BREAK_SPACE : value}
|
||||
</Radio>
|
||||
)) }
|
||||
</Radio.Group>
|
||||
</fieldset>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
|
||||
if (attrInputType === 'select') {
|
||||
return (
|
||||
<>
|
||||
<Col span={24}>
|
||||
<Text strong className='cvat-text'>
|
||||
{attrName}
|
||||
</Text>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Select
|
||||
size='small'
|
||||
onChange={(value: string): void => {
|
||||
changeAttribute(attrID, value);
|
||||
}}
|
||||
value={attrValue}
|
||||
className='cvat-object-item-select-attribute'
|
||||
>
|
||||
{ attrValues.map((value: string): JSX.Element => (
|
||||
<Select.Option key={value} value={value}>
|
||||
{value === consts.UNDEFINED_ATTRIBUTE_VALUE
|
||||
? consts.NO_BREAK_SPACE : value}
|
||||
</Select.Option>
|
||||
)) }
|
||||
</Select>
|
||||
</Col>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (attrInputType === 'number') {
|
||||
const [min, max, step] = attrValues.map((value: string): number => +value);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Col span={24}>
|
||||
<Text strong className='cvat-text'>
|
||||
{attrName}
|
||||
</Text>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<InputNumber
|
||||
size='small'
|
||||
onChange={(value: number | undefined): void => {
|
||||
if (typeof (value) === 'number') {
|
||||
changeAttribute(
|
||||
attrID, `${clamp(value, min, max)}`,
|
||||
);
|
||||
}
|
||||
}}
|
||||
value={+attrValue}
|
||||
className='cvat-object-item-number-attribute'
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
/>
|
||||
</Col>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Col span={24}>
|
||||
<Text strong className='cvat-text'>
|
||||
{attrName}
|
||||
</Text>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Input
|
||||
size='small'
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
changeAttribute(attrID, event.target.value);
|
||||
}}
|
||||
value={attrValue}
|
||||
className='cvat-object-item-text-attribute'
|
||||
/>
|
||||
</Col>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(ItemAttributeComponent, attrIsTheSame);
|
||||
@ -0,0 +1,131 @@
|
||||
// Copyright (C) 2020 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import React from 'react';
|
||||
import { Row, Col } from 'antd/lib/grid';
|
||||
import Icon from 'antd/lib/icon';
|
||||
import Select, { OptionProps } from 'antd/lib/select';
|
||||
import Dropdown from 'antd/lib/dropdown';
|
||||
import Text from 'antd/lib/typography/Text';
|
||||
import Tooltip from 'antd/lib/tooltip';
|
||||
|
||||
import { ObjectType, ShapeType } from 'reducers/interfaces';
|
||||
import ItemMenu from './object-item-menu';
|
||||
|
||||
interface Props {
|
||||
clientID: number;
|
||||
serverID: number | undefined;
|
||||
labelID: number;
|
||||
labels: any[];
|
||||
shapeType: ShapeType;
|
||||
objectType: ObjectType;
|
||||
type: string;
|
||||
locked: boolean;
|
||||
copyShortcut: string;
|
||||
pasteShortcut: string;
|
||||
propagateShortcut: string;
|
||||
toBackgroundShortcut: string;
|
||||
toForegroundShortcut: string;
|
||||
removeShortcut: string;
|
||||
changeLabel(labelID: string): void;
|
||||
copy(): void;
|
||||
remove(): void;
|
||||
propagate(): void;
|
||||
createURL(): void;
|
||||
switchOrientation(): void;
|
||||
toBackground(): void;
|
||||
toForeground(): void;
|
||||
resetCuboidPerspective(): void;
|
||||
}
|
||||
|
||||
function ItemTopComponent(props: Props): JSX.Element {
|
||||
const {
|
||||
clientID,
|
||||
serverID,
|
||||
labelID,
|
||||
labels,
|
||||
shapeType,
|
||||
objectType,
|
||||
type,
|
||||
locked,
|
||||
copyShortcut,
|
||||
pasteShortcut,
|
||||
propagateShortcut,
|
||||
toBackgroundShortcut,
|
||||
toForegroundShortcut,
|
||||
removeShortcut,
|
||||
changeLabel,
|
||||
copy,
|
||||
remove,
|
||||
propagate,
|
||||
createURL,
|
||||
switchOrientation,
|
||||
toBackground,
|
||||
toForeground,
|
||||
resetCuboidPerspective,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Row type='flex' align='middle'>
|
||||
<Col span={10}>
|
||||
<Text style={{ fontSize: 12 }}>{clientID}</Text>
|
||||
<br />
|
||||
<Text type='secondary' style={{ fontSize: 10 }}>{type}</Text>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Tooltip title='Change current label'>
|
||||
<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}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<Dropdown
|
||||
placement='bottomLeft'
|
||||
overlay={ItemMenu({
|
||||
serverID,
|
||||
locked,
|
||||
shapeType,
|
||||
objectType,
|
||||
copyShortcut,
|
||||
pasteShortcut,
|
||||
propagateShortcut,
|
||||
toBackgroundShortcut,
|
||||
toForegroundShortcut,
|
||||
removeShortcut,
|
||||
copy,
|
||||
remove,
|
||||
propagate,
|
||||
createURL,
|
||||
switchOrientation,
|
||||
toBackground,
|
||||
toForeground,
|
||||
resetCuboidPerspective,
|
||||
})}
|
||||
>
|
||||
<Icon type='more' />
|
||||
</Dropdown>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(ItemTopComponent);
|
||||
@ -0,0 +1,262 @@
|
||||
// Copyright (C) 2020 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import React from 'react';
|
||||
import { Row, Col } from 'antd/lib/grid';
|
||||
import Icon from 'antd/lib/icon';
|
||||
import Tooltip from 'antd/lib/tooltip';
|
||||
|
||||
import {
|
||||
ObjectOutsideIcon,
|
||||
FirstIcon,
|
||||
LastIcon,
|
||||
PreviousIcon,
|
||||
NextIcon,
|
||||
} from 'icons';
|
||||
import { ObjectType, ShapeType } from 'reducers/interfaces';
|
||||
|
||||
interface Props {
|
||||
objectType: ObjectType;
|
||||
shapeType: ShapeType;
|
||||
occluded: boolean;
|
||||
outside: boolean | undefined;
|
||||
locked: boolean;
|
||||
pinned: boolean;
|
||||
hidden: boolean;
|
||||
keyframe: boolean | undefined;
|
||||
outsideDisabled: boolean;
|
||||
hiddenDisabled: boolean;
|
||||
keyframeDisabled: boolean;
|
||||
switchOccludedShortcut: string;
|
||||
switchOutsideShortcut: string;
|
||||
switchLockShortcut: string;
|
||||
switchHiddenShortcut: string;
|
||||
switchKeyFrameShortcut: string;
|
||||
nextKeyFrameShortcut: string;
|
||||
prevKeyFrameShortcut: string;
|
||||
|
||||
navigateFirstKeyframe: null | (() => void);
|
||||
navigatePrevKeyframe: null | (() => void);
|
||||
navigateNextKeyframe: null | (() => void);
|
||||
navigateLastKeyframe: null | (() => void);
|
||||
|
||||
setOccluded(): void;
|
||||
unsetOccluded(): void;
|
||||
setOutside(): void;
|
||||
unsetOutside(): void;
|
||||
setKeyframe(): void;
|
||||
unsetKeyframe(): void;
|
||||
lock(): void;
|
||||
unlock(): void;
|
||||
pin(): void;
|
||||
unpin(): void;
|
||||
hide(): void;
|
||||
show(): void;
|
||||
}
|
||||
|
||||
function ItemButtonsComponent(props: Props): JSX.Element {
|
||||
const {
|
||||
objectType,
|
||||
shapeType,
|
||||
occluded,
|
||||
outside,
|
||||
locked,
|
||||
pinned,
|
||||
hidden,
|
||||
keyframe,
|
||||
outsideDisabled,
|
||||
hiddenDisabled,
|
||||
keyframeDisabled,
|
||||
switchOccludedShortcut,
|
||||
switchOutsideShortcut,
|
||||
switchLockShortcut,
|
||||
switchHiddenShortcut,
|
||||
switchKeyFrameShortcut,
|
||||
nextKeyFrameShortcut,
|
||||
prevKeyFrameShortcut,
|
||||
|
||||
navigateFirstKeyframe,
|
||||
navigatePrevKeyframe,
|
||||
navigateNextKeyframe,
|
||||
navigateLastKeyframe,
|
||||
|
||||
setOccluded,
|
||||
unsetOccluded,
|
||||
setOutside,
|
||||
unsetOutside,
|
||||
setKeyframe,
|
||||
unsetKeyframe,
|
||||
lock,
|
||||
unlock,
|
||||
pin,
|
||||
unpin,
|
||||
hide,
|
||||
show,
|
||||
} = props;
|
||||
|
||||
const outsideStyle = outsideDisabled ? { opacity: 0.5, pointerEvents: 'none' as 'none' } : {};
|
||||
const hiddenStyle = hiddenDisabled ? { opacity: 0.5, pointerEvents: 'none' as 'none' } : {};
|
||||
const keyframeStyle = keyframeDisabled ? { opacity: 0.5, pointerEvents: 'none' as 'none' } : {};
|
||||
|
||||
|
||||
if (objectType === ObjectType.TRACK) {
|
||||
return (
|
||||
<Row type='flex' align='middle' justify='space-around'>
|
||||
<Col span={20} style={{ textAlign: 'center' }}>
|
||||
<Row type='flex' justify='space-around'>
|
||||
<Col>
|
||||
{ navigateFirstKeyframe
|
||||
? <Icon component={FirstIcon} onClick={navigateFirstKeyframe} />
|
||||
: <Icon component={FirstIcon} style={{ opacity: 0.5, pointerEvents: 'none' }} />}
|
||||
</Col>
|
||||
<Col>
|
||||
{ navigatePrevKeyframe
|
||||
? (
|
||||
<Tooltip title={`Go to previous keyframe ${prevKeyFrameShortcut}`}>
|
||||
<Icon
|
||||
component={PreviousIcon}
|
||||
onClick={navigatePrevKeyframe}
|
||||
/>
|
||||
</Tooltip>
|
||||
)
|
||||
: <Icon component={PreviousIcon} style={{ opacity: 0.5, pointerEvents: 'none' }} />}
|
||||
</Col>
|
||||
<Col>
|
||||
{ navigateNextKeyframe
|
||||
? (
|
||||
<Tooltip title={`Go to next keyframe ${nextKeyFrameShortcut}`}>
|
||||
<Icon
|
||||
component={NextIcon}
|
||||
onClick={navigateNextKeyframe}
|
||||
/>
|
||||
</Tooltip>
|
||||
)
|
||||
: <Icon component={NextIcon} style={{ opacity: 0.5, pointerEvents: 'none' }} />}
|
||||
</Col>
|
||||
<Col>
|
||||
{ navigateLastKeyframe
|
||||
? <Icon component={LastIcon} onClick={navigateLastKeyframe} />
|
||||
: <Icon component={LastIcon} style={{ opacity: 0.5, pointerEvents: 'none' }} />}
|
||||
</Col>
|
||||
</Row>
|
||||
<Row type='flex' justify='space-around'>
|
||||
<Col>
|
||||
<Tooltip title={`Switch outside property ${switchOutsideShortcut}`}>
|
||||
{ outside
|
||||
? (
|
||||
<Icon
|
||||
component={ObjectOutsideIcon}
|
||||
onClick={unsetOutside}
|
||||
style={outsideStyle}
|
||||
/>
|
||||
)
|
||||
: <Icon type='select' onClick={setOutside} style={outsideStyle} />}
|
||||
</Tooltip>
|
||||
</Col>
|
||||
<Col>
|
||||
<Tooltip title={`Switch lock property ${switchLockShortcut}`}>
|
||||
{ locked
|
||||
? <Icon type='lock' theme='filled' onClick={unlock} />
|
||||
: <Icon type='unlock' onClick={lock} />}
|
||||
</Tooltip>
|
||||
</Col>
|
||||
<Col>
|
||||
<Tooltip title={`Switch occluded property ${switchOccludedShortcut}`}>
|
||||
{ occluded
|
||||
? <Icon type='team' onClick={unsetOccluded} />
|
||||
: <Icon type='user' onClick={setOccluded} />}
|
||||
</Tooltip>
|
||||
</Col>
|
||||
<Col>
|
||||
<Tooltip title={`Switch hidden property ${switchHiddenShortcut}`}>
|
||||
{ hidden
|
||||
? <Icon type='eye-invisible' theme='filled' onClick={show} style={hiddenStyle} />
|
||||
: <Icon type='eye' onClick={hide} style={hiddenStyle} />}
|
||||
</Tooltip>
|
||||
</Col>
|
||||
<Col>
|
||||
<Tooltip title={`Switch keyframe property ${switchKeyFrameShortcut}`}>
|
||||
{ keyframe
|
||||
? <Icon type='star' theme='filled' onClick={unsetKeyframe} style={keyframeStyle} />
|
||||
: <Icon type='star' onClick={setKeyframe} style={keyframeStyle} />}
|
||||
</Tooltip>
|
||||
</Col>
|
||||
{
|
||||
shapeType !== ShapeType.POINTS && (
|
||||
<Col>
|
||||
<Tooltip title='Switch pinned property'>
|
||||
{ pinned
|
||||
? <Icon type='pushpin' theme='filled' onClick={unpin} />
|
||||
: <Icon type='pushpin' onClick={pin} />}
|
||||
</Tooltip>
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
if (objectType === ObjectType.TAG) {
|
||||
return (
|
||||
<Row type='flex' align='middle' justify='space-around'>
|
||||
<Col span={20} style={{ textAlign: 'center' }}>
|
||||
<Row type='flex' justify='space-around'>
|
||||
<Col>
|
||||
<Tooltip title={`Switch lock property ${switchLockShortcut}`}>
|
||||
{ locked
|
||||
? <Icon type='lock' onClick={unlock} theme='filled' />
|
||||
: <Icon type='unlock' onClick={lock} />}
|
||||
</Tooltip>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Row type='flex' align='middle' justify='space-around'>
|
||||
<Col span={20} style={{ textAlign: 'center' }}>
|
||||
<Row type='flex' justify='space-around'>
|
||||
<Col>
|
||||
<Tooltip title={`Switch lock property ${switchLockShortcut}`}>
|
||||
{ locked
|
||||
? <Icon type='lock' onClick={unlock} theme='filled' />
|
||||
: <Icon type='unlock' onClick={lock} />}
|
||||
</Tooltip>
|
||||
</Col>
|
||||
<Col>
|
||||
<Tooltip title={`Switch occluded property ${switchOccludedShortcut}`}>
|
||||
{ occluded
|
||||
? <Icon type='team' onClick={unsetOccluded} />
|
||||
: <Icon type='user' onClick={setOccluded} />}
|
||||
</Tooltip>
|
||||
</Col>
|
||||
<Col>
|
||||
<Tooltip title={`Switch hidden property ${switchHiddenShortcut}`}>
|
||||
{ hidden
|
||||
? <Icon type='eye-invisible' onClick={show} />
|
||||
: <Icon type='eye' onClick={hide} />}
|
||||
</Tooltip>
|
||||
</Col>
|
||||
{
|
||||
shapeType !== ShapeType.POINTS && (
|
||||
<Col>
|
||||
<Tooltip title='Switch pinned property'>
|
||||
{ pinned
|
||||
? <Icon type='pushpin' theme='filled' onClick={unpin} />
|
||||
: <Icon type='pushpin' onClick={pin} />}
|
||||
</Tooltip>
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(ItemButtonsComponent);
|
||||
@ -0,0 +1,86 @@
|
||||
// Copyright (C) 2020 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import React from 'react';
|
||||
import { Row } from 'antd/lib/grid';
|
||||
import Collapse from 'antd/lib/collapse';
|
||||
|
||||
import ItemAttribute from './object-item-attribute';
|
||||
|
||||
interface Props {
|
||||
collapsed: boolean;
|
||||
attributes: any[];
|
||||
values: Record<number, string>;
|
||||
changeAttribute(attrID: number, value: string): void;
|
||||
collapse(): void;
|
||||
}
|
||||
|
||||
export function attrValuesAreEqual(
|
||||
next: Record<number, string>, prev: Record<number, string>,
|
||||
): boolean {
|
||||
const prevKeys = Object.keys(prev);
|
||||
const nextKeys = Object.keys(next);
|
||||
|
||||
return nextKeys.length === prevKeys.length
|
||||
&& nextKeys.map((key: string): boolean => prev[+key] === next[+key])
|
||||
.every((value: boolean) => value);
|
||||
}
|
||||
|
||||
function attrAreTheSame(
|
||||
prevProps: Props,
|
||||
nextProps: Props,
|
||||
): boolean {
|
||||
return nextProps.collapsed === prevProps.collapsed
|
||||
&& nextProps.attributes === prevProps.attributes
|
||||
&& attrValuesAreEqual(nextProps.values, prevProps.values);
|
||||
}
|
||||
|
||||
function ItemAttributesComponent(props: Props): JSX.Element {
|
||||
const {
|
||||
collapsed,
|
||||
attributes,
|
||||
values,
|
||||
changeAttribute,
|
||||
collapse,
|
||||
} = props;
|
||||
|
||||
const sorted = [...attributes]
|
||||
.sort((a: any, b: any): number => a.inputType.localeCompare(b.inputType));
|
||||
|
||||
return (
|
||||
<Row>
|
||||
<Collapse
|
||||
className='cvat-objects-sidebar-state-item-collapse'
|
||||
activeKey={collapsed ? [] : ['details']}
|
||||
onChange={collapse}
|
||||
>
|
||||
<Collapse.Panel
|
||||
header={<span style={{ fontSize: '11px' }}>Details</span>}
|
||||
key='details'
|
||||
>
|
||||
{ sorted.map((attribute: any): JSX.Element => (
|
||||
<Row
|
||||
key={attribute.id}
|
||||
type='flex'
|
||||
align='middle'
|
||||
justify='start'
|
||||
className='cvat-object-item-attribute-wrapper'
|
||||
>
|
||||
<ItemAttribute
|
||||
attrValue={values[attribute.id]}
|
||||
attrInputType={attribute.inputType}
|
||||
attrName={attribute.name}
|
||||
attrID={attribute.id}
|
||||
attrValues={attribute.values}
|
||||
changeAttribute={changeAttribute}
|
||||
/>
|
||||
</Row>
|
||||
))}
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(ItemAttributesComponent, attrAreTheSame);
|
||||
@ -0,0 +1,139 @@
|
||||
// Copyright (C) 2020 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import React from 'react';
|
||||
import Icon from 'antd/lib/icon';
|
||||
import Menu from 'antd/lib/menu';
|
||||
import Button from 'antd/lib/button';
|
||||
import Modal from 'antd/lib/modal';
|
||||
import Tooltip from 'antd/lib/tooltip';
|
||||
|
||||
import { BackgroundIcon, ForegroundIcon, ResetPerspectiveIcon } from 'icons';
|
||||
import { ObjectType, ShapeType } from 'reducers/interfaces';
|
||||
|
||||
interface Props {
|
||||
serverID: number | undefined;
|
||||
locked: boolean;
|
||||
shapeType: ShapeType;
|
||||
objectType: ObjectType;
|
||||
copyShortcut: string;
|
||||
pasteShortcut: string;
|
||||
propagateShortcut: string;
|
||||
toBackgroundShortcut: string;
|
||||
toForegroundShortcut: string;
|
||||
removeShortcut: string;
|
||||
copy: (() => void);
|
||||
remove: (() => void);
|
||||
propagate: (() => void);
|
||||
createURL: (() => void);
|
||||
switchOrientation: (() => void);
|
||||
toBackground: (() => void);
|
||||
toForeground: (() => void);
|
||||
resetCuboidPerspective: (() => void);
|
||||
}
|
||||
|
||||
export default function ItemMenu(props: Props): JSX.Element {
|
||||
const {
|
||||
serverID,
|
||||
locked,
|
||||
shapeType,
|
||||
objectType,
|
||||
copyShortcut,
|
||||
pasteShortcut,
|
||||
propagateShortcut,
|
||||
toBackgroundShortcut,
|
||||
toForegroundShortcut,
|
||||
removeShortcut,
|
||||
copy,
|
||||
remove,
|
||||
propagate,
|
||||
createURL,
|
||||
switchOrientation,
|
||||
toBackground,
|
||||
toForeground,
|
||||
resetCuboidPerspective,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Menu className='cvat-object-item-menu'>
|
||||
<Menu.Item>
|
||||
<Button disabled={serverID === undefined} type='link' icon='link' onClick={createURL}>
|
||||
Create object URL
|
||||
</Button>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<Tooltip title={`${copyShortcut} and ${pasteShortcut}`}>
|
||||
<Button type='link' icon='copy' onClick={copy}>
|
||||
Make a copy
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<Tooltip title={`${propagateShortcut}`}>
|
||||
<Button type='link' icon='block' onClick={propagate}>
|
||||
Propagate
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Menu.Item>
|
||||
{ [ShapeType.POLYGON, ShapeType.POLYLINE, ShapeType.CUBOID].includes(shapeType) && (
|
||||
<Menu.Item>
|
||||
<Button type='link' icon='retweet' onClick={switchOrientation}>
|
||||
Switch orientation
|
||||
</Button>
|
||||
</Menu.Item>
|
||||
)}
|
||||
{shapeType === ShapeType.CUBOID && (
|
||||
<Menu.Item>
|
||||
<Button type='link' onClick={resetCuboidPerspective}>
|
||||
<Icon component={ResetPerspectiveIcon} />
|
||||
Reset perspective
|
||||
</Button>
|
||||
</Menu.Item>
|
||||
)}
|
||||
{objectType !== ObjectType.TAG && (
|
||||
<Menu.Item>
|
||||
<Tooltip title={`${toBackgroundShortcut}`}>
|
||||
<Button type='link' onClick={toBackground}>
|
||||
<Icon component={BackgroundIcon} />
|
||||
To background
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Menu.Item>
|
||||
)}
|
||||
{objectType !== ObjectType.TAG && (
|
||||
<Menu.Item>
|
||||
<Tooltip title={`${toForegroundShortcut}`}>
|
||||
<Button type='link' onClick={toForeground}>
|
||||
<Icon component={ForegroundIcon} />
|
||||
To foreground
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Menu.Item>
|
||||
)}
|
||||
<Menu.Item>
|
||||
<Tooltip title={`${removeShortcut}`}>
|
||||
<Button
|
||||
type='link'
|
||||
icon='delete'
|
||||
onClick={(): void => {
|
||||
if (locked) {
|
||||
Modal.confirm({
|
||||
title: 'Object is locked',
|
||||
content: 'Are you sure you want to remove it?',
|
||||
onOk() {
|
||||
remove();
|
||||
},
|
||||
});
|
||||
} else {
|
||||
remove();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,296 @@
|
||||
// Copyright (C) 2020 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { LogType } from 'cvat-logger';
|
||||
import { Canvas } from 'cvat-canvas-wrapper';
|
||||
import { ThunkDispatch } from 'utils/redux';
|
||||
import { updateAnnotationsAsync, changeFrameAsync } from 'actions/annotation-actions';
|
||||
import { CombinedState } from 'reducers/interfaces';
|
||||
import ItemButtonsComponent from 'components/annotation-page/standard-workspace/objects-side-bar/object-item-buttons';
|
||||
|
||||
interface OwnProps {
|
||||
clientID: number;
|
||||
outsideDisabled?: boolean;
|
||||
hiddenDisabled?: boolean;
|
||||
keyframeDisabled?: boolean;
|
||||
}
|
||||
|
||||
interface StateToProps {
|
||||
objectState: any;
|
||||
jobInstance: any;
|
||||
frameNumber: number;
|
||||
normalizedKeyMap: Record<string, string>;
|
||||
canvasInstance: Canvas;
|
||||
outsideDisabled: boolean;
|
||||
hiddenDisabled: boolean;
|
||||
keyframeDisabled: boolean;
|
||||
}
|
||||
|
||||
interface DispatchToProps {
|
||||
updateAnnotations(statesToUpdate: any[]): void;
|
||||
changeFrame(frame: number): void;
|
||||
}
|
||||
|
||||
function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
|
||||
const {
|
||||
annotation: {
|
||||
annotations: {
|
||||
states,
|
||||
},
|
||||
job: {
|
||||
instance: jobInstance,
|
||||
},
|
||||
player: {
|
||||
frame: {
|
||||
number: frameNumber,
|
||||
},
|
||||
},
|
||||
canvas: {
|
||||
instance: canvasInstance,
|
||||
},
|
||||
},
|
||||
shortcuts: {
|
||||
normalizedKeyMap,
|
||||
},
|
||||
} = state;
|
||||
|
||||
const {
|
||||
clientID,
|
||||
outsideDisabled,
|
||||
hiddenDisabled,
|
||||
keyframeDisabled,
|
||||
} = own;
|
||||
const [objectState] = states
|
||||
.filter((_objectState): boolean => _objectState.clientID === clientID);
|
||||
|
||||
return {
|
||||
objectState,
|
||||
normalizedKeyMap,
|
||||
frameNumber,
|
||||
jobInstance,
|
||||
canvasInstance,
|
||||
outsideDisabled: typeof (outsideDisabled) === 'undefined' ? false : outsideDisabled,
|
||||
hiddenDisabled: typeof (hiddenDisabled) === 'undefined' ? false : hiddenDisabled,
|
||||
keyframeDisabled: typeof (keyframeDisabled) === 'undefined' ? false : keyframeDisabled,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch: ThunkDispatch): DispatchToProps {
|
||||
return {
|
||||
updateAnnotations(states: any[]) {
|
||||
dispatch(updateAnnotationsAsync(states));
|
||||
},
|
||||
changeFrame(frame: number): void {
|
||||
dispatch(changeFrameAsync(frame));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
class ItemButtonsWrapper extends React.PureComponent<StateToProps & DispatchToProps> {
|
||||
private navigateFirstKeyframe = (): void => {
|
||||
const { objectState, frameNumber } = this.props;
|
||||
const { first } = objectState.keyframes;
|
||||
if (first !== frameNumber) {
|
||||
this.changeFrame(first);
|
||||
}
|
||||
};
|
||||
|
||||
private navigatePrevKeyframe = (): void => {
|
||||
const { objectState, frameNumber } = this.props;
|
||||
const { prev } = objectState.keyframes;
|
||||
if (prev !== null && prev !== frameNumber) {
|
||||
this.changeFrame(prev);
|
||||
}
|
||||
};
|
||||
|
||||
private navigateNextKeyframe = (): void => {
|
||||
const { objectState, frameNumber } = this.props;
|
||||
const { next } = objectState.keyframes;
|
||||
if (next !== null && next !== frameNumber) {
|
||||
this.changeFrame(next);
|
||||
}
|
||||
};
|
||||
|
||||
private navigateLastKeyframe = (): void => {
|
||||
const { objectState, frameNumber } = this.props;
|
||||
const { last } = objectState.keyframes;
|
||||
if (last !== frameNumber) {
|
||||
this.changeFrame(last);
|
||||
}
|
||||
};
|
||||
|
||||
private lock = (): void => {
|
||||
const { objectState, jobInstance } = this.props;
|
||||
jobInstance.logger.log(LogType.lockObject, { locked: true });
|
||||
objectState.lock = true;
|
||||
this.commit();
|
||||
};
|
||||
|
||||
private unlock = (): void => {
|
||||
const { objectState, jobInstance } = this.props;
|
||||
jobInstance.logger.log(LogType.lockObject, { locked: false });
|
||||
objectState.lock = false;
|
||||
this.commit();
|
||||
};
|
||||
|
||||
private pin = (): void => {
|
||||
const { objectState } = this.props;
|
||||
objectState.pinned = true;
|
||||
this.commit();
|
||||
};
|
||||
|
||||
private unpin = (): void => {
|
||||
const { objectState } = this.props;
|
||||
objectState.pinned = false;
|
||||
this.commit();
|
||||
};
|
||||
|
||||
private show = (): void => {
|
||||
const { objectState } = this.props;
|
||||
objectState.hidden = false;
|
||||
this.commit();
|
||||
};
|
||||
|
||||
private hide = (): void => {
|
||||
const { objectState } = this.props;
|
||||
objectState.hidden = true;
|
||||
this.commit();
|
||||
};
|
||||
|
||||
private setOccluded = (): void => {
|
||||
const { objectState } = this.props;
|
||||
objectState.occluded = true;
|
||||
this.commit();
|
||||
};
|
||||
|
||||
private unsetOccluded = (): void => {
|
||||
const { objectState } = this.props;
|
||||
objectState.occluded = false;
|
||||
this.commit();
|
||||
};
|
||||
|
||||
private setOutside = (): void => {
|
||||
const { objectState } = this.props;
|
||||
objectState.outside = true;
|
||||
this.commit();
|
||||
};
|
||||
|
||||
private unsetOutside = (): void => {
|
||||
const { objectState } = this.props;
|
||||
objectState.outside = false;
|
||||
this.commit();
|
||||
};
|
||||
|
||||
private setKeyframe = (): void => {
|
||||
const { objectState } = this.props;
|
||||
objectState.keyframe = true;
|
||||
this.commit();
|
||||
};
|
||||
|
||||
private unsetKeyframe = (): void => {
|
||||
const { objectState } = this.props;
|
||||
objectState.keyframe = false;
|
||||
this.commit();
|
||||
};
|
||||
|
||||
private commit(): void {
|
||||
const {
|
||||
objectState,
|
||||
updateAnnotations,
|
||||
} = this.props;
|
||||
|
||||
updateAnnotations([objectState]);
|
||||
}
|
||||
|
||||
private changeFrame(frame: number): void {
|
||||
const { changeFrame, canvasInstance } = this.props;
|
||||
if (canvasInstance.isAbleToChangeFrame()) {
|
||||
changeFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
const {
|
||||
objectState,
|
||||
normalizedKeyMap,
|
||||
frameNumber,
|
||||
outsideDisabled,
|
||||
hiddenDisabled,
|
||||
keyframeDisabled,
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
first,
|
||||
prev,
|
||||
next,
|
||||
last,
|
||||
} = objectState.keyframes || {
|
||||
first: null, // shapes don't have keyframes, so we use null
|
||||
prev: null,
|
||||
next: null,
|
||||
last: null,
|
||||
};
|
||||
|
||||
return (
|
||||
<ItemButtonsComponent
|
||||
objectType={objectState.objectType}
|
||||
shapeType={objectState.shapeType}
|
||||
occluded={objectState.occluded}
|
||||
outside={objectState.outside}
|
||||
locked={objectState.lock}
|
||||
pinned={objectState.pinned}
|
||||
hidden={objectState.hidden}
|
||||
keyframe={objectState.keyframe}
|
||||
switchOccludedShortcut={normalizedKeyMap.SWITCH_OCCLUDED}
|
||||
switchOutsideShortcut={normalizedKeyMap.SWITCH_OUTSIDE}
|
||||
switchLockShortcut={normalizedKeyMap.SWITCH_LOCK}
|
||||
switchHiddenShortcut={normalizedKeyMap.SWITCH_HIDDEN}
|
||||
switchKeyFrameShortcut={normalizedKeyMap.SWITCH_KEYFRAME}
|
||||
nextKeyFrameShortcut={normalizedKeyMap.NEXT_KEY_FRAME}
|
||||
prevKeyFrameShortcut={normalizedKeyMap.PREV_KEY_FRAME}
|
||||
outsideDisabled={outsideDisabled}
|
||||
hiddenDisabled={hiddenDisabled}
|
||||
keyframeDisabled={keyframeDisabled}
|
||||
|
||||
navigateFirstKeyframe={
|
||||
first >= frameNumber || first === null
|
||||
? null : this.navigateFirstKeyframe
|
||||
}
|
||||
navigatePrevKeyframe={
|
||||
prev === frameNumber || prev === null
|
||||
? null : this.navigatePrevKeyframe
|
||||
}
|
||||
navigateNextKeyframe={
|
||||
next === frameNumber || next === null
|
||||
? null : this.navigateNextKeyframe
|
||||
}
|
||||
navigateLastKeyframe={
|
||||
last <= frameNumber || last === null
|
||||
? null : this.navigateLastKeyframe
|
||||
}
|
||||
|
||||
setOccluded={this.setOccluded}
|
||||
unsetOccluded={this.unsetOccluded}
|
||||
setOutside={this.setOutside}
|
||||
unsetOutside={this.unsetOutside}
|
||||
setKeyframe={this.setKeyframe}
|
||||
unsetKeyframe={this.unsetKeyframe}
|
||||
lock={this.lock}
|
||||
unlock={this.unlock}
|
||||
pin={this.pin}
|
||||
unpin={this.unpin}
|
||||
hide={this.hide}
|
||||
show={this.show}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(ItemButtonsWrapper);
|
||||
@ -1,254 +0,0 @@
|
||||
// Copyright (C) 2020 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { RadioChangeEvent } from 'antd/lib/radio';
|
||||
import { SliderValue } from 'antd/lib/slider';
|
||||
import { CheckboxChangeEvent } from 'antd/lib/checkbox';
|
||||
|
||||
import ObjectsSidebarComponent from 'components/annotation-page/standard-workspace/objects-side-bar/objects-side-bar';
|
||||
import {
|
||||
CombinedState,
|
||||
ColorBy,
|
||||
} from 'reducers/interfaces';
|
||||
|
||||
import {
|
||||
collapseSidebar as collapseSidebarAction,
|
||||
collapseAppearance as collapseAppearanceAction,
|
||||
updateTabContentHeight as updateTabContentHeightAction,
|
||||
} from 'actions/annotation-actions';
|
||||
|
||||
import {
|
||||
changeShapesColorBy as changeShapesColorByAction,
|
||||
changeShapesOpacity as changeShapesOpacityAction,
|
||||
changeSelectedShapesOpacity as changeSelectedShapesOpacityAction,
|
||||
changeShapesBlackBorders as changeShapesBlackBordersAction,
|
||||
changeShowBitmap as changeShowUnlabeledRegionsAction,
|
||||
changeShowProjections as changeShowProjectionsAction,
|
||||
} from 'actions/settings-actions';
|
||||
|
||||
import { Canvas } from 'cvat-canvas-wrapper';
|
||||
|
||||
interface StateToProps {
|
||||
sidebarCollapsed: boolean;
|
||||
appearanceCollapsed: boolean;
|
||||
colorBy: ColorBy;
|
||||
opacity: number;
|
||||
selectedOpacity: number;
|
||||
blackBorders: boolean;
|
||||
showBitmap: boolean;
|
||||
showProjections: boolean;
|
||||
canvasInstance: Canvas;
|
||||
}
|
||||
|
||||
interface DispatchToProps {
|
||||
collapseSidebar(): void;
|
||||
collapseAppearance(): void;
|
||||
updateTabContentHeight(): void;
|
||||
changeShapesColorBy(colorBy: ColorBy): void;
|
||||
changeShapesOpacity(shapesOpacity: number): void;
|
||||
changeSelectedShapesOpacity(selectedShapesOpacity: number): void;
|
||||
changeShapesBlackBorders(blackBorders: boolean): void;
|
||||
changeShowBitmap(showBitmap: boolean): void;
|
||||
changeShowProjections(showProjections: boolean): void;
|
||||
}
|
||||
|
||||
function mapStateToProps(state: CombinedState): StateToProps {
|
||||
const {
|
||||
annotation: {
|
||||
sidebarCollapsed,
|
||||
appearanceCollapsed,
|
||||
canvas: {
|
||||
instance: canvasInstance,
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
shapes: {
|
||||
colorBy,
|
||||
opacity,
|
||||
selectedOpacity,
|
||||
blackBorders,
|
||||
showBitmap,
|
||||
showProjections,
|
||||
},
|
||||
},
|
||||
} = state;
|
||||
|
||||
return {
|
||||
sidebarCollapsed,
|
||||
appearanceCollapsed,
|
||||
colorBy,
|
||||
opacity,
|
||||
selectedOpacity,
|
||||
blackBorders,
|
||||
showBitmap,
|
||||
showProjections,
|
||||
canvasInstance,
|
||||
};
|
||||
}
|
||||
|
||||
function computeHeight(): number {
|
||||
const [sidebar] = window.document.getElementsByClassName('cvat-objects-sidebar');
|
||||
const [appearance] = window.document.getElementsByClassName('cvat-objects-appearance-collapse');
|
||||
const [tabs] = Array.from(
|
||||
window.document.querySelectorAll('.cvat-objects-sidebar-tabs > .ant-tabs-card-bar'),
|
||||
);
|
||||
|
||||
if (sidebar && appearance && tabs) {
|
||||
const maxHeight = sidebar ? sidebar.clientHeight : 0;
|
||||
const appearanceHeight = appearance ? appearance.clientHeight : 0;
|
||||
const tabsHeight = tabs ? tabs.clientHeight : 0;
|
||||
return maxHeight - appearanceHeight - tabsHeight;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch: any): DispatchToProps {
|
||||
return {
|
||||
collapseSidebar(): void {
|
||||
dispatch(collapseSidebarAction());
|
||||
},
|
||||
collapseAppearance(): void {
|
||||
dispatch(collapseAppearanceAction());
|
||||
|
||||
const [collapser] = window.document
|
||||
.getElementsByClassName('cvat-objects-appearance-collapse');
|
||||
|
||||
if (collapser) {
|
||||
collapser.addEventListener('transitionend', () => {
|
||||
dispatch(
|
||||
updateTabContentHeightAction(
|
||||
computeHeight(),
|
||||
),
|
||||
);
|
||||
}, { once: true });
|
||||
}
|
||||
},
|
||||
updateTabContentHeight(): void {
|
||||
dispatch(
|
||||
updateTabContentHeightAction(
|
||||
computeHeight(),
|
||||
),
|
||||
);
|
||||
},
|
||||
changeShapesColorBy(colorBy: ColorBy): void {
|
||||
dispatch(changeShapesColorByAction(colorBy));
|
||||
},
|
||||
changeShapesOpacity(shapesOpacity: number): void {
|
||||
dispatch(changeShapesOpacityAction(shapesOpacity));
|
||||
},
|
||||
changeSelectedShapesOpacity(selectedShapesOpacity: number): void {
|
||||
dispatch(changeSelectedShapesOpacityAction(selectedShapesOpacity));
|
||||
},
|
||||
changeShapesBlackBorders(blackBorders: boolean): void {
|
||||
dispatch(changeShapesBlackBordersAction(blackBorders));
|
||||
},
|
||||
changeShowBitmap(showBitmap: boolean) {
|
||||
dispatch(changeShowUnlabeledRegionsAction(showBitmap));
|
||||
},
|
||||
changeShowProjections(showProjections: boolean) {
|
||||
dispatch(changeShowProjectionsAction(showProjections));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
type Props = StateToProps & DispatchToProps;
|
||||
class ObjectsSideBarContainer extends React.PureComponent<Props> {
|
||||
public componentDidMount(): void {
|
||||
window.addEventListener('resize', this.alignTabHeight);
|
||||
this.alignTabHeight();
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
window.removeEventListener('resize', this.alignTabHeight);
|
||||
}
|
||||
|
||||
private alignTabHeight = (): void => {
|
||||
const {
|
||||
sidebarCollapsed,
|
||||
updateTabContentHeight,
|
||||
} = this.props;
|
||||
|
||||
if (!sidebarCollapsed) {
|
||||
updateTabContentHeight();
|
||||
}
|
||||
};
|
||||
|
||||
private changeShapesColorBy = (event: RadioChangeEvent): void => {
|
||||
const { changeShapesColorBy } = this.props;
|
||||
changeShapesColorBy(event.target.value);
|
||||
};
|
||||
|
||||
private changeShapesOpacity = (value: SliderValue): void => {
|
||||
const { changeShapesOpacity } = this.props;
|
||||
changeShapesOpacity(value as number);
|
||||
};
|
||||
|
||||
private changeSelectedShapesOpacity = (value: SliderValue): void => {
|
||||
const { changeSelectedShapesOpacity } = this.props;
|
||||
changeSelectedShapesOpacity(value as number);
|
||||
};
|
||||
|
||||
private changeShapesBlackBorders = (event: CheckboxChangeEvent): void => {
|
||||
const { changeShapesBlackBorders } = this.props;
|
||||
changeShapesBlackBorders(event.target.checked);
|
||||
};
|
||||
|
||||
private changeShowBitmap = (event: CheckboxChangeEvent): void => {
|
||||
const { changeShowBitmap } = this.props;
|
||||
changeShowBitmap(event.target.checked);
|
||||
};
|
||||
|
||||
private changeShowProjections = (event: CheckboxChangeEvent): void => {
|
||||
const { changeShowProjections } = this.props;
|
||||
changeShowProjections(event.target.checked);
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
const {
|
||||
sidebarCollapsed,
|
||||
appearanceCollapsed,
|
||||
colorBy,
|
||||
opacity,
|
||||
selectedOpacity,
|
||||
blackBorders,
|
||||
showBitmap,
|
||||
showProjections,
|
||||
canvasInstance,
|
||||
collapseSidebar,
|
||||
collapseAppearance,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<ObjectsSidebarComponent
|
||||
sidebarCollapsed={sidebarCollapsed}
|
||||
appearanceCollapsed={appearanceCollapsed}
|
||||
colorBy={colorBy}
|
||||
opacity={opacity}
|
||||
selectedOpacity={selectedOpacity}
|
||||
blackBorders={blackBorders}
|
||||
showBitmap={showBitmap}
|
||||
showProjections={showProjections}
|
||||
canvasInstance={canvasInstance}
|
||||
collapseSidebar={collapseSidebar}
|
||||
collapseAppearance={collapseAppearance}
|
||||
changeShapesColorBy={this.changeShapesColorBy}
|
||||
changeShapesOpacity={this.changeShapesOpacity}
|
||||
changeSelectedShapesOpacity={this.changeSelectedShapesOpacity}
|
||||
changeShapesBlackBorders={this.changeShapesBlackBorders}
|
||||
changeShowBitmap={this.changeShowBitmap}
|
||||
changeShowProjections={this.changeShowProjections}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(ObjectsSideBarContainer);
|
||||
Loading…
Reference in New Issue