feat(ui): bbox tool

This commit is contained in:
psychedelicious 2024-06-12 17:12:53 +10:00
parent 95d6183a6c
commit 6c1d1588fc
3 changed files with 30 additions and 11 deletions

View File

@ -11,7 +11,14 @@ import {
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PiArrowsOutCardinalBold, PiEraserBold, PiHandBold, PiPaintBrushBold, PiRectangleBold } from 'react-icons/pi'; import {
PiArrowsOutCardinalBold,
PiBoundingBoxBold,
PiEraserBold,
PiHandBold,
PiPaintBrushBold,
PiRectangleBold,
} from 'react-icons/pi';
const selectIsDisabled = createSelector(selectControlLayersSlice, (controlLayers) => { const selectIsDisabled = createSelector(selectControlLayersSlice, (controlLayers) => {
const selectedLayer = controlLayers.present.layers.find((l) => l.id === controlLayers.present.selectedLayerId); const selectedLayer = controlLayers.present.layers.find((l) => l.id === controlLayers.present.selectedLayerId);
@ -45,6 +52,10 @@ export const ToolChooser: React.FC = () => {
$tool.set('view'); $tool.set('view');
}, []); }, []);
useHotkeys('h', setToolToView, { enabled: !isDisabled }, [isDisabled]); useHotkeys('h', setToolToView, { enabled: !isDisabled }, [isDisabled]);
const setToolToBbox = useCallback(() => {
$tool.set('bbox');
}, []);
useHotkeys('q', setToolToBbox, { enabled: !isDisabled }, [isDisabled]);
const resetSelectedLayer = useCallback(() => { const resetSelectedLayer = useCallback(() => {
if (selectedLayerId === null) { if (selectedLayerId === null) {
@ -101,6 +112,14 @@ export const ToolChooser: React.FC = () => {
onClick={setToolToView} onClick={setToolToView}
isDisabled={isDisabled} isDisabled={isDisabled}
/> />
<IconButton
aria-label={`${t('controlLayers.bbox')} (Q)`}
tooltip={`${t('controlLayers.bbox')} (Q)`}
icon={<PiBoundingBoxBold />}
variant={tool === 'bbox' ? 'solid' : 'outline'}
onClick={setToolToBbox}
isDisabled={isDisabled}
/>
</ButtonGroup> </ButtonGroup>
); );
}; };

View File

@ -62,10 +62,10 @@ export const getBboxPreviewGroup = (
// Use a transformer for the generation bbox. Transformers need some shape to transform, we will use a fully // Use a transformer for the generation bbox. Transformers need some shape to transform, we will use a fully
// transparent rect for this purpose. // transparent rect for this purpose.
bboxPreviewGroup = new Konva.Group({ id: PREVIEW_GENERATION_BBOX_GROUP }); bboxPreviewGroup = new Konva.Group({ id: PREVIEW_GENERATION_BBOX_GROUP, listening: false });
const bboxRect = new Konva.Rect({ const bboxRect = new Konva.Rect({
id: PREVIEW_GENERATION_BBOX_DUMMY_RECT, id: PREVIEW_GENERATION_BBOX_DUMMY_RECT,
listening: true, listening: false,
strokeEnabled: false, strokeEnabled: false,
draggable: true, draggable: true,
...getBbox(), ...getBbox(),
@ -269,12 +269,10 @@ export const renderBboxPreview = (
); );
const bboxRect = bboxGroup.findOne<Konva.Rect>(`#${PREVIEW_GENERATION_BBOX_DUMMY_RECT}`); const bboxRect = bboxGroup.findOne<Konva.Rect>(`#${PREVIEW_GENERATION_BBOX_DUMMY_RECT}`);
const bboxTransformer = bboxGroup.findOne<Konva.Transformer>(`#${PREVIEW_GENERATION_BBOX_TRANSFORMER}`); const bboxTransformer = bboxGroup.findOne<Konva.Transformer>(`#${PREVIEW_GENERATION_BBOX_TRANSFORMER}`);
bboxGroup.listening(tool === 'bbox');
// This updates the bbox during transformation // This updates the bbox during transformation
bboxRect?.setAttrs({ ...bbox, scaleX: 1, scaleY: 1, listening: tool === 'move' }); bboxRect?.setAttrs({ ...bbox, scaleX: 1, scaleY: 1, listening: tool === 'bbox' });
bboxTransformer?.setAttrs({ bboxTransformer?.setAttrs({ listening: tool === 'bbox', enabledAnchors: tool === 'bbox' ? ALL_ANCHORS : NO_ANCHORS });
listening: tool === 'move',
enabledAnchors: tool === 'move' ? ALL_ANCHORS : NO_ANCHORS,
});
}; };
export const getToolPreviewGroup = (stage: Konva.Stage): Konva.Group => { export const getToolPreviewGroup = (stage: Konva.Stage): Konva.Group => {
@ -365,9 +363,11 @@ export const renderToolPreview = (
} else if (tool === 'rect') { } else if (tool === 'rect') {
// Rect gets a crosshair // Rect gets a crosshair
stage.container().style.cursor = 'crosshair'; stage.container().style.cursor = 'crosshair';
} else { } else if (tool === 'brush' || tool === 'eraser') {
// Else we hide the native cursor and use the konva-rendered brush preview // Hide the native cursor and use the konva-rendered brush preview
stage.container().style.cursor = 'none'; stage.container().style.cursor = 'none';
} else if (tool === 'bbox') {
stage.container().style.cursor = 'default';
} }
stage.draggable(tool === 'view'); stage.draggable(tool === 'view');

View File

@ -23,7 +23,7 @@ import type { IRect } from 'konva/lib/types';
import type { ImageDTO } from 'services/api/types'; import type { ImageDTO } from 'services/api/types';
import { z } from 'zod'; import { z } from 'zod';
const zTool = z.enum(['brush', 'eraser', 'move', 'rect', 'view']); const zTool = z.enum(['brush', 'eraser', 'move', 'rect', 'view', 'bbox']);
export type Tool = z.infer<typeof zTool>; export type Tool = z.infer<typeof zTool>;
const zDrawingTool = zTool.extract(['brush', 'eraser']); const zDrawingTool = zTool.extract(['brush', 'eraser']);