mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Adds hotkeys and refactors sharing of konva instances
Adds hotkeys to canvas. As part of this change, the access to konva instance objects was refactored: Previously closure'd refs were used to indirectly get access to the konva instances outside of react components. Now, a getter and setter function are used to provide access directly to the konva objects.
This commit is contained in:
parent
e28599cadb
commit
aa96a457b6
@ -5,10 +5,13 @@ import { SystemState } from 'features/system/systemSlice';
|
||||
import { stringToSeedWeightsArray } from './seedWeightPairs';
|
||||
import randomInt from './randomInt';
|
||||
import { InvokeTabName } from 'features/tabs/InvokeTabs';
|
||||
import { CanvasState, isCanvasMaskLine } from 'features/canvas/store/canvasTypes';
|
||||
import {
|
||||
CanvasState,
|
||||
isCanvasMaskLine,
|
||||
} from 'features/canvas/store/canvasTypes';
|
||||
import generateMask from 'features/canvas/util/generateMask';
|
||||
import { canvasImageLayerRef } from 'features/canvas/components/IAICanvas';
|
||||
import openBase64ImageInTab from './openBase64ImageInTab';
|
||||
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
|
||||
|
||||
export type FrontendToBackendParametersConfig = {
|
||||
generationMode: InvokeTabName;
|
||||
@ -25,6 +28,8 @@ export type FrontendToBackendParametersConfig = {
|
||||
export const frontendToBackendParameters = (
|
||||
config: FrontendToBackendParametersConfig
|
||||
): { [key: string]: any } => {
|
||||
const canvasBaseLayer = getCanvasBaseLayer();
|
||||
|
||||
const {
|
||||
generationMode,
|
||||
optionsState,
|
||||
@ -106,7 +111,7 @@ export const frontendToBackendParameters = (
|
||||
}
|
||||
|
||||
// inpainting exclusive parameters
|
||||
if (generationMode === 'unifiedCanvas' && canvasImageLayerRef.current) {
|
||||
if (generationMode === 'unifiedCanvas' && canvasBaseLayer) {
|
||||
const {
|
||||
layerState: { objects },
|
||||
boundingBoxCoordinates,
|
||||
@ -143,16 +148,16 @@ export const frontendToBackendParameters = (
|
||||
|
||||
generationParameters.bounding_box = boundingBox;
|
||||
|
||||
const tempScale = canvasImageLayerRef.current.scale();
|
||||
const tempScale = canvasBaseLayer.scale();
|
||||
|
||||
canvasImageLayerRef.current.scale({
|
||||
canvasBaseLayer.scale({
|
||||
x: 1 / stageScale,
|
||||
y: 1 / stageScale,
|
||||
});
|
||||
|
||||
const absPos = canvasImageLayerRef.current.getAbsolutePosition();
|
||||
const absPos = canvasBaseLayer.getAbsolutePosition();
|
||||
|
||||
const imageDataURL = canvasImageLayerRef.current.toDataURL({
|
||||
const imageDataURL = canvasBaseLayer.toDataURL({
|
||||
x: boundingBox.x + absPos.x,
|
||||
y: boundingBox.y + absPos.y,
|
||||
width: boundingBox.width,
|
||||
@ -166,7 +171,7 @@ export const frontendToBackendParameters = (
|
||||
]);
|
||||
}
|
||||
|
||||
canvasImageLayerRef.current.scale(tempScale);
|
||||
canvasBaseLayer.scale(tempScale);
|
||||
|
||||
generationParameters.init_img = imageDataURL;
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { MutableRefObject, useRef } from 'react';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import Konva from 'konva';
|
||||
import { Layer, Stage } from 'react-konva';
|
||||
import { Stage as StageType } from 'konva/lib/Stage';
|
||||
import { useAppSelector } from 'app/store';
|
||||
import {
|
||||
canvasSelector,
|
||||
@ -28,6 +27,10 @@ import IAICanvasIntermediateImage from './IAICanvasIntermediateImage';
|
||||
import IAICanvasStatusText from './IAICanvasStatusText';
|
||||
import IAICanvasStagingArea from './IAICanvasStagingArea';
|
||||
import IAICanvasStagingAreaToolbar from './IAICanvasStagingAreaToolbar';
|
||||
import {
|
||||
setCanvasBaseLayer,
|
||||
setCanvasStage,
|
||||
} from '../util/konvaInstanceProvider';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector, isStagingSelector],
|
||||
@ -84,10 +87,6 @@ const selector = createSelector(
|
||||
}
|
||||
);
|
||||
|
||||
// Use a closure allow other components to use these things... not ideal...
|
||||
export let stageRef: MutableRefObject<StageType | null>;
|
||||
export let canvasImageLayerRef: MutableRefObject<Konva.Layer | null>;
|
||||
|
||||
const IAICanvas = () => {
|
||||
const {
|
||||
isMaskEnabled,
|
||||
@ -104,9 +103,18 @@ const IAICanvas = () => {
|
||||
} = useAppSelector(selector);
|
||||
useCanvasHotkeys();
|
||||
|
||||
// set the closure'd refs
|
||||
stageRef = useRef<StageType>(null);
|
||||
canvasImageLayerRef = useRef<Konva.Layer>(null);
|
||||
const stageRef = useRef<Konva.Stage | null>(null);
|
||||
const canvasBaseLayerRef = useRef<Konva.Layer | null>(null);
|
||||
|
||||
const canvasStageRefCallback = useCallback((el: Konva.Stage) => {
|
||||
setCanvasStage(el as Konva.Stage);
|
||||
stageRef.current = el;
|
||||
}, []);
|
||||
|
||||
const canvasBaseLayerRefCallback = useCallback((el: Konva.Layer) => {
|
||||
setCanvasBaseLayer(el as Konva.Layer);
|
||||
canvasBaseLayerRef.current = el;
|
||||
}, []);
|
||||
|
||||
const lastCursorPositionRef = useRef<Vector2d>({ x: 0, y: 0 });
|
||||
|
||||
@ -131,7 +139,7 @@ const IAICanvas = () => {
|
||||
<div className="inpainting-canvas-wrapper">
|
||||
<Stage
|
||||
tabIndex={-1}
|
||||
ref={stageRef}
|
||||
ref={canvasStageRefCallback}
|
||||
className={'inpainting-canvas-stage'}
|
||||
style={{
|
||||
...(stageCursor ? { cursor: stageCursor } : {}),
|
||||
@ -160,7 +168,7 @@ const IAICanvas = () => {
|
||||
|
||||
<Layer
|
||||
id={'base'}
|
||||
ref={canvasImageLayerRef}
|
||||
ref={canvasBaseLayerRefCallback}
|
||||
listening={false}
|
||||
imageSmoothingEnabled={false}
|
||||
>
|
||||
|
@ -1,5 +1,9 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { setBrushColor, setBrushSize, setTool } from 'features/canvas/store/canvasSlice';
|
||||
import {
|
||||
setBrushColor,
|
||||
setBrushSize,
|
||||
setTool,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import _ from 'lodash';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
@ -8,7 +12,11 @@ import IAIPopover from 'common/components/IAIPopover';
|
||||
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { canvasSelector, isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import {
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
} from 'features/canvas/store/canvasSelectors';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
export const selector = createSelector(
|
||||
[canvasSelector, isStagingSelector],
|
||||
@ -33,6 +41,44 @@ const IAICanvasBrushButtonPopover = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { tool, brushColor, brushSize, isStaging } = useAppSelector(selector);
|
||||
|
||||
useHotkeys(
|
||||
['b'],
|
||||
() => {
|
||||
handleSelectBrushTool();
|
||||
},
|
||||
{
|
||||
enabled: () => true,
|
||||
preventDefault: true,
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['['],
|
||||
() => {
|
||||
dispatch(setBrushSize(Math.max(brushSize - 5, 5)));
|
||||
},
|
||||
{
|
||||
enabled: () => true,
|
||||
preventDefault: true,
|
||||
},
|
||||
[brushSize]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
[']'],
|
||||
() => {
|
||||
dispatch(setBrushSize(Math.min(brushSize + 5, 500)));
|
||||
},
|
||||
{
|
||||
enabled: () => true,
|
||||
preventDefault: true,
|
||||
},
|
||||
[brushSize]
|
||||
);
|
||||
|
||||
const handleSelectBrushTool = () => dispatch(setTool('brush'));
|
||||
|
||||
return (
|
||||
<IAIPopover
|
||||
trigger="hover"
|
||||
@ -42,7 +88,7 @@ const IAICanvasBrushButtonPopover = () => {
|
||||
tooltip="Brush (B)"
|
||||
icon={<FaPaintBrush />}
|
||||
data-selected={tool === 'brush' && !isStaging}
|
||||
onClick={() => dispatch(setTool('brush'))}
|
||||
onClick={handleSelectBrushTool}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import {
|
||||
setEraserSize,
|
||||
setTool,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { setEraserSize, setTool } from 'features/canvas/store/canvasSlice';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import _ from 'lodash';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
@ -11,7 +8,10 @@ import IAIPopover from 'common/components/IAIPopover';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { canvasSelector, isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import {
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
} from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
export const selector = createSelector(
|
||||
[canvasSelector, isStagingSelector],
|
||||
@ -37,17 +37,41 @@ const IAICanvasEraserButtonPopover = () => {
|
||||
const handleSelectEraserTool = () => dispatch(setTool('eraser'));
|
||||
|
||||
useHotkeys(
|
||||
'e',
|
||||
(e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
['e'],
|
||||
() => {
|
||||
handleSelectEraserTool();
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
enabled: () => true,
|
||||
preventDefault: true,
|
||||
},
|
||||
[tool]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['['],
|
||||
() => {
|
||||
dispatch(setEraserSize(Math.max(eraserSize - 5, 5)));
|
||||
},
|
||||
{
|
||||
enabled: () => true,
|
||||
preventDefault: true,
|
||||
},
|
||||
[eraserSize]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
[']'],
|
||||
() => {
|
||||
dispatch(setEraserSize(Math.min(eraserSize + 5, 500)));
|
||||
},
|
||||
{
|
||||
enabled: () => true,
|
||||
preventDefault: true,
|
||||
},
|
||||
[eraserSize]
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIPopover
|
||||
trigger="hover"
|
||||
|
@ -16,6 +16,7 @@ import IAICheckbox from 'common/components/IAICheckbox';
|
||||
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
export const selector = createSelector(
|
||||
[canvasSelector],
|
||||
@ -41,6 +42,22 @@ const IAICanvasMaskButtonPopover = () => {
|
||||
const { layer, maskColor, isMaskEnabled, shouldPreserveMaskedArea } =
|
||||
useAppSelector(selector);
|
||||
|
||||
useHotkeys(
|
||||
['q'],
|
||||
() => {
|
||||
handleToggleMaskLayer();
|
||||
},
|
||||
{
|
||||
enabled: () => true,
|
||||
preventDefault: true,
|
||||
},
|
||||
[layer]
|
||||
);
|
||||
|
||||
const handleToggleMaskLayer = () => {
|
||||
dispatch(setLayer(layer === 'mask' ? 'base' : 'mask'));
|
||||
};
|
||||
|
||||
return (
|
||||
<IAIPopover
|
||||
trigger="hover"
|
||||
@ -49,7 +66,7 @@ const IAICanvasMaskButtonPopover = () => {
|
||||
aria-label="Select Mask Layer"
|
||||
tooltip="Select Mask Layer"
|
||||
data-alert={layer === 'mask'}
|
||||
onClick={() => dispatch(setLayer(layer === 'mask' ? 'base' : 'mask'))}
|
||||
onClick={handleToggleMaskLayer}
|
||||
icon={<FaMask />}
|
||||
/>
|
||||
}
|
||||
|
@ -1,15 +1,12 @@
|
||||
import { ButtonGroup } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import {
|
||||
resizeAndScaleCanvas,
|
||||
resetCanvas,
|
||||
resetCanvasView,
|
||||
setTool,
|
||||
fitBoundingBoxToStage,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import _ from 'lodash';
|
||||
import { canvasImageLayerRef, stageRef } from '../IAICanvas';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import {
|
||||
FaArrowsAlt,
|
||||
@ -28,12 +25,15 @@ import IAICanvasEraserButtonPopover from './IAICanvasEraserButtonPopover';
|
||||
import IAICanvasBrushButtonPopover from './IAICanvasBrushButtonPopover';
|
||||
import IAICanvasMaskButtonPopover from './IAICanvasMaskButtonPopover';
|
||||
import { mergeAndUploadCanvas } from 'features/canvas/util/mergeAndUploadCanvas';
|
||||
import IAICheckbox from 'common/components/IAICheckbox';
|
||||
import { ChangeEvent } from 'react';
|
||||
import {
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
} from 'features/canvas/store/canvasSelectors';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import {
|
||||
getCanvasBaseLayer,
|
||||
getCanvasStage,
|
||||
} from 'features/canvas/util/konvaInstanceProvider';
|
||||
|
||||
export const selector = createSelector(
|
||||
[canvasSelector, isStagingSelector],
|
||||
@ -54,6 +54,138 @@ export const selector = createSelector(
|
||||
const IAICanvasOutpaintingControls = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { tool, isStaging } = useAppSelector(selector);
|
||||
const canvasBaseLayer = getCanvasBaseLayer();
|
||||
|
||||
useHotkeys(
|
||||
['m'],
|
||||
() => {
|
||||
handleSelectMoveTool();
|
||||
},
|
||||
{
|
||||
enabled: () => true,
|
||||
preventDefault: true,
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['shift+r'],
|
||||
() => {
|
||||
handleResetCanvasView();
|
||||
},
|
||||
{
|
||||
enabled: () => true,
|
||||
preventDefault: true,
|
||||
},
|
||||
[canvasBaseLayer]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['shift+c'],
|
||||
() => {
|
||||
handleResetCanvas();
|
||||
},
|
||||
{
|
||||
enabled: () => true,
|
||||
preventDefault: true,
|
||||
},
|
||||
[canvasBaseLayer]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['shift+m'],
|
||||
() => {
|
||||
handleMergeVisible();
|
||||
},
|
||||
{
|
||||
enabled: () => true,
|
||||
preventDefault: true,
|
||||
},
|
||||
[canvasBaseLayer]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['shift+s'],
|
||||
() => {
|
||||
handleSaveToGallery();
|
||||
},
|
||||
{
|
||||
enabled: () => true,
|
||||
preventDefault: true,
|
||||
},
|
||||
[canvasBaseLayer]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['meta+c', 'ctrl+c'],
|
||||
() => {
|
||||
handleCopyImageToClipboard();
|
||||
},
|
||||
{
|
||||
enabled: () => true,
|
||||
preventDefault: true,
|
||||
},
|
||||
[canvasBaseLayer]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
['shift+d'],
|
||||
() => {
|
||||
handleDownloadAsImage();
|
||||
},
|
||||
{
|
||||
enabled: () => true,
|
||||
preventDefault: true,
|
||||
},
|
||||
[canvasBaseLayer]
|
||||
);
|
||||
|
||||
const handleSelectMoveTool = () => dispatch(setTool('move'));
|
||||
|
||||
const handleResetCanvasView = () => {
|
||||
if (!canvasBaseLayer) return;
|
||||
const clientRect = canvasBaseLayer.getClientRect({
|
||||
skipTransform: true,
|
||||
});
|
||||
dispatch(
|
||||
resetCanvasView({
|
||||
contentRect: clientRect,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleResetCanvas = () => dispatch(resetCanvas());
|
||||
|
||||
const handleMergeVisible = () => {
|
||||
dispatch(mergeAndUploadCanvas({}));
|
||||
};
|
||||
|
||||
const handleSaveToGallery = () => {
|
||||
dispatch(
|
||||
mergeAndUploadCanvas({
|
||||
cropVisible: true,
|
||||
saveToGallery: true,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleCopyImageToClipboard = () => {
|
||||
dispatch(
|
||||
mergeAndUploadCanvas({
|
||||
cropVisible: true,
|
||||
copyAfterSaving: true,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleDownloadAsImage = () => {
|
||||
dispatch(
|
||||
mergeAndUploadCanvas({
|
||||
cropVisible: true,
|
||||
downloadAfterSaving: true,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="inpainting-settings">
|
||||
@ -66,63 +198,33 @@ const IAICanvasOutpaintingControls = () => {
|
||||
tooltip="Move (M)"
|
||||
icon={<FaArrowsAlt />}
|
||||
data-selected={tool === 'move' || isStaging}
|
||||
onClick={() => dispatch(setTool('move'))}
|
||||
onClick={handleSelectMoveTool}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup isAttached>
|
||||
<IAIIconButton
|
||||
aria-label="Merge Visible"
|
||||
tooltip="Merge Visible"
|
||||
aria-label="Merge Visible (Shift + M)"
|
||||
tooltip="Merge Visible (Shift + M)"
|
||||
icon={<FaLayerGroup />}
|
||||
onClick={() => {
|
||||
dispatch(
|
||||
mergeAndUploadCanvas({
|
||||
canvasImageLayerRef,
|
||||
})
|
||||
);
|
||||
}}
|
||||
onClick={handleMergeVisible}
|
||||
/>
|
||||
<IAIIconButton
|
||||
aria-label="Save to Gallery"
|
||||
tooltip="Save to Gallery"
|
||||
aria-label="Save to Gallery (Shift + S)"
|
||||
tooltip="Save to Gallery (Shift + S)"
|
||||
icon={<FaSave />}
|
||||
onClick={() => {
|
||||
dispatch(
|
||||
mergeAndUploadCanvas({
|
||||
canvasImageLayerRef,
|
||||
cropVisible: true,
|
||||
saveToGallery: true,
|
||||
})
|
||||
);
|
||||
}}
|
||||
onClick={handleSaveToGallery}
|
||||
/>
|
||||
<IAIIconButton
|
||||
aria-label="Copy Selection"
|
||||
tooltip="Copy Selection"
|
||||
aria-label="Copy to Clipboard (Cmd/Ctrl + C)"
|
||||
tooltip="Copy to Clipboard (Cmd/Ctrl + C)"
|
||||
icon={<FaCopy />}
|
||||
onClick={() => {
|
||||
dispatch(
|
||||
mergeAndUploadCanvas({
|
||||
canvasImageLayerRef,
|
||||
cropVisible: true,
|
||||
copyAfterSaving: true,
|
||||
})
|
||||
);
|
||||
}}
|
||||
onClick={handleCopyImageToClipboard}
|
||||
/>
|
||||
<IAIIconButton
|
||||
aria-label="Download Selection"
|
||||
tooltip="Download Selection"
|
||||
aria-label="Download as Image (Shift + D)"
|
||||
tooltip="Download as Image (Shift + D)"
|
||||
icon={<FaDownload />}
|
||||
onClick={() => {
|
||||
dispatch(
|
||||
mergeAndUploadCanvas({
|
||||
canvasImageLayerRef,
|
||||
cropVisible: true,
|
||||
downloadAfterSaving: true,
|
||||
})
|
||||
);
|
||||
}}
|
||||
onClick={handleDownloadAsImage}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup isAttached>
|
||||
@ -142,23 +244,13 @@ const IAICanvasOutpaintingControls = () => {
|
||||
aria-label="Reset Canvas View"
|
||||
tooltip="Reset Canvas View"
|
||||
icon={<FaCrosshairs />}
|
||||
onClick={() => {
|
||||
if (!stageRef.current || !canvasImageLayerRef.current) return;
|
||||
const clientRect = canvasImageLayerRef.current.getClientRect({
|
||||
skipTransform: true,
|
||||
});
|
||||
dispatch(
|
||||
resetCanvasView({
|
||||
contentRect: clientRect,
|
||||
})
|
||||
);
|
||||
}}
|
||||
onClick={handleResetCanvasView}
|
||||
/>
|
||||
<IAIIconButton
|
||||
aria-label="Reset Canvas"
|
||||
tooltip="Reset Canvas"
|
||||
icon={<FaTrash />}
|
||||
onClick={() => dispatch(resetCanvas())}
|
||||
onClick={handleResetCanvas}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
|
@ -9,9 +9,9 @@ import {
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import { useRef } from 'react';
|
||||
import { stageRef } from '../components/IAICanvas';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { CanvasTool } from '../store/canvasTypes';
|
||||
import { getCanvasStage } from '../util/konvaInstanceProvider';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector, activeTabNameSelector],
|
||||
@ -44,6 +44,9 @@ const useInpaintingCanvasHotkeys = () => {
|
||||
useAppSelector(selector);
|
||||
|
||||
const previousToolRef = useRef<CanvasTool | null>(null);
|
||||
|
||||
const canvasStage = getCanvasStage();
|
||||
|
||||
// Toggle lock bounding box
|
||||
useHotkeys(
|
||||
'shift+w',
|
||||
@ -72,7 +75,7 @@ const useInpaintingCanvasHotkeys = () => {
|
||||
(e: KeyboardEvent) => {
|
||||
if (e.repeat) return;
|
||||
|
||||
stageRef.current?.container().focus();
|
||||
canvasStage?.container().focus();
|
||||
|
||||
if (tool !== 'move') {
|
||||
previousToolRef.current = tool;
|
||||
|
16
frontend/src/features/canvas/util/konvaInstanceProvider.ts
Normal file
16
frontend/src/features/canvas/util/konvaInstanceProvider.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import Konva from 'konva';
|
||||
|
||||
let canvasBaseLayer: Konva.Layer | null = null;
|
||||
let canvasStage: Konva.Stage | null = null;
|
||||
|
||||
export const setCanvasBaseLayer = (layer: Konva.Layer) => {
|
||||
canvasBaseLayer = layer;
|
||||
};
|
||||
|
||||
export const getCanvasBaseLayer = () => canvasBaseLayer;
|
||||
|
||||
export const setCanvasStage = (stage: Konva.Stage) => {
|
||||
canvasStage = stage;
|
||||
};
|
||||
|
||||
export const getCanvasStage = () => canvasStage;
|
@ -1,18 +1,16 @@
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import { RootState } from 'app/store';
|
||||
import Konva from 'konva';
|
||||
import { MutableRefObject } from 'react';
|
||||
import * as InvokeAI from 'app/invokeai';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import layerToDataURL from './layerToDataURL';
|
||||
import downloadFile from './downloadFile';
|
||||
import copyImage from './copyImage';
|
||||
import { getCanvasBaseLayer } from './konvaInstanceProvider';
|
||||
|
||||
export const mergeAndUploadCanvas = createAsyncThunk(
|
||||
'canvas/mergeAndUploadCanvas',
|
||||
async (
|
||||
args: {
|
||||
canvasImageLayerRef: MutableRefObject<Konva.Layer | null>;
|
||||
cropVisible?: boolean;
|
||||
saveToGallery?: boolean;
|
||||
downloadAfterSaving?: boolean;
|
||||
@ -20,13 +18,8 @@ export const mergeAndUploadCanvas = createAsyncThunk(
|
||||
},
|
||||
thunkAPI
|
||||
) => {
|
||||
const {
|
||||
canvasImageLayerRef,
|
||||
saveToGallery,
|
||||
downloadAfterSaving,
|
||||
cropVisible,
|
||||
copyAfterSaving,
|
||||
} = args;
|
||||
const { saveToGallery, downloadAfterSaving, cropVisible, copyAfterSaving } =
|
||||
args;
|
||||
|
||||
const { getState } = thunkAPI;
|
||||
|
||||
@ -34,10 +27,12 @@ export const mergeAndUploadCanvas = createAsyncThunk(
|
||||
|
||||
const stageScale = state.canvas.stageScale;
|
||||
|
||||
if (!canvasImageLayerRef.current) return;
|
||||
const canvasBaseLayer = getCanvasBaseLayer();
|
||||
|
||||
if (!canvasBaseLayer) return;
|
||||
|
||||
const { dataURL, boundingBox: originalBoundingBox } = layerToDataURL(
|
||||
canvasImageLayerRef.current,
|
||||
canvasBaseLayer,
|
||||
stageScale
|
||||
);
|
||||
|
||||
|
@ -246,20 +246,6 @@ export default function ImageGallery() {
|
||||
[galleryImageMinimumWidth]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
'shift+r',
|
||||
() => {
|
||||
dispatch(setGalleryImageMinimumWidth(64));
|
||||
toast({
|
||||
title: `Reset Gallery Image Size`,
|
||||
status: 'success',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
});
|
||||
},
|
||||
[galleryImageMinimumWidth]
|
||||
);
|
||||
|
||||
// set gallery scroll position
|
||||
useEffect(() => {
|
||||
if (!galleryContainerRef.current) return;
|
||||
|
@ -135,11 +135,6 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
|
||||
desc: 'Decreases gallery thumbnails size',
|
||||
hotkey: 'Shift+Down',
|
||||
},
|
||||
{
|
||||
title: 'Reset Gallery Image Size',
|
||||
desc: 'Resets image gallery size',
|
||||
hotkey: 'Shift+R',
|
||||
},
|
||||
];
|
||||
|
||||
const unifiedCanvasHotkeys = [
|
||||
|
Loading…
Reference in New Issue
Block a user