Address bounding box feedback

- Adds back toggle to hide bounding box
- Box quick toggle = q, normal toggle = shift + q
- Styles canvas alert icons
This commit is contained in:
psychedelicious 2022-11-01 23:40:11 +11:00 committed by Lincoln Stein
parent 423ae32097
commit 0eef74bc00
9 changed files with 143 additions and 55 deletions

View File

@ -113,7 +113,7 @@ const BoundingBoxSettings = () => {
<div className="inpainting-bounding-box-settings"> <div className="inpainting-bounding-box-settings">
<div className="inpainting-bounding-box-header"> <div className="inpainting-bounding-box-header">
<p>Inpaint Box</p> <p>Inpaint Box</p>
{/* <IAIIconButton <IAIIconButton
aria-label="Toggle Bounding Box Visibility" aria-label="Toggle Bounding Box Visibility"
icon={ icon={
shouldShowBoundingBox ? <BiShow size={22} /> : <BiHide size={22} /> shouldShowBoundingBox ? <BiShow size={22} /> : <BiHide size={22} />
@ -121,7 +121,7 @@ const BoundingBoxSettings = () => {
onClick={handleShowBoundingBox} onClick={handleShowBoundingBox}
background={'none'} background={'none'}
padding={0} padding={0}
/> */} />
</div> </div>
<div className="inpainting-bounding-box-settings-items"> <div className="inpainting-bounding-box-settings-items">
<div className="inpainting-bounding-box-dimensions-slider-numberinput"> <div className="inpainting-bounding-box-dimensions-slider-numberinput">

View File

@ -194,12 +194,12 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
{ {
title: 'Lock Bounding Box', title: 'Lock Bounding Box',
desc: 'Locks the bounding box', desc: 'Locks the bounding box',
hotkey: 'M', hotkey: 'Shift+Q',
}, },
{ {
title: 'Quick Toggle Lock Bounding Box', title: 'Quick Toggle Lock Bounding Box',
desc: 'Hold to toggle locking the bounding box', desc: 'Hold to toggle locking the bounding box',
hotkey: 'Space', hotkey: 'Q',
}, },
{ {
title: 'Expand Inpainting Area', title: 'Expand Inpainting Area',

View File

@ -38,6 +38,7 @@ import { Icon, IconButton, Tooltip, useToast } from '@chakra-ui/react';
import { FaLock, FaUnlock } from 'react-icons/fa'; import { FaLock, FaUnlock } from 'react-icons/fa';
import { MdInvertColors, MdInvertColorsOff } from 'react-icons/md'; import { MdInvertColors, MdInvertColorsOff } from 'react-icons/md';
import { BiHide, BiShow } from 'react-icons/bi'; import { BiHide, BiShow } from 'react-icons/bi';
import InpaintingCanvasStatusIcons from './InpaintingCanvasStatusIcons';
// Use a closure allow other components to use these things... not ideal... // Use a closure allow other components to use these things... not ideal...
export let stageRef: MutableRefObject<StageType | null>; export let stageRef: MutableRefObject<StageType | null>;
@ -243,39 +244,7 @@ const InpaintingCanvas = () => {
return ( return (
<div className="inpainting-canvas-container" tabIndex={1}> <div className="inpainting-canvas-container" tabIndex={1}>
<div className="inpainting-canvas-wrapper"> <div className="inpainting-canvas-wrapper">
<div className="inpainting-alerts"> <InpaintingCanvasStatusIcons />
<div style={{ pointerEvents: 'none' }}>
<IconButton
aria-label="Show/HideMask"
size="xs"
variant={'ghost'}
fontSize={'1rem'}
data-selected={!shouldShowMask}
icon={shouldShowMask ? <BiShow /> : <BiHide />}
/>
</div>
<div style={{ pointerEvents: 'none' }}>
<IconButton
aria-label="Invert Mask"
size="xs"
variant={'ghost'}
fontSize={'1rem'}
data-selected={shouldInvertMask}
icon={
shouldInvertMask ? <MdInvertColors /> : <MdInvertColorsOff />
}
/>
</div>
<div style={{ pointerEvents: 'none' }}>
<IconButton
aria-label="Bounding Box Lock"
size="xs"
variant={'ghost'}
data-selected={shouldLockBoundingBox}
icon={shouldLockBoundingBox ? <FaLock /> : <FaUnlock />}
/>
</div>
</div>
{canvasBgImage && ( {canvasBgImage && (
<Stage <Stage
@ -330,17 +299,16 @@ const InpaintingCanvas = () => {
/> />
)} )}
</Layer> </Layer>
{shouldShowMask && (
<Layer> <Layer>
{shouldShowBoundingBoxFill && shouldShowBoundingBox && ( {shouldShowBoundingBoxFill && shouldShowBoundingBox && (
<InpaintingBoundingBoxPreviewOverlay /> <InpaintingBoundingBoxPreviewOverlay />
)} )}
<InpaintingBoundingBoxPreview /> {shouldShowBoundingBox && <InpaintingBoundingBoxPreview />}
{!isMouseOverBoundingBox && !isModifyingBoundingBox && ( {!isMouseOverBoundingBox && !isModifyingBoundingBox && (
<InpaintingCanvasBrushPreviewOutline /> <InpaintingCanvasBrushPreviewOutline />
)} )}
</Layer> </Layer>
)}
</> </>
)} )}
</Stage> </Stage>

View File

@ -0,0 +1,31 @@
.inpainting-alerts {
position: absolute;
top: 0;
left: 0;
display: flex;
z-index: 2;
margin: 0.25rem;
padding: 0.25rem;
pointer-events: none;
background-color: var(--text-color);
opacity: 0.7;
border-radius: 0.5rem;
button {
svg {
fill: var(--background-color);
}
&[data-selected='true'] {
svg {
fill: var(--accent-color-hover);
}
}
&[data-alert='true'] {
svg {
fill: var(--destructive-color-hover);
}
}
}
}

View File

@ -0,0 +1,81 @@
const inpaintingCanvasStatusIconsSelector = createSelector(
(state: RootState) => state.inpainting,
(inpainting: InpaintingState) => {
const {
shouldShowMask,
shouldInvertMask,
shouldLockBoundingBox,
shouldShowBoundingBox,
} = inpainting;
return {
shouldShowMask,
shouldInvertMask,
shouldLockBoundingBox,
shouldShowBoundingBox,
};
}
);
import { IconButton } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { BiHide, BiShow } from 'react-icons/bi';
import { BsBoundingBox } from 'react-icons/bs';
import { FaLock, FaUnlock } from 'react-icons/fa';
import { MdInvertColors, MdInvertColorsOff } from 'react-icons/md';
import { RootState, useAppSelector } from '../../../app/store';
import { InpaintingState } from './inpaintingSlice';
const InpaintingCanvasStatusIcons = () => {
const {
shouldShowMask,
shouldInvertMask,
shouldLockBoundingBox,
shouldShowBoundingBox,
} = useAppSelector(inpaintingCanvasStatusIconsSelector);
return (
<div className="inpainting-alerts">
<div style={{ pointerEvents: 'none' }}>
<IconButton
aria-label="Show/HideMask"
size="xs"
variant={'ghost'}
fontSize={'1rem'}
data-selected={!shouldShowMask}
icon={shouldShowMask ? <BiShow /> : <BiHide />}
/>
</div>
<div style={{ pointerEvents: 'none' }}>
<IconButton
aria-label="Invert Mask"
variant={'ghost'}
size="xs"
fontSize={'1rem'}
data-selected={shouldInvertMask}
icon={shouldInvertMask ? <MdInvertColors /> : <MdInvertColorsOff />}
/>
</div>
<div style={{ pointerEvents: 'none' }}>
<IconButton
aria-label="Bounding Box Lock"
size="xs"
variant={'ghost'}
data-selected={shouldLockBoundingBox}
icon={shouldLockBoundingBox ? <FaLock /> : <FaUnlock />}
/>
</div>
<div style={{ pointerEvents: 'none' }}>
<IconButton
aria-label="Bounding Box Lock"
size="xs"
variant={'ghost'}
data-alert={!shouldShowBoundingBox}
icon={<BsBoundingBox />}
/>
</div>
</div>
);
};
export default InpaintingCanvasStatusIcons;

View File

@ -154,7 +154,7 @@ const InpaintingControls = () => {
// Toggle lock bounding box // Toggle lock bounding box
useHotkeys( useHotkeys(
'm', 'shift+q',
(e: KeyboardEvent) => { (e: KeyboardEvent) => {
e.preventDefault(); e.preventDefault();
dispatch(toggleShouldLockBoundingBox()); dispatch(toggleShouldLockBoundingBox());

View File

@ -24,13 +24,18 @@ const keyboardEventManagerSelector = createSelector(
activeTabNameSelector, activeTabNameSelector,
], ],
(options: OptionsState, inpainting: InpaintingState, activeTabName) => { (options: OptionsState, inpainting: InpaintingState, activeTabName) => {
const { shouldShowMask, cursorPosition, shouldLockBoundingBox } = const {
inpainting; shouldShowMask,
cursorPosition,
shouldLockBoundingBox,
shouldShowBoundingBox,
} = inpainting;
return { return {
activeTabName, activeTabName,
shouldShowMask, shouldShowMask,
isCursorOnCanvas: Boolean(cursorPosition), isCursorOnCanvas: Boolean(cursorPosition),
shouldLockBoundingBox, shouldLockBoundingBox,
shouldShowBoundingBox,
}; };
}, },
{ {
@ -47,6 +52,7 @@ const KeyboardEventManager = () => {
activeTabName, activeTabName,
isCursorOnCanvas, isCursorOnCanvas,
shouldLockBoundingBox, shouldLockBoundingBox,
shouldShowBoundingBox,
} = useAppSelector(keyboardEventManagerSelector); } = useAppSelector(keyboardEventManagerSelector);
const wasLastEventOverCanvas = useRef<boolean>(false); const wasLastEventOverCanvas = useRef<boolean>(false);
@ -55,7 +61,7 @@ const KeyboardEventManager = () => {
useEffect(() => { useEffect(() => {
const listener = (e: KeyboardEvent) => { const listener = (e: KeyboardEvent) => {
if ( if (
!['x', ' '].includes(e.key) || !['x', 'q'].includes(e.key) ||
activeTabName !== 'inpainting' || activeTabName !== 'inpainting' ||
!shouldShowMask !shouldShowMask
) { ) {
@ -93,8 +99,8 @@ const KeyboardEventManager = () => {
dispatch(toggleTool()); dispatch(toggleTool());
break; break;
} }
case ' ': { case 'q': {
if (!shouldShowMask) break; if (!shouldShowMask || !shouldShowBoundingBox) break;
dispatch(setIsSpacebarHeld(e.type === 'keydown')); dispatch(setIsSpacebarHeld(e.type === 'keydown'));
dispatch(setShouldLockBoundingBox(e.type !== 'keydown')); dispatch(setShouldLockBoundingBox(e.type !== 'keydown'));
break; break;
@ -118,6 +124,7 @@ const KeyboardEventManager = () => {
shouldShowMask, shouldShowMask,
isCursorOnCanvas, isCursorOnCanvas,
shouldLockBoundingBox, shouldLockBoundingBox,
shouldShowBoundingBox,
]); ]);
return null; return null;

View File

@ -39,8 +39,9 @@
@use '../features/tabs/InvokeOptionsPanel.scss'; @use '../features/tabs/InvokeOptionsPanel.scss';
@use '../features/tabs/TextToImage/TextToImage.scss'; @use '../features/tabs/TextToImage/TextToImage.scss';
@use '../features/tabs/ImageToImage/ImageToImage.scss'; @use '../features/tabs/ImageToImage/ImageToImage.scss';
@use '../features/tabs/Inpainting/Inpainting.scss';
@use '../features/tabs/FloatingButton.scss'; @use '../features/tabs/FloatingButton.scss';
@use '../features/tabs/Inpainting/Inpainting.scss';
@use '../features/tabs/Inpainting/InpaintingCanvasStatusIcons.scss';
// Component Shared // Component Shared
@use '../common/components/IAINumberInput.scss'; @use '../common/components/IAINumberInput.scss';