mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
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:
parent
423ae32097
commit
0eef74bc00
@ -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">
|
||||||
|
@ -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',
|
||||||
|
@ -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 />
|
)}
|
||||||
)}
|
{shouldShowBoundingBox && <InpaintingBoundingBoxPreview />}
|
||||||
<InpaintingBoundingBoxPreview />
|
|
||||||
{!isMouseOverBoundingBox && !isModifyingBoundingBox && (
|
{!isMouseOverBoundingBox && !isModifyingBoundingBox && (
|
||||||
<InpaintingCanvasBrushPreviewOutline />
|
<InpaintingCanvasBrushPreviewOutline />
|
||||||
)}
|
)}
|
||||||
</Layer>
|
</Layer>
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Stage>
|
</Stage>
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
@ -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());
|
||||||
|
@ -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;
|
||||||
|
@ -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';
|
||||||
|
Loading…
Reference in New Issue
Block a user