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 *;
|
||||
|
||||
.hotkeys-modal {
|
||||
width: 36rem !important;
|
||||
max-width: 36rem !important;
|
||||
display: grid;
|
||||
padding: 1rem;
|
||||
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 {
|
||||
max-height: 32rem;
|
||||
max-height: 36rem;
|
||||
overflow-y: scroll;
|
||||
@include HideScrollbar;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {
|
||||
Accordion,
|
||||
AccordionButton,
|
||||
AccordionIcon,
|
||||
AccordionItem,
|
||||
AccordionPanel,
|
||||
Modal,
|
||||
@ -181,8 +182,13 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
|
||||
hotkey: 'Ctrl+Shift+Z, Ctrl+Y',
|
||||
},
|
||||
{
|
||||
title: 'Move Bounding Box',
|
||||
desc: 'Hold to move bounding box',
|
||||
title: 'Lock 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',
|
||||
},
|
||||
{
|
||||
@ -225,8 +231,9 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
|
||||
<div className="hotkeys-modal-items">
|
||||
<Accordion allowToggle allowMultiple>
|
||||
<AccordionItem>
|
||||
<AccordionButton>
|
||||
<AccordionButton className="hotkeys-modal-button">
|
||||
<h2>App Hotkeys</h2>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
<AccordionPanel>
|
||||
{renderHotkeyModalItems(appHotkeys)}
|
||||
@ -234,8 +241,9 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem>
|
||||
<AccordionButton>
|
||||
<AccordionButton className="hotkeys-modal-button">
|
||||
<h2>General Hotkeys</h2>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
<AccordionPanel>
|
||||
{renderHotkeyModalItems(generalHotkeys)}
|
||||
@ -243,8 +251,9 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem>
|
||||
<AccordionButton>
|
||||
<AccordionButton className="hotkeys-modal-button">
|
||||
<h2>Gallery Hotkeys</h2>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
<AccordionPanel>
|
||||
{renderHotkeyModalItems(galleryHotkeys)}
|
||||
@ -252,8 +261,9 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem>
|
||||
<AccordionButton>
|
||||
<AccordionButton className="hotkeys-modal-button">
|
||||
<h2>Inpainting Hotkeys</h2>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
<AccordionPanel>
|
||||
{renderHotkeyModalItems(inpaintingHotkeys)}
|
||||
|
@ -220,8 +220,17 @@ const InpaintingCanvas = () => {
|
||||
return (
|
||||
<div className="inpainting-canvas-wrapper checkerboard" tabIndex={1}>
|
||||
<div className="inpainting-alerts">
|
||||
{!shouldShowMask && <div>Mask Hidden (H)</div>}
|
||||
{shouldInvertMask && <div>Mask Inverted (Shift+M)</div>}
|
||||
{!shouldShowMask && (
|
||||
<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>
|
||||
|
||||
{canvasBgImage && (
|
||||
|
@ -23,6 +23,7 @@ import {
|
||||
setShouldShowMask,
|
||||
setShouldInvertMask,
|
||||
setNeedsRepaint,
|
||||
toggleShouldLockBoundingBox,
|
||||
} from './inpaintingSlice';
|
||||
|
||||
import { MdInvertColors, MdInvertColorsOff } from 'react-icons/md';
|
||||
@ -144,6 +145,19 @@ const InpaintingControls = () => {
|
||||
[activeTabName, shouldShowMask]
|
||||
);
|
||||
|
||||
// Toggle lock bounding box
|
||||
useHotkeys(
|
||||
'm',
|
||||
(e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
dispatch(toggleShouldLockBoundingBox());
|
||||
},
|
||||
{
|
||||
enabled: activeTabName === 'inpainting' && shouldShowMask,
|
||||
},
|
||||
[activeTabName, shouldShowMask]
|
||||
);
|
||||
|
||||
// Undo
|
||||
useHotkeys(
|
||||
'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 dispatch = useAppDispatch();
|
||||
const {
|
||||
@ -326,15 +266,14 @@ const InpaintingBoundingBoxPreview = () => {
|
||||
strokeWidth={strokeWidth}
|
||||
listening={!shouldLockBoundingBox}
|
||||
onMouseEnter={(e) => {
|
||||
// style stage container:
|
||||
const container = e?.target?.getStage()?.container();
|
||||
if (!container) return;
|
||||
container.style.cursor = 'move';
|
||||
container.style.cursor = shouldLockBoundingBox ? 'none' : 'move';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
const container = e?.target?.getStage()?.container();
|
||||
if (!container) return;
|
||||
container.style.cursor = 'default';
|
||||
container.style.cursor = shouldLockBoundingBox ? 'none' : 'default';
|
||||
}}
|
||||
draggable={!shouldLockBoundingBox}
|
||||
onDragMove={handleOnDragMove}
|
||||
|
@ -12,7 +12,6 @@ import {
|
||||
InpaintingState,
|
||||
setIsDrawing,
|
||||
setShouldLockBoundingBox,
|
||||
setShouldShowBrush,
|
||||
toggleTool,
|
||||
} from '../inpaintingSlice';
|
||||
|
||||
@ -91,10 +90,8 @@ const KeyboardEventManager = () => {
|
||||
case ' ': {
|
||||
if (e.type === 'keydown') {
|
||||
dispatch(setIsDrawing(false));
|
||||
dispatch(setShouldLockBoundingBox(false));
|
||||
} else {
|
||||
dispatch(setShouldLockBoundingBox(true));
|
||||
}
|
||||
dispatch(setShouldLockBoundingBox(!shouldLockBoundingBox));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -36,11 +36,9 @@ export interface InpaintingState {
|
||||
canvasDimensions: Dimensions;
|
||||
boundingBoxDimensions: Dimensions;
|
||||
boundingBoxCoordinate: Vector2d;
|
||||
isMovingBoundingBox: boolean;
|
||||
boundingBoxPreviewFill: RgbaColor;
|
||||
shouldShowBoundingBox: boolean;
|
||||
shouldShowBoundingBoxFill: boolean;
|
||||
isTransformingBoundingBox: boolean;
|
||||
lines: MaskLine[];
|
||||
pastLines: MaskLine[][];
|
||||
futureLines: MaskLine[][];
|
||||
@ -68,7 +66,6 @@ const initialInpaintingState: InpaintingState = {
|
||||
boundingBoxPreviewFill: { r: 0, g: 0, b: 0, a: 0.7 },
|
||||
shouldShowBoundingBox: false,
|
||||
shouldShowBoundingBoxFill: false,
|
||||
isTransformingBoundingBox: false,
|
||||
cursorPosition: null,
|
||||
lines: [],
|
||||
pastLines: [],
|
||||
@ -78,7 +75,6 @@ const initialInpaintingState: InpaintingState = {
|
||||
shouldShowCheckboardTransparency: false,
|
||||
shouldShowBrush: true,
|
||||
shouldShowBrushPreview: false,
|
||||
isMovingBoundingBox: false,
|
||||
needsRepaint: false,
|
||||
isDrawing: false,
|
||||
stageScale: 1,
|
||||
@ -289,24 +285,6 @@ export const inpaintingSlice = createSlice({
|
||||
},
|
||||
setBoundingBoxCoordinate: (state, action: PayloadAction<Vector2d>) => {
|
||||
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>) => {
|
||||
state.boundingBoxPreviewFill = action.payload;
|
||||
@ -321,9 +299,6 @@ export const inpaintingSlice = createSlice({
|
||||
setShouldShowBoundingBoxFill: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldShowBoundingBoxFill = action.payload;
|
||||
},
|
||||
setIsTransformingBoundingBox: (state, action: PayloadAction<boolean>) => {
|
||||
state.isTransformingBoundingBox = action.payload;
|
||||
},
|
||||
setIsDrawing: (state, action: PayloadAction<boolean>) => {
|
||||
state.isDrawing = action.payload;
|
||||
},
|
||||
@ -343,6 +318,9 @@ export const inpaintingSlice = createSlice({
|
||||
setShouldLockBoundingBox: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldLockBoundingBox = action.payload;
|
||||
},
|
||||
toggleShouldLockBoundingBox: (state) => {
|
||||
state.shouldLockBoundingBox = !state.shouldLockBoundingBox;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -364,14 +342,11 @@ export const {
|
||||
setImageToInpaint,
|
||||
setBoundingBoxDimensions,
|
||||
setBoundingBoxCoordinate,
|
||||
setIsMovingBoundingBox,
|
||||
setBoundingBoxPreviewFill,
|
||||
setNeedsRepaint,
|
||||
setStageScale,
|
||||
toggleTool,
|
||||
toggleIsMovingBoundingBox,
|
||||
setShouldShowBoundingBoxFill,
|
||||
setIsTransformingBoundingBox,
|
||||
setIsDrawing,
|
||||
setShouldShowBrush,
|
||||
setShouldShowBoundingBox,
|
||||
@ -379,6 +354,7 @@ export const {
|
||||
setShouldUseInpaintReplace,
|
||||
setInpaintReplace,
|
||||
setShouldLockBoundingBox,
|
||||
toggleShouldLockBoundingBox,
|
||||
} = inpaintingSlice.actions;
|
||||
|
||||
export default inpaintingSlice.reducer;
|
||||
|
Loading…
Reference in New Issue
Block a user