Skip to content

Commit 3c23a87

Browse files
feat: add Alt key to temporarily toggle between Brush and Eraser modes
1 parent bdb62af commit 3c23a87

1 file changed

Lines changed: 61 additions & 7 deletions

File tree

src/components/panel/editor/ImageCanvas.tsx

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,8 @@ const ImageCanvas = memo(
759759
const [isFadingIn, setIsFadingIn] = useState(false);
760760
const prevImageIdentityRef = useRef(selectedImage.thumbnailUrl);
761761

762+
const [baseTool, setBaseTool] = useState<ToolType>(brushSettings?.tool ?? ToolType.Brush);
763+
762764
useEffect(() => {
763765
const newSrc = finalPreviewUrl || selectedImage.thumbnailUrl;
764766
const isNewImage = prevImageIdentityRef.current !== selectedImage.thumbnailUrl;
@@ -804,6 +806,32 @@ const ImageCanvas = memo(
804806
}
805807
}, [finalPreviewUrl, selectedImage.thumbnailUrl, isSliderDragging]);
806808

809+
useEffect(() => {
810+
setBaseTool(brushSettings?.tool ?? ToolType.Brush);
811+
}, [brushSettings?.tool]);
812+
813+
useEffect(() => {
814+
const handleKeyDown = (e: KeyboardEvent) => {
815+
if (e.key === 'Alt') {
816+
e.preventDefault();
817+
(window as any).altKeyDown = true;
818+
}
819+
};
820+
const handleKeyUp = (e: KeyboardEvent) => {
821+
if (e.key === 'Alt') {
822+
e.preventDefault();
823+
(window as any).altKeyDown = false;
824+
}
825+
};
826+
window.addEventListener('keydown', handleKeyDown);
827+
window.addEventListener('keyup', handleKeyUp);
828+
return () => {
829+
window.removeEventListener('keydown', handleKeyDown);
830+
window.removeEventListener('keyup', handleKeyUp);
831+
delete (window as any).altKeyDown;
832+
};
833+
}, []);
834+
807835
const activeContainer = useMemo(() => {
808836
if (isMasking) {
809837
return adjustments.masks.find((c: MaskContainer) => c.id === activeMaskContainerId);
@@ -1088,7 +1116,16 @@ const ImageCanvas = memo(
10881116
return;
10891117
}
10901118

1091-
const toolType = isAiSubjectActive ? ToolType.AiSeletor : ToolType.Brush;
1119+
const isAltPressed = e.evt.altKey;
1120+
let effectiveTool;
1121+
1122+
if (isAiSubjectActive) {
1123+
effectiveTool = ToolType.AiSeletor;
1124+
} else if (isAltPressed) {
1125+
effectiveTool = baseTool === ToolType.Brush ? ToolType.Eraser : ToolType.Brush;
1126+
} else {
1127+
effectiveTool = baseTool;
1128+
}
10921129
const isShiftClick = isBrushActive && e.evt.shiftKey && lastBrushPoint.current;
10931130

10941131
if (isShiftClick) {
@@ -1121,7 +1158,7 @@ const ImageCanvas = memo(
11211158
brushSize: brushImageSpaceSize,
11221159
feather: brushSettings?.feather ? brushSettings?.feather / 100 : 0,
11231160
points: interpolatedPoints,
1124-
tool: brushSettings?.tool ?? ToolType.Brush,
1161+
tool: effectiveTool,
11251162
};
11261163

11271164
const activeId = isMasking ? activeMaskId : activeAiSubMaskId;
@@ -1146,7 +1183,7 @@ const ImageCanvas = memo(
11461183
const newLine: DrawnLine = {
11471184
brushSize: isBrushActive && brushSettings?.size ? brushStageSize : 2,
11481185
points: [pos],
1149-
tool: toolType,
1186+
tool: effectiveTool,
11501187
};
11511188
currentLine.current = newLine;
11521189
} else {
@@ -1182,6 +1219,7 @@ const ImageCanvas = memo(
11821219
isToolActive,
11831220
brushImageSpaceSize,
11841221
brushStageSize,
1222+
baseTool,
11851223
],
11861224
);
11871225

@@ -1302,14 +1340,24 @@ const ImageCanvas = memo(
13021340
const cropX = crop ? (isPercent ? (crop.x / 100) * effectiveImageDimensions.width : crop.x) : 0;
13031341
const cropY = crop ? (isPercent ? (crop.y / 100) * effectiveImageDimensions.height : crop.y) : 0;
13041342

1343+
const isAltPressedDuringMove = (window as any).altKeyDown || false;
1344+
let effectiveToolForPreview;
1345+
1346+
if (isAltPressedDuringMove) {
1347+
// Alt toggles: Brush -> Eraser, Eraser -> Brush
1348+
effectiveToolForPreview = baseTool === ToolType.Brush ? ToolType.Eraser : ToolType.Brush;
1349+
} else {
1350+
effectiveToolForPreview = baseTool;
1351+
}
1352+
13051353
const imageSpaceLine: DrawnLine = {
13061354
brushSize: brushImageSpaceSize,
13071355
feather: brushSettings?.feather ? brushSettings?.feather / 100 : 0,
13081356
points: updatedLine.points.map((p: Coord) => ({
13091357
x: p.x / scale + cropX,
13101358
y: p.y / scale + cropY,
13111359
})),
1312-
tool: brushSettings?.tool ?? ToolType.Brush,
1360+
tool: effectiveToolForPreview,
13131361
};
13141362

13151363
const existingLines = activeSubMask.parameters?.lines || [];
@@ -1351,6 +1399,7 @@ const ImageCanvas = memo(
13511399
isMasking,
13521400
localInitialDrawParams,
13531401
brushImageSpaceSize,
1402+
baseTool,
13541403
],
13551404
);
13561405

@@ -1458,14 +1507,17 @@ const ImageCanvas = memo(
14581507
const activeId = isMasking ? activeMaskId : activeAiSubMaskId;
14591508

14601509
if (isBrushActive) {
1510+
const wasAltPressed = (window as any).altKeyDown || false;
1511+
const effectiveToolForFinal = wasAltPressed ? (baseTool === ToolType.Brush ? ToolType.Eraser : ToolType.Brush) : baseTool;
1512+
14611513
const imageSpaceLine: DrawnLine = {
14621514
brushSize: brushImageSpaceSize,
14631515
feather: brushSettings?.feather ? brushSettings?.feather / 100 : 0,
14641516
points: line.points.map((p: Coord) => ({
14651517
x: p.x / scale + cropX,
14661518
y: p.y / scale + cropY,
14671519
})),
1468-
tool: brushSettings?.tool ?? ToolType.Brush,
1520+
tool: effectiveToolForFinal,
14691521
};
14701522

14711523
const existingLines = activeSubMask?.parameters.lines || [];
@@ -1503,6 +1555,7 @@ const ImageCanvas = memo(
15031555
localInitialDrawParams,
15041556
brushImageSpaceSize,
15051557
brushStageSize,
1558+
baseTool,
15061559
]);
15071560

15081561
const handleMouseEnter = useCallback(() => {
@@ -1903,7 +1956,6 @@ const ImageCanvas = memo(
19031956
);
19041957
})}
19051958

1906-
{/* Visualizer for drawing new AI Bounding Box */}
19071959
{previewBox && (
19081960
<Rect
19091961
x={Math.min(previewBox.start.x, previewBox.end.x)}
@@ -1920,7 +1972,9 @@ const ImageCanvas = memo(
19201972
<Circle
19211973
listening={false}
19221974
perfectDrawEnabled={false}
1923-
stroke={brushSettings?.tool === ToolType.Eraser ? '#f43f5e' : '#0ea5e9'}
1975+
stroke={(window as any).altKeyDown ?
1976+
(baseTool === ToolType.Brush ? '#f43f5e' : '#0ea5e9') :
1977+
(baseTool === ToolType.Eraser ? '#f43f5e' : '#0ea5e9')}
19241978
radius={brushStageSize / 2}
19251979
strokeWidth={1}
19261980
x={cursorPreview.x}

0 commit comments

Comments
 (0)