Skip to content

Commit

Permalink
[GSoC'24] Keyboard Shortcut Customization: Merge and Group handler fix (
Browse files Browse the repository at this point in the history
  • Loading branch information
tahamukhtar20 authored and Bradley Schultz committed Sep 12, 2024
1 parent 9db8817 commit 16c8414
Show file tree
Hide file tree
Showing 5 changed files with 261 additions and 233 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,30 @@ const componentShortcuts = {
sequences: ['shift+n'],
scope: ShortcutScope.STANDARD_WORKSPACE_CONTROLS,
},
SWITCH_GROUP_MODE_STANDARD_CONTROLS: {
name: 'Group mode',
description: 'Activate or deactivate mode to grouping shapes',
sequences: ['g'],
scope: ShortcutScope.STANDARD_WORKSPACE_CONTROLS,
},
RESET_GROUP_STANDARD_CONTROLS: {
name: 'Reset group',
description: 'Reset group for selected shapes (in group mode)',
sequences: ['shift+g'],
scope: ShortcutScope.STANDARD_WORKSPACE_CONTROLS,
},
SWITCH_MERGE_MODE_STANDARD_CONTROLS: {
name: 'Merge mode',
description: 'Activate or deactivate mode to merging shapes',
sequences: ['m'],
scope: ShortcutScope.STANDARD_WORKSPACE_CONTROLS,
},
SWITCH_SPLIT_MODE_STANDARD_CONTROLS: {
name: 'Split mode',
description: 'Activate or deactivate mode to splitting shapes',
sequences: ['alt+m'],
scope: ShortcutScope.STANDARD_WORKSPACE_CONTROLS,
},
};

registerComponentShortcuts(componentShortcuts);
Expand Down Expand Up @@ -161,6 +185,58 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
}
};

const dynamicMergeIconProps =
activeControl === ActiveControl.MERGE ?
{
className: 'cvat-merge-control cvat-active-canvas-control',
onClick: (): void => {
canvasInstance.merge({ enabled: false });
updateActiveControl(ActiveControl.CURSOR);
},
} :
{
className: 'cvat-merge-control',
onClick: (): void => {
canvasInstance.cancel();
canvasInstance.merge({ enabled: true });
updateActiveControl(ActiveControl.MERGE);
},
};

const dynamicGroupIconProps =
activeControl === ActiveControl.GROUP ?
{
className: 'cvat-group-control cvat-active-canvas-control',
onClick: (): void => {
canvasInstance.group({ enabled: false });
updateActiveControl(ActiveControl.CURSOR);
},
} :
{
className: 'cvat-group-control',
onClick: (): void => {
canvasInstance.cancel();
canvasInstance.group({ enabled: true });
updateActiveControl(ActiveControl.GROUP);
},
};

const dynamicTrackIconProps = activeControl === ActiveControl.SPLIT ?
{
className: 'cvat-split-track-control cvat-active-canvas-control',
onClick: (): void => {
canvasInstance.split({ enabled: false });
},
} :
{
className: 'cvat-split-track-control',
onClick: (): void => {
canvasInstance.cancel();
canvasInstance.split({ enabled: true });
updateActiveControl(ActiveControl.SPLIT);
},
};

let handlers: Partial<Record<keyof typeof componentShortcuts, (event?: KeyboardEvent) => void>> = {
CLOCKWISE_ROTATION_STANDARD_CONTROLS: (event: KeyboardEvent | undefined) => {
preventDefault(event);
Expand All @@ -170,6 +246,28 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
preventDefault(event);
rotateFrame(Rotation.ANTICLOCKWISE90);
},
SWITCH_GROUP_MODE_STANDARD_CONTROLS: (event: KeyboardEvent | undefined): void => {
if (event) event.preventDefault();
dynamicGroupIconProps.onClick();
},
RESET_GROUP_STANDARD_CONTROLS: (event: KeyboardEvent | undefined): void => {
if (event) event.preventDefault();
const grouping = activeControl === ActiveControl.GROUP;
if (!grouping) {
return;
}
resetGroup();
canvasInstance.group({ enabled: false });
updateActiveControl(ActiveControl.CURSOR);
},
SWITCH_MERGE_MODE_STANDARD_CONTROLS: (event: KeyboardEvent | undefined): void => {
if (event) event.preventDefault();
dynamicMergeIconProps.onClick();
},
SWITCH_SPLIT_MODE_STANDARD_CONTROLS: (event: KeyboardEvent | undefined) => {
if (event) event.preventDefault();
dynamicTrackIconProps.onClick();
},
};

