diff --git a/frontend/src/common/hooks/useSingleAndDoubleClick.ts b/frontend/src/common/hooks/useSingleAndDoubleClick.ts new file mode 100644 index 0000000000..8b2b35bcb1 --- /dev/null +++ b/frontend/src/common/hooks/useSingleAndDoubleClick.ts @@ -0,0 +1,28 @@ +// https://stackoverflow.com/a/73731908 + +import { useEffect, useState } from 'react'; + +export function useSingleAndDoubleClick( + handleSingleClick: () => void, + handleDoubleClick: () => void, + delay = 250 +) { + const [click, setClick] = useState(0); + + useEffect(() => { + const timer = setTimeout(() => { + if (click === 1) { + handleSingleClick(); + } + setClick(0); + }, delay); + + if (click === 2) { + handleDoubleClick(); + } + + return () => clearTimeout(timer); + }, [click, handleSingleClick, handleDoubleClick, delay]); + + return () => setClick((prev) => prev + 1); +} diff --git a/frontend/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx b/frontend/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx index 531abd0820..9b1eb034a4 100644 --- a/frontend/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx +++ b/frontend/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx @@ -42,6 +42,7 @@ import { } from 'features/canvas/store/canvasTypes'; import { ChangeEvent } from 'react'; import { useTranslation } from 'react-i18next'; +import { useSingleAndDoubleClick } from 'common/hooks/useSingleAndDoubleClick'; export const selector = createSelector( [systemSelector, canvasSelector, isStagingSelector], @@ -156,7 +157,12 @@ const IAICanvasOutpaintingControls = () => { const handleSelectMoveTool = () => dispatch(setTool('move')); - const handleResetCanvasView = () => { + const handleClickResetCanvasView = useSingleAndDoubleClick( + () => handleResetCanvasView(false), + () => handleResetCanvasView(true) + ); + + const handleResetCanvasView = (shouldScaleTo1 = false) => { const canvasBaseLayer = getCanvasBaseLayer(); if (!canvasBaseLayer) return; const clientRect = canvasBaseLayer.getClientRect({ @@ -165,6 +171,7 @@ const IAICanvasOutpaintingControls = () => { dispatch( resetCanvasView({ contentRect: clientRect, + shouldScaleTo1, }) ); }; @@ -247,7 +254,7 @@ const IAICanvasOutpaintingControls = () => { aria-label={`${t('unifiedcanvas:resetView')} (R)`} tooltip={`${t('unifiedcanvas:resetView')} (R)`} icon={} - onClick={handleResetCanvasView} + onClick={handleClickResetCanvasView} /> diff --git a/frontend/src/features/canvas/store/canvasSlice.ts b/frontend/src/features/canvas/store/canvasSlice.ts index 31d02d2790..29aaa6342d 100644 --- a/frontend/src/features/canvas/store/canvasSlice.ts +++ b/frontend/src/features/canvas/store/canvasSlice.ts @@ -602,9 +602,10 @@ export const canvasSlice = createSlice({ state, action: PayloadAction<{ contentRect: IRect; + shouldScaleTo1?: boolean; }> ) => { - const { contentRect } = action.payload; + const { contentRect, shouldScaleTo1 } = action.payload; const { stageDimensions: { width: stageWidth, height: stageHeight }, } = state; @@ -612,13 +613,15 @@ export const canvasSlice = createSlice({ const { x, y, width, height } = contentRect; if (width !== 0 && height !== 0) { - const newScale = calculateScale( - stageWidth, - stageHeight, - width, - height, - STAGE_PADDING_PERCENTAGE - ); + const newScale = shouldScaleTo1 + ? 1 + : calculateScale( + stageWidth, + stageHeight, + width, + height, + STAGE_PADDING_PERCENTAGE + ); const newCoordinates = calculateCoordinates( stageWidth, diff --git a/frontend/src/features/gallery/components/CurrentImageButtons.tsx b/frontend/src/features/gallery/components/CurrentImageButtons.tsx index 557f6c2e60..90a5efb650 100644 --- a/frontend/src/features/gallery/components/CurrentImageButtons.tsx +++ b/frontend/src/features/gallery/components/CurrentImageButtons.tsx @@ -399,11 +399,11 @@ const CurrentImageButtons = () => { {t('options:copyImageToLink')} - } size={'sm'}> - + + } size={'sm'} w="100%"> {t('options:downloadImage')} - - + + { + const handleClickResetCanvasView = useSingleAndDoubleClick( + () => handleResetCanvasView(false), + () => handleResetCanvasView(true) + ); + + const handleResetCanvasView = (shouldScaleTo1 = false) => { const canvasBaseLayer = getCanvasBaseLayer(); if (!canvasBaseLayer) return; const clientRect = canvasBaseLayer.getClientRect({ @@ -33,6 +39,7 @@ export default function UnifiedCanvasResetView() { dispatch( resetCanvasView({ contentRect: clientRect, + shouldScaleTo1, }) ); }; @@ -41,7 +48,7 @@ export default function UnifiedCanvasResetView() { aria-label={`${t('unifiedcanvas:resetView')} (R)`} tooltip={`${t('unifiedcanvas:resetView')} (R)`} icon={} - onClick={handleResetCanvasView} + onClick={handleClickResetCanvasView} /> ); }