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

View File

@ -194,12 +194,12 @@ export default function HotkeysModal({ children }: HotkeysModalProps) {
{
title: 'Lock Bounding Box',
desc: 'Locks the bounding box',
hotkey: 'M',
hotkey: 'Shift+Q',
},
{
title: 'Quick Toggle Lock Bounding Box',
desc: 'Hold to toggle locking the bounding box',
hotkey: 'Space',
hotkey: 'Q',
},
{
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 { MdInvertColors, MdInvertColorsOff } from 'react-icons/md';
import { BiHide, BiShow } from 'react-icons/bi';
import InpaintingCanvasStatusIcons from './InpaintingCanvasStatusIcons';
// Use a closure allow other components to use these things... not ideal...
export let stageRef: MutableRefObject<StageType | null>;
@ -243,39 +244,7 @@ const InpaintingCanvas = () => {
return (
<div className="inpainting-canvas-container" tabIndex={1}>
<div className="inpainting-canvas-wrapper">
<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"
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>
<InpaintingCanvasStatusIcons />
{canvasBgImage && (
<Stage
@ -330,17 +299,16 @@ const InpaintingCanvas = () => {
/>
)}
</Layer>
{shouldShowMask && (
<Layer>
{shouldShowBoundingBoxFill && shouldShowBoundingBox && (
<InpaintingBoundingBoxPreviewOverlay />
)}
<InpaintingBoundingBoxPreview />
{shouldShowBoundingBox && <InpaintingBoundingBoxPreview />}
{!isMouseOverBoundingBox && !isModifyingBoundingBox && (
<InpaintingCanvasBrushPreviewOutline />
)}
</Layer>
)}
</>
)}
</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
useHotkeys(
'm',
'shift+q',
(e: KeyboardEvent) => {
e.preventDefault();
dispatch(toggleShouldLockBoundingBox());

View File

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

View File

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