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 *;
.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;

View File

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

View File

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

View File

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

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

View File

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

View File

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