mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
adds double-click to reset view to 100% (#2436)
Adds double-click to reset canvas view to 100%. - Adds hook to manage single and double clicks - Single Click `Reset Canvas View` --> scale to fit, no change to current behaviour - Double Click `Reset Canvas View` --> set scale to 1
This commit is contained in:
commit
1ceb7a60db
28
frontend/src/common/hooks/useSingleAndDoubleClick.ts
Normal file
28
frontend/src/common/hooks/useSingleAndDoubleClick.ts
Normal file
@ -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);
|
||||||
|
}
|
@ -42,6 +42,7 @@ import {
|
|||||||
} from 'features/canvas/store/canvasTypes';
|
} from 'features/canvas/store/canvasTypes';
|
||||||
import { ChangeEvent } from 'react';
|
import { ChangeEvent } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useSingleAndDoubleClick } from 'common/hooks/useSingleAndDoubleClick';
|
||||||
|
|
||||||
export const selector = createSelector(
|
export const selector = createSelector(
|
||||||
[systemSelector, canvasSelector, isStagingSelector],
|
[systemSelector, canvasSelector, isStagingSelector],
|
||||||
@ -156,7 +157,12 @@ const IAICanvasOutpaintingControls = () => {
|
|||||||
|
|
||||||
const handleSelectMoveTool = () => dispatch(setTool('move'));
|
const handleSelectMoveTool = () => dispatch(setTool('move'));
|
||||||
|
|
||||||
const handleResetCanvasView = () => {
|
const handleClickResetCanvasView = useSingleAndDoubleClick(
|
||||||
|
() => handleResetCanvasView(false),
|
||||||
|
() => handleResetCanvasView(true)
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleResetCanvasView = (shouldScaleTo1 = false) => {
|
||||||
const canvasBaseLayer = getCanvasBaseLayer();
|
const canvasBaseLayer = getCanvasBaseLayer();
|
||||||
if (!canvasBaseLayer) return;
|
if (!canvasBaseLayer) return;
|
||||||
const clientRect = canvasBaseLayer.getClientRect({
|
const clientRect = canvasBaseLayer.getClientRect({
|
||||||
@ -165,6 +171,7 @@ const IAICanvasOutpaintingControls = () => {
|
|||||||
dispatch(
|
dispatch(
|
||||||
resetCanvasView({
|
resetCanvasView({
|
||||||
contentRect: clientRect,
|
contentRect: clientRect,
|
||||||
|
shouldScaleTo1,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -247,7 +254,7 @@ const IAICanvasOutpaintingControls = () => {
|
|||||||
aria-label={`${t('unifiedcanvas:resetView')} (R)`}
|
aria-label={`${t('unifiedcanvas:resetView')} (R)`}
|
||||||
tooltip={`${t('unifiedcanvas:resetView')} (R)`}
|
tooltip={`${t('unifiedcanvas:resetView')} (R)`}
|
||||||
icon={<FaCrosshairs />}
|
icon={<FaCrosshairs />}
|
||||||
onClick={handleResetCanvasView}
|
onClick={handleClickResetCanvasView}
|
||||||
/>
|
/>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
|
||||||
|
@ -602,9 +602,10 @@ export const canvasSlice = createSlice({
|
|||||||
state,
|
state,
|
||||||
action: PayloadAction<{
|
action: PayloadAction<{
|
||||||
contentRect: IRect;
|
contentRect: IRect;
|
||||||
|
shouldScaleTo1?: boolean;
|
||||||
}>
|
}>
|
||||||
) => {
|
) => {
|
||||||
const { contentRect } = action.payload;
|
const { contentRect, shouldScaleTo1 } = action.payload;
|
||||||
const {
|
const {
|
||||||
stageDimensions: { width: stageWidth, height: stageHeight },
|
stageDimensions: { width: stageWidth, height: stageHeight },
|
||||||
} = state;
|
} = state;
|
||||||
@ -612,7 +613,9 @@ export const canvasSlice = createSlice({
|
|||||||
const { x, y, width, height } = contentRect;
|
const { x, y, width, height } = contentRect;
|
||||||
|
|
||||||
if (width !== 0 && height !== 0) {
|
if (width !== 0 && height !== 0) {
|
||||||
const newScale = calculateScale(
|
const newScale = shouldScaleTo1
|
||||||
|
? 1
|
||||||
|
: calculateScale(
|
||||||
stageWidth,
|
stageWidth,
|
||||||
stageHeight,
|
stageHeight,
|
||||||
width,
|
width,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { useAppDispatch } from 'app/storeHooks';
|
import { useAppDispatch } from 'app/storeHooks';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
|
import { useSingleAndDoubleClick } from 'common/hooks/useSingleAndDoubleClick';
|
||||||
import { resetCanvasView } from 'features/canvas/store/canvasSlice';
|
import { resetCanvasView } from 'features/canvas/store/canvasSlice';
|
||||||
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
|
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@ -24,7 +25,12 @@ export default function UnifiedCanvasResetView() {
|
|||||||
[canvasBaseLayer]
|
[canvasBaseLayer]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleResetCanvasView = () => {
|
const handleClickResetCanvasView = useSingleAndDoubleClick(
|
||||||
|
() => handleResetCanvasView(false),
|
||||||
|
() => handleResetCanvasView(true)
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleResetCanvasView = (shouldScaleTo1 = false) => {
|
||||||
const canvasBaseLayer = getCanvasBaseLayer();
|
const canvasBaseLayer = getCanvasBaseLayer();
|
||||||
if (!canvasBaseLayer) return;
|
if (!canvasBaseLayer) return;
|
||||||
const clientRect = canvasBaseLayer.getClientRect({
|
const clientRect = canvasBaseLayer.getClientRect({
|
||||||
@ -33,6 +39,7 @@ export default function UnifiedCanvasResetView() {
|
|||||||
dispatch(
|
dispatch(
|
||||||
resetCanvasView({
|
resetCanvasView({
|
||||||
contentRect: clientRect,
|
contentRect: clientRect,
|
||||||
|
shouldScaleTo1,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -41,7 +48,7 @@ export default function UnifiedCanvasResetView() {
|
|||||||
aria-label={`${t('unifiedcanvas:resetView')} (R)`}
|
aria-label={`${t('unifiedcanvas:resetView')} (R)`}
|
||||||
tooltip={`${t('unifiedcanvas:resetView')} (R)`}
|
tooltip={`${t('unifiedcanvas:resetView')} (R)`}
|
||||||
icon={<FaCrosshairs />}
|
icon={<FaCrosshairs />}
|
||||||
onClick={handleResetCanvasView}
|
onClick={handleClickResetCanvasView}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user