mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Improves bounding box hotkeys/UX
This commit is contained in:
parent
dd71066391
commit
ed45dca7c1
@ -1,6 +1,8 @@
|
|||||||
@use '../../../styles/Mixins/' as *;
|
@use '../../../styles/Mixins/' as *;
|
||||||
|
|
||||||
.hotkeys-modal {
|
.hotkeys-modal {
|
||||||
|
width: 36rem !important;
|
||||||
|
max-width: 36rem !important;
|
||||||
display: grid;
|
display: grid;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
background-color: var(--settings-modal-bg) !important;
|
background-color: var(--settings-modal-bg) !important;
|
||||||
@ -17,8 +19,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hotkeys-modal-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
.hotkeys-modal-items {
|
.hotkeys-modal-items {
|
||||||
max-height: 32rem;
|
max-height: 36rem;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
@include HideScrollbar;
|
@include HideScrollbar;
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
Accordion,
|
Accordion,
|
||||||
AccordionButton,
|
AccordionButton,
|
||||||
|
AccordionIcon,
|
||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionPanel,
|
AccordionPanel,
|
||||||
Modal,
|
Modal,
|
||||||
@ -181,8 +182,13 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
|
|||||||
hotkey: 'Ctrl+Shift+Z, Ctrl+Y',
|
hotkey: 'Ctrl+Shift+Z, Ctrl+Y',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Move Bounding Box',
|
title: 'Lock Bounding Box',
|
||||||
desc: 'Hold to move bounding box',
|
desc: 'Locks the bounding box',
|
||||||
|
hotkey: 'M',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Quick Toggle Lock Bounding Box',
|
||||||
|
desc: 'Hold to toggle locking the bounding box',
|
||||||
hotkey: 'Space',
|
hotkey: 'Space',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -225,8 +231,9 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
|
|||||||
<div className="hotkeys-modal-items">
|
<div className="hotkeys-modal-items">
|
||||||
<Accordion allowToggle allowMultiple>
|
<Accordion allowToggle allowMultiple>
|
||||||
<AccordionItem>
|
<AccordionItem>
|
||||||
<AccordionButton>
|
<AccordionButton className="hotkeys-modal-button">
|
||||||
<h2>App Hotkeys</h2>
|
<h2>App Hotkeys</h2>
|
||||||
|
<AccordionIcon />
|
||||||
</AccordionButton>
|
</AccordionButton>
|
||||||
<AccordionPanel>
|
<AccordionPanel>
|
||||||
{renderHotkeyModalItems(appHotkeys)}
|
{renderHotkeyModalItems(appHotkeys)}
|
||||||
@ -234,8 +241,9 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
|
|||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
|
|
||||||
<AccordionItem>
|
<AccordionItem>
|
||||||
<AccordionButton>
|
<AccordionButton className="hotkeys-modal-button">
|
||||||
<h2>General Hotkeys</h2>
|
<h2>General Hotkeys</h2>
|
||||||
|
<AccordionIcon />
|
||||||
</AccordionButton>
|
</AccordionButton>
|
||||||
<AccordionPanel>
|
<AccordionPanel>
|
||||||
{renderHotkeyModalItems(generalHotkeys)}
|
{renderHotkeyModalItems(generalHotkeys)}
|
||||||
@ -243,8 +251,9 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
|
|||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
|
|
||||||
<AccordionItem>
|
<AccordionItem>
|
||||||
<AccordionButton>
|
<AccordionButton className="hotkeys-modal-button">
|
||||||
<h2>Gallery Hotkeys</h2>
|
<h2>Gallery Hotkeys</h2>
|
||||||
|
<AccordionIcon />
|
||||||
</AccordionButton>
|
</AccordionButton>
|
||||||
<AccordionPanel>
|
<AccordionPanel>
|
||||||
{renderHotkeyModalItems(galleryHotkeys)}
|
{renderHotkeyModalItems(galleryHotkeys)}
|
||||||
@ -252,8 +261,9 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
|
|||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
|
|
||||||
<AccordionItem>
|
<AccordionItem>
|
||||||
<AccordionButton>
|
<AccordionButton className="hotkeys-modal-button">
|
||||||
<h2>Inpainting Hotkeys</h2>
|
<h2>Inpainting Hotkeys</h2>
|
||||||
|
<AccordionIcon />
|
||||||
</AccordionButton>
|
</AccordionButton>
|
||||||
<AccordionPanel>
|
<AccordionPanel>
|
||||||
{renderHotkeyModalItems(inpaintingHotkeys)}
|
{renderHotkeyModalItems(inpaintingHotkeys)}
|
||||||
|
@ -220,8 +220,17 @@ const InpaintingCanvas = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="inpainting-canvas-wrapper checkerboard" tabIndex={1}>
|
<div className="inpainting-canvas-wrapper checkerboard" tabIndex={1}>
|
||||||
<div className="inpainting-alerts">
|
<div className="inpainting-alerts">
|
||||||
{!shouldShowMask && <div>Mask Hidden (H)</div>}
|
{!shouldShowMask && (
|
||||||
{shouldInvertMask && <div>Mask Inverted (Shift+M)</div>}
|
<div style={{ pointerEvents: 'none' }}>Mask Hidden (H)</div>
|
||||||
|
)}
|
||||||
|
{shouldInvertMask && (
|
||||||
|
<div style={{ pointerEvents: 'none' }}>Mask Inverted (Shift+M)</div>
|
||||||
|
)}
|
||||||
|
{!shouldLockBoundingBox && (
|
||||||
|
<div style={{ pointerEvents: 'none' }}>
|
||||||
|
Transforming Bounding Box (M)
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{canvasBgImage && (
|
{canvasBgImage && (
|
||||||
|
@ -23,6 +23,7 @@ import {
|
|||||||
setShouldShowMask,
|
setShouldShowMask,
|
||||||
setShouldInvertMask,
|
setShouldInvertMask,
|
||||||
setNeedsRepaint,
|
setNeedsRepaint,
|
||||||
|
toggleShouldLockBoundingBox,
|
||||||
} from './inpaintingSlice';
|
} from './inpaintingSlice';
|
||||||
|
|
||||||
import { MdInvertColors, MdInvertColorsOff } from 'react-icons/md';
|
import { MdInvertColors, MdInvertColorsOff } from 'react-icons/md';
|
||||||
@ -144,6 +145,19 @@ const InpaintingControls = () => {
|
|||||||
[activeTabName, shouldShowMask]
|
[activeTabName, shouldShowMask]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Toggle lock bounding box
|
||||||
|
useHotkeys(
|
||||||
|
'm',
|
||||||
|
(e: KeyboardEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
dispatch(toggleShouldLockBoundingBox());
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: activeTabName === 'inpainting' && shouldShowMask,
|
||||||
|
},
|
||||||
|
[activeTabName, shouldShowMask]
|
||||||
|
);
|
||||||
|
|
||||||
// Undo
|
// Undo
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
'cmd+z, control+z',
|
'cmd+z, control+z',
|
||||||
|
@ -87,66 +87,6 @@ export const InpaintingBoundingBoxPreviewOverlay = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// /**
|
|
||||||
// * Draws marching ants around the mask. Unused.
|
|
||||||
// */
|
|
||||||
// const _InpaintingBoundingBoxPreviewMarchingAnts = () => {
|
|
||||||
// const { boundingBoxCoordinate, boundingBoxDimensions } = useAppSelector(
|
|
||||||
// boundingBoxPreviewSelector
|
|
||||||
// );
|
|
||||||
|
|
||||||
// const blackStrokeRectRef = useRef<Konva.Rect>(null);
|
|
||||||
// const whiteStrokeRectRef = useRef<Konva.Rect>(null);
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// const blackStrokeRect = blackStrokeRectRef.current;
|
|
||||||
// const whiteStrokeRect = whiteStrokeRectRef.current;
|
|
||||||
|
|
||||||
// const anim = new Konva.Animation((frame) => {
|
|
||||||
// if (!frame) return;
|
|
||||||
// blackStrokeRect?.dashOffset(
|
|
||||||
// -1 * (Math.floor(frame.time / MARCHING_ANTS_SPEED) % 16)
|
|
||||||
// );
|
|
||||||
// whiteStrokeRect?.dashOffset(
|
|
||||||
// -1 * ((Math.floor(frame.time / MARCHING_ANTS_SPEED) % 16) + 4)
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// anim.start();
|
|
||||||
|
|
||||||
// return () => {
|
|
||||||
// anim.stop();
|
|
||||||
// };
|
|
||||||
// }, []);
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <Group>
|
|
||||||
// <Rect
|
|
||||||
// x={boundingBoxCoordinate.x}
|
|
||||||
// y={boundingBoxCoordinate.y}
|
|
||||||
// width={boundingBoxDimensions.width}
|
|
||||||
// height={boundingBoxDimensions.height}
|
|
||||||
// stroke={'black'}
|
|
||||||
// strokeWidth={1}
|
|
||||||
// dash={[4, 4]}
|
|
||||||
// ref={blackStrokeRectRef}
|
|
||||||
// listening={false}
|
|
||||||
// />
|
|
||||||
// <Rect
|
|
||||||
// x={boundingBoxCoordinate.x}
|
|
||||||
// y={boundingBoxCoordinate.y}
|
|
||||||
// width={boundingBoxDimensions.width}
|
|
||||||
// height={boundingBoxDimensions.height}
|
|
||||||
// stroke={'white'}
|
|
||||||
// dash={[4, 4]}
|
|
||||||
// strokeWidth={1}
|
|
||||||
// ref={whiteStrokeRectRef}
|
|
||||||
// listening={false}
|
|
||||||
// />
|
|
||||||
// </Group>
|
|
||||||
// );
|
|
||||||
// };
|
|
||||||
|
|
||||||
const InpaintingBoundingBoxPreview = () => {
|
const InpaintingBoundingBoxPreview = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const {
|
const {
|
||||||
@ -326,15 +266,14 @@ const InpaintingBoundingBoxPreview = () => {
|
|||||||
strokeWidth={strokeWidth}
|
strokeWidth={strokeWidth}
|
||||||
listening={!shouldLockBoundingBox}
|
listening={!shouldLockBoundingBox}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e) => {
|
||||||
// style stage container:
|
|
||||||
const container = e?.target?.getStage()?.container();
|
const container = e?.target?.getStage()?.container();
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
container.style.cursor = 'move';
|
container.style.cursor = shouldLockBoundingBox ? 'none' : 'move';
|
||||||
}}
|
}}
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
const container = e?.target?.getStage()?.container();
|
const container = e?.target?.getStage()?.container();
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
container.style.cursor = 'default';
|
container.style.cursor = shouldLockBoundingBox ? 'none' : 'default';
|
||||||
}}
|
}}
|
||||||
draggable={!shouldLockBoundingBox}
|
draggable={!shouldLockBoundingBox}
|
||||||
onDragMove={handleOnDragMove}
|
onDragMove={handleOnDragMove}
|
||||||
|
@ -12,7 +12,6 @@ import {
|
|||||||
InpaintingState,
|
InpaintingState,
|
||||||
setIsDrawing,
|
setIsDrawing,
|
||||||
setShouldLockBoundingBox,
|
setShouldLockBoundingBox,
|
||||||
setShouldShowBrush,
|
|
||||||
toggleTool,
|
toggleTool,
|
||||||
} from '../inpaintingSlice';
|
} from '../inpaintingSlice';
|
||||||
|
|
||||||
@ -91,10 +90,8 @@ const KeyboardEventManager = () => {
|
|||||||
case ' ': {
|
case ' ': {
|
||||||
if (e.type === 'keydown') {
|
if (e.type === 'keydown') {
|
||||||
dispatch(setIsDrawing(false));
|
dispatch(setIsDrawing(false));
|
||||||
dispatch(setShouldLockBoundingBox(false));
|
|
||||||
} else {
|
|
||||||
dispatch(setShouldLockBoundingBox(true));
|
|
||||||
}
|
}
|
||||||
|
dispatch(setShouldLockBoundingBox(!shouldLockBoundingBox));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,11 +36,9 @@ export interface InpaintingState {
|
|||||||
canvasDimensions: Dimensions;
|
canvasDimensions: Dimensions;
|
||||||
boundingBoxDimensions: Dimensions;
|
boundingBoxDimensions: Dimensions;
|
||||||
boundingBoxCoordinate: Vector2d;
|
boundingBoxCoordinate: Vector2d;
|
||||||
isMovingBoundingBox: boolean;
|
|
||||||
boundingBoxPreviewFill: RgbaColor;
|
boundingBoxPreviewFill: RgbaColor;
|
||||||
shouldShowBoundingBox: boolean;
|
shouldShowBoundingBox: boolean;
|
||||||
shouldShowBoundingBoxFill: boolean;
|
shouldShowBoundingBoxFill: boolean;
|
||||||
isTransformingBoundingBox: boolean;
|
|
||||||
lines: MaskLine[];
|
lines: MaskLine[];
|
||||||
pastLines: MaskLine[][];
|
pastLines: MaskLine[][];
|
||||||
futureLines: MaskLine[][];
|
futureLines: MaskLine[][];
|
||||||
@ -68,7 +66,6 @@ const initialInpaintingState: InpaintingState = {
|
|||||||
boundingBoxPreviewFill: { r: 0, g: 0, b: 0, a: 0.7 },
|
boundingBoxPreviewFill: { r: 0, g: 0, b: 0, a: 0.7 },
|
||||||
shouldShowBoundingBox: false,
|
shouldShowBoundingBox: false,
|
||||||
shouldShowBoundingBoxFill: false,
|
shouldShowBoundingBoxFill: false,
|
||||||
isTransformingBoundingBox: false,
|
|
||||||
cursorPosition: null,
|
cursorPosition: null,
|
||||||
lines: [],
|
lines: [],
|
||||||
pastLines: [],
|
pastLines: [],
|
||||||
@ -78,7 +75,6 @@ const initialInpaintingState: InpaintingState = {
|
|||||||
shouldShowCheckboardTransparency: false,
|
shouldShowCheckboardTransparency: false,
|
||||||
shouldShowBrush: true,
|
shouldShowBrush: true,
|
||||||
shouldShowBrushPreview: false,
|
shouldShowBrushPreview: false,
|
||||||
isMovingBoundingBox: false,
|
|
||||||
needsRepaint: false,
|
needsRepaint: false,
|
||||||
isDrawing: false,
|
isDrawing: false,
|
||||||
stageScale: 1,
|
stageScale: 1,
|
||||||
@ -289,24 +285,6 @@ export const inpaintingSlice = createSlice({
|
|||||||
},
|
},
|
||||||
setBoundingBoxCoordinate: (state, action: PayloadAction<Vector2d>) => {
|
setBoundingBoxCoordinate: (state, action: PayloadAction<Vector2d>) => {
|
||||||
state.boundingBoxCoordinate = action.payload;
|
state.boundingBoxCoordinate = action.payload;
|
||||||
// const { x, y } = action.payload;
|
|
||||||
|
|
||||||
// const maxX =
|
|
||||||
// state.canvasDimensions.width - state.boundingBoxDimensions.width;
|
|
||||||
|
|
||||||
// const maxY =
|
|
||||||
// state.canvasDimensions.height - state.boundingBoxDimensions.height;
|
|
||||||
|
|
||||||
// const clampedX = _.clamp(x, 0, maxX);
|
|
||||||
// const clampedY = _.clamp(y, 0, maxY);
|
|
||||||
|
|
||||||
// state.boundingBoxCoordinate = { x: clampedX, y: clampedY };
|
|
||||||
},
|
|
||||||
setIsMovingBoundingBox: (state, action: PayloadAction<boolean>) => {
|
|
||||||
state.isMovingBoundingBox = action.payload;
|
|
||||||
},
|
|
||||||
toggleIsMovingBoundingBox: (state) => {
|
|
||||||
state.isMovingBoundingBox = !state.isMovingBoundingBox;
|
|
||||||
},
|
},
|
||||||
setBoundingBoxPreviewFill: (state, action: PayloadAction<RgbaColor>) => {
|
setBoundingBoxPreviewFill: (state, action: PayloadAction<RgbaColor>) => {
|
||||||
state.boundingBoxPreviewFill = action.payload;
|
state.boundingBoxPreviewFill = action.payload;
|
||||||
@ -321,9 +299,6 @@ export const inpaintingSlice = createSlice({
|
|||||||
setShouldShowBoundingBoxFill: (state, action: PayloadAction<boolean>) => {
|
setShouldShowBoundingBoxFill: (state, action: PayloadAction<boolean>) => {
|
||||||
state.shouldShowBoundingBoxFill = action.payload;
|
state.shouldShowBoundingBoxFill = action.payload;
|
||||||
},
|
},
|
||||||
setIsTransformingBoundingBox: (state, action: PayloadAction<boolean>) => {
|
|
||||||
state.isTransformingBoundingBox = action.payload;
|
|
||||||
},
|
|
||||||
setIsDrawing: (state, action: PayloadAction<boolean>) => {
|
setIsDrawing: (state, action: PayloadAction<boolean>) => {
|
||||||
state.isDrawing = action.payload;
|
state.isDrawing = action.payload;
|
||||||
},
|
},
|
||||||
@ -343,6 +318,9 @@ export const inpaintingSlice = createSlice({
|
|||||||
setShouldLockBoundingBox: (state, action: PayloadAction<boolean>) => {
|
setShouldLockBoundingBox: (state, action: PayloadAction<boolean>) => {
|
||||||
state.shouldLockBoundingBox = action.payload;
|
state.shouldLockBoundingBox = action.payload;
|
||||||
},
|
},
|
||||||
|
toggleShouldLockBoundingBox: (state) => {
|
||||||
|
state.shouldLockBoundingBox = !state.shouldLockBoundingBox;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -364,14 +342,11 @@ export const {
|
|||||||
setImageToInpaint,
|
setImageToInpaint,
|
||||||
setBoundingBoxDimensions,
|
setBoundingBoxDimensions,
|
||||||
setBoundingBoxCoordinate,
|
setBoundingBoxCoordinate,
|
||||||
setIsMovingBoundingBox,
|
|
||||||
setBoundingBoxPreviewFill,
|
setBoundingBoxPreviewFill,
|
||||||
setNeedsRepaint,
|
setNeedsRepaint,
|
||||||
setStageScale,
|
setStageScale,
|
||||||
toggleTool,
|
toggleTool,
|
||||||
toggleIsMovingBoundingBox,
|
|
||||||
setShouldShowBoundingBoxFill,
|
setShouldShowBoundingBoxFill,
|
||||||
setIsTransformingBoundingBox,
|
|
||||||
setIsDrawing,
|
setIsDrawing,
|
||||||
setShouldShowBrush,
|
setShouldShowBrush,
|
||||||
setShouldShowBoundingBox,
|
setShouldShowBoundingBox,
|
||||||
@ -379,6 +354,7 @@ export const {
|
|||||||
setShouldUseInpaintReplace,
|
setShouldUseInpaintReplace,
|
||||||
setInpaintReplace,
|
setInpaintReplace,
|
||||||
setShouldLockBoundingBox,
|
setShouldLockBoundingBox,
|
||||||
|
toggleShouldLockBoundingBox,
|
||||||
} = inpaintingSlice.actions;
|
} = inpaintingSlice.actions;
|
||||||
|
|
||||||
export default inpaintingSlice.reducer;
|
export default inpaintingSlice.reducer;
|
||||||
|
Loading…
Reference in New Issue
Block a user