Improves bounding box hotkeys/UX

This commit is contained in:
psychedelicious 2022-10-28 14:53:07 +11:00
parent dd71066391
commit ed45dca7c1
7 changed files with 57 additions and 104 deletions

View File

@ -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;

View File

@ -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)}

View File

@ -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 && (

View File

@ -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',

View File

@ -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}

View File

@ -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;
} }
} }

View File

@ -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;