const handleDrawMode = (event: KeyboardEvent | undefined, action: 'draw' | 'redraw'): void => {
Expand Down Expand Up @@ -339,22 +437,18 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
<hr />

<ObservedMergeControl
updateActiveControl={updateActiveControl}
canvasInstance={canvasInstance}
activeControl={activeControl}
dynamicIconProps={dynamicMergeIconProps}
disabled={controlsDisabled}
/>
<ObservedGroupControl
updateActiveControl={updateActiveControl}
resetGroup={resetGroup}
canvasInstance={canvasInstance}
activeControl={activeControl}
dynamicIconProps={dynamicGroupIconProps}
disabled={controlsDisabled}
/>
<ObservedSplitControl
updateActiveControl={updateActiveControl}
canvasInstance={canvasInstance}
activeControl={activeControl}
dynamicIconProps={dynamicTrackIconProps}
disabled={controlsDisabled}
/>
<ObservedJoinControl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,122 +7,42 @@ import React from 'react';
import Icon from '@ant-design/icons';

import { GroupIcon } from 'icons';
import { Canvas } from 'cvat-canvas-wrapper';
import { Canvas3d } from 'cvat-canvas3d-wrapper';
import { ActiveControl, CombinedState } from 'reducers';
import CVATTooltip from 'components/common/cvat-tooltip';
import GlobalHotKeys from 'utils/mousetrap-react';
import { ShortcutScope } from 'utils/enums';
import { registerComponentShortcuts } from 'actions/shortcuts-actions';
import { subKeyMap } from 'utils/component-subkeymap';
import { useSelector } from 'react-redux';
import { CombinedState } from 'reducers';
import { Canvas3d } from 'cvat-canvas3d-wrapper';
import { Canvas } from 'cvat-canvas-wrapper';

export interface Props {
updateActiveControl(activeControl: ActiveControl): void;
resetGroup(): void;
canvasInstance: Canvas | Canvas3d;
activeControl: ActiveControl;
disabled?: boolean;
dynamicIconProps: Record<string, any>;
canvasInstance: Canvas | Canvas3d;
}

const componentShortcuts = {
SWITCH_GROUP_MODE_STANDARD_CONTROLS: {
name: 'Group mode',
description: 'Activate or deactivate mode to grouping shapes',
sequences: ['g'],
scope: ShortcutScope.STANDARD_WORKSPACE_CONTROLS,
},
RESET_GROUP_STANDARD_CONTROLS: {
name: 'Reset group',
description: 'Reset group for selected shapes (in group mode)',
sequences: ['shift+g'],
scope: ShortcutScope.STANDARD_WORKSPACE_CONTROLS,
},
SWITCH_GROUP_MODE_STANDARD_3D_CONTROLS: {
name: 'Group mode',
description: 'Activate or deactivate mode to grouping shapes',
sequences: ['g'],
scope: ShortcutScope['3D_ANNOTATION_WORKSPACE_CONTROLS'],
},
RESET_GROUP_STANDARD_3D_CONTROLS: {
name: 'Reset group',
description: 'Reset group for selected shapes (in group mode)',
sequences: ['shift+g'],
scope: ShortcutScope['3D_ANNOTATION_WORKSPACE_CONTROLS'],
},
};

registerComponentShortcuts(componentShortcuts);

function GroupControl(props: Props): JSX.Element {
const {
updateActiveControl,
resetGroup,
activeControl,
canvasInstance,
disabled,
dynamicIconProps,
canvasInstance,
} = props;

const { keyMap, normalizedKeyMap } = useSelector((state: CombinedState) => state.shortcuts);

const dynamicIconProps =
activeControl === ActiveControl.GROUP ?
{
className: 'cvat-group-control cvat-active-canvas-control',
onClick: (): void => {
canvasInstance.group({ enabled: false });
updateActiveControl(ActiveControl.CURSOR);
},
} :
{
className: 'cvat-group-control',
onClick: (): void => {
canvasInstance.cancel();
canvasInstance.group({ enabled: true });
updateActiveControl(ActiveControl.GROUP);
},
};

const handleSwitchGroupMode = (event: KeyboardEvent | undefined): void => {
if (event) event.preventDefault();
dynamicIconProps.onClick();
};

const handleResetGroup = (event: KeyboardEvent | undefined): void => {
if (event) event.preventDefault();
const grouping = activeControl === ActiveControl.GROUP;
if (!grouping) {
return;
}
resetGroup();
canvasInstance.group({ enabled: false });
updateActiveControl(ActiveControl.CURSOR);
};

const handlers: Partial<Record<keyof typeof componentShortcuts, (event?: KeyboardEvent) => void>> = {
SWITCH_GROUP_MODE_STANDARD_CONTROLS: handleSwitchGroupMode,
RESET_GROUP_STANDARD_CONTROLS: handleResetGroup,
SWITCH_GROUP_MODE_STANDARD_3D_CONTROLS: handleSwitchGroupMode,
RESET_GROUP_STANDARD_3D_CONTROLS: handleResetGroup,
};
const { normalizedKeyMap } = useSelector((state: CombinedState) => state.shortcuts);

const title = [
`Group shapes/tracks ${normalizedKeyMap.SWITCH_GROUP_MODE_STANDARD_CONTROLS}`,
`Select and press ${normalizedKeyMap.RESET_GROUP_STANDARD_CONTROLS} to reset a group.`,
].join(' ');
const title = [];
if (canvasInstance instanceof Canvas) {
title.push(`Group shapes ${normalizedKeyMap.SWITCH_GROUP_MODE_STANDARD_CONTROLS}`);
title.push(`Select and press ${normalizedKeyMap.RESET_GROUP_STANDARD_CONTROLS} to reset a group.`);
} else if (canvasInstance instanceof Canvas3d) {
title.push(`Group shapes/tracks ${normalizedKeyMap.SWITCH_GROUP_MODE_STANDARD_3D_CONTROLS}`);
title.push(`Select and press ${normalizedKeyMap.RESET_GROUP_STANDARD_3D_CONTROLS} to reset a group.`);
}

return disabled ? (
<Icon className='cvat-group-control cvat-disabled-canvas-control' component={GroupIcon} />
) : (
<>
<GlobalHotKeys
keyMap={subKeyMap(componentShortcuts, keyMap)}
handlers={handlers}
/>
<CVATTooltip title={title} placement='right'>
<Icon {...dynamicIconProps} component={GroupIcon} />
</CVATTooltip>
</>
<CVATTooltip title={title.join(' ')} placement='right'>
<Icon {...dynamicIconProps} component={GroupIcon} />
</CVATTooltip>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,87 +7,39 @@ import React from 'react';
import Icon from '@ant-design/icons';

import { MergeIcon } from 'icons';
import { Canvas } from 'cvat-canvas-wrapper';
import { Canvas3d } from 'cvat-canvas3d-wrapper';
import { ActiveControl, CombinedState } from 'reducers';
import { CombinedState } from 'reducers';
import CVATTooltip from 'components/common/cvat-tooltip';
import GlobalHotKeys from 'utils/mousetrap-react';
import { registerComponentShortcuts } from 'actions/shortcuts-actions';
import { ShortcutScope } from 'utils/enums';
import { useSelector } from 'react-redux';
import { subKeyMap } from 'utils/component-subkeymap';
import { Canvas3d } from 'cvat-canvas3d-wrapper';
import { Canvas } from 'cvat-canvas-wrapper';

export interface Props {
updateActiveControl(activeControl: ActiveControl): void;
canvasInstance: Canvas | Canvas3d;
activeControl: ActiveControl;
disabled?: boolean;
dynamicIconProps: Record<string, any>;
canvasInstance: Canvas | Canvas3d;
}

const componentShortcuts = {
SWITCH_MERGE_MODE_STANDARD_CONTROLS: {
name: 'Merge mode',
description: 'Activate or deactivate mode to merging shapes',
sequences: ['m'],
scope: ShortcutScope.STANDARD_WORKSPACE_CONTROLS,
},
SWITCH_MERGE_MODE_STANDARD_3D_CONTROLS: {
name: 'Merge mode',
description: 'Activate or deactivate mode to merging shapes',
sequences: ['m'],
scope: ShortcutScope['3D_ANNOTATION_WORKSPACE_CONTROLS'],
},
};

registerComponentShortcuts(componentShortcuts);

function MergeControl(props: Props): JSX.Element {
const {
activeControl, canvasInstance, updateActiveControl, disabled,
disabled,
dynamicIconProps,
canvasInstance,
} = props;

const { keyMap, normalizedKeyMap } = useSelector((state: CombinedState) => state.shortcuts);

const dynamicIconProps =
activeControl === ActiveControl.MERGE ?
{
className: 'cvat-merge-control cvat-active-canvas-control',
onClick: (): void => {
canvasInstance.merge({ enabled: false });
updateActiveControl(ActiveControl.CURSOR);
},
} :
{
className: 'cvat-merge-control',
onClick: (): void => {
canvasInstance.cancel();
canvasInstance.merge({ enabled: true });
updateActiveControl(ActiveControl.MERGE);
},
};

const handleMergeMode = (event: KeyboardEvent | undefined): void => {
if (event) event.preventDefault();
dynamicIconProps.onClick();
};

const handlers: Partial<Record<keyof typeof componentShortcuts, (event?: KeyboardEvent) => void>> = {
SWITCH_MERGE_MODE_STANDARD_CONTROLS: handleMergeMode,
SWITCH_MERGE_MODE_STANDARD_3D_CONTROLS: handleMergeMode,
};
const { normalizedKeyMap } = useSelector((state: CombinedState) => state.shortcuts);

return disabled ? (
<Icon className='cvat-merge-control cvat-disabled-canvas-control' component={MergeIcon} />
) : (
<>
<GlobalHotKeys
keyMap={subKeyMap(componentShortcuts, keyMap)}
handlers={handlers}
/>
<CVATTooltip title={`Merge shapes/tracks ${normalizedKeyMap.SWITCH_MERGE_MODE_STANDARD_CONTROLS}`} placement='right'>
<Icon {...dynamicIconProps} component={MergeIcon} />
</CVATTooltip>
</>
<CVATTooltip
title={`Merge shapes/tracks ${
canvasInstance instanceof Canvas ?
normalizedKeyMap.SWITCH_MERGE_MODE_STANDARD_CONTROLS :
normalizedKeyMap.SWITCH_MERGE_MODE_STANDARD_3D_CONTROLS}`}
placement='right'
>
<Icon {...dynamicIconProps} component={MergeIcon} />
</CVATTooltip>
);
}

Expand Down
Loading

0 comments on commit 16c8414

Please sign in to comment.