feat(ui): refine canvas entity list items & menus

This commit is contained in:
psychedelicious
2024-08-23 11:09:17 +10:00
parent cff80524a8
commit c4a85cf1bf
25 changed files with 130 additions and 148 deletions

View File

@@ -1,11 +1,11 @@
import { Spacer } from '@invoke-ai/ui-library';
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
import { CanvasEntityPreviewImage } from 'features/controlLayers/components/common/CanvasEntityPreviewImage';
import { CanvasEntitySettingsWrapper } from 'features/controlLayers/components/common/CanvasEntitySettingsWrapper';
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
import { ControlLayerBadges } from 'features/controlLayers/components/ControlLayer/ControlLayerBadges';
import { ControlLayerControlAdapter } from 'features/controlLayers/components/ControlLayer/ControlLayerControlAdapter';
import { EntityLayerAdapterGate } from 'features/controlLayers/contexts/EntityAdapterContext';
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
@@ -25,10 +25,10 @@ export const ControlLayer = memo(({ id }: Props) => {
<CanvasEntityContainer>
<CanvasEntityHeader>
<CanvasEntityPreviewImage />
<CanvasEntityEnabledToggle />
<CanvasEntityEditableTitle />
<Spacer />
<CanvasEntityDeleteButton />
<ControlLayerBadges />
<CanvasEntityEnabledToggle />
</CanvasEntityHeader>
<CanvasEntitySettingsWrapper>
<ControlLayerControlAdapter />

View File

@@ -0,0 +1,26 @@
import { Badge } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { selectControlLayerOrThrow } from 'features/controlLayers/store/controlLayersReducers';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
export const ControlLayerBadges = memo(() => {
const { id } = useEntityIdentifierContext();
const { t } = useTranslation();
const withTransparencyEffect = useAppSelector(
(s) => selectControlLayerOrThrow(s.canvasV2, id).withTransparencyEffect
);
return (
<>
{withTransparencyEffect && (
<Badge color="base.300" bg="transparent" borderWidth={1} userSelect="none">
{t('controlLayers.transparency')}
</Badge>
)}
</>
);
});
ControlLayerBadges.displayName = 'ControlLayerBadges';

View File

@@ -2,7 +2,7 @@ import { MenuDivider } from '@invoke-ai/ui-library';
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
import { CanvasEntityMenuItemsFilter } from 'features/controlLayers/components/common/CanvasEntityMenuItemsFilter';
import { CanvasEntityMenuItemsReset } from 'features/controlLayers/components/common/CanvasEntityMenuItemsReset';
import { CanvasEntityMenuItemsTransform } from 'features/controlLayers/components/common/CanvasEntityMenuItemsTransform';
import { ControlLayerMenuItemsControlToRaster } from 'features/controlLayers/components/ControlLayer/ControlLayerMenuItemsControlToRaster';
import { ControlLayerMenuItemsTransparencyEffect } from 'features/controlLayers/components/ControlLayer/ControlLayerMenuItemsTransparencyEffect';
import { memo } from 'react';
@@ -10,13 +10,13 @@ import { memo } from 'react';
export const ControlLayerMenuItems = memo(() => {
return (
<>
<CanvasEntityMenuItemsTransform />
<CanvasEntityMenuItemsFilter />
<ControlLayerMenuItemsControlToRaster />
<ControlLayerMenuItemsTransparencyEffect />
<MenuDivider />
<CanvasEntityMenuItemsArrange />
<MenuDivider />
<CanvasEntityMenuItemsReset />
<CanvasEntityMenuItemsDelete />
</>
);

View File

@@ -1,6 +1,5 @@
import { Spacer } from '@invoke-ai/ui-library';
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
@@ -19,11 +18,10 @@ export const IPAdapter = memo(({ id }: Props) => {
return (
<EntityIdentifierContext.Provider value={entityIdentifier}>
<CanvasEntityContainer>
<CanvasEntityHeader>
<CanvasEntityEnabledToggle />
<CanvasEntityHeader ps={4}>
<CanvasEntityEditableTitle />
<Spacer />
<CanvasEntityDeleteButton />
<CanvasEntityEnabledToggle />
</CanvasEntityHeader>
<IPAdapterSettings />
</CanvasEntityContainer>

View File

@@ -0,0 +1,16 @@
import { MenuDivider } from '@invoke-ai/ui-library';
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
import { memo } from 'react';
export const IPAdapterMenuItems = memo(() => {
return (
<>
<CanvasEntityMenuItemsArrange />
<MenuDivider />
<CanvasEntityMenuItemsDelete />
</>
);
});
IPAdapterMenuItems.displayName = 'IPAdapterMenuItems';

View File

@@ -1,6 +1,5 @@
import { Spacer } from '@invoke-ai/ui-library';
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
import { CanvasEntityPreviewImage } from 'features/controlLayers/components/common/CanvasEntityPreviewImage';
@@ -25,11 +24,10 @@ export const InpaintMask = memo(({ id }: Props) => {
<CanvasEntityContainer>
<CanvasEntityHeader>
<CanvasEntityPreviewImage />
<CanvasEntityEnabledToggle />
<CanvasEntityEditableTitle />
<Spacer />
<InpaintMaskMaskFillColorPicker />
<CanvasEntityDeleteButton />
<CanvasEntityEnabledToggle />
</CanvasEntityHeader>
</CanvasEntityContainer>
</EntityMaskAdapterGate>

View File

@@ -2,7 +2,6 @@ import { Flex, Popover, PopoverBody, PopoverContent, PopoverTrigger } from '@inv
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import RgbColorPicker from 'common/components/RgbColorPicker';
import { rgbColorToString } from 'common/util/colorCodeTransformers';
import { stopPropagation } from 'common/util/stopPropagation';
import { MaskFillStyle } from 'features/controlLayers/components/common/MaskFillStyle';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { inpaintMaskFillColorChanged, inpaintMaskFillStyleChanged } from 'features/controlLayers/store/canvasV2Slice';
@@ -38,10 +37,9 @@ export const InpaintMaskMaskFillColorPicker = memo(() => {
borderRadius="full"
borderWidth={1}
bg={rgbColorToString(fill.color)}
w={8}
h={8}
w="22px"
h="22px"
tabIndex={-1}
onDoubleClick={stopPropagation} // double click expands the layer
/>
</PopoverTrigger>
<PopoverContent>

View File

@@ -1,10 +1,17 @@
import { CanvasEntityMenuItemsReset } from 'features/controlLayers/components/common/CanvasEntityMenuItemsReset';
import { MenuDivider } from '@invoke-ai/ui-library';
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
import { CanvasEntityMenuItemsTransform } from 'features/controlLayers/components/common/CanvasEntityMenuItemsTransform';
import { memo } from 'react';
export const InpaintMaskMenuItems = memo(() => {
return (
<>
<CanvasEntityMenuItemsReset />
<CanvasEntityMenuItemsTransform />
<MenuDivider />
<CanvasEntityMenuItemsArrange />
<MenuDivider />
<CanvasEntityMenuItemsDelete />
</>
);
});

View File

@@ -1,6 +1,5 @@
import { Spacer } from '@invoke-ai/ui-library';
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
import { CanvasEntityPreviewImage } from 'features/controlLayers/components/common/CanvasEntityPreviewImage';
@@ -23,10 +22,9 @@ export const RasterLayer = memo(({ id }: Props) => {
<CanvasEntityContainer>
<CanvasEntityHeader>
<CanvasEntityPreviewImage />
<CanvasEntityEnabledToggle />
<CanvasEntityEditableTitle />
<Spacer />
<CanvasEntityDeleteButton />
<CanvasEntityEnabledToggle />
</CanvasEntityHeader>
</CanvasEntityContainer>
</EntityLayerAdapterGate>

View File

@@ -2,7 +2,6 @@ import { MenuDivider } from '@invoke-ai/ui-library';
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
import { CanvasEntityMenuItemsFilter } from 'features/controlLayers/components/common/CanvasEntityMenuItemsFilter';
import { CanvasEntityMenuItemsReset } from 'features/controlLayers/components/common/CanvasEntityMenuItemsReset';
import { CanvasEntityMenuItemsTransform } from 'features/controlLayers/components/common/CanvasEntityMenuItemsTransform';
import { RasterLayerMenuItemsRasterToControl } from 'features/controlLayers/components/RasterLayer/RasterLayerMenuItemsRasterToControl';
import { memo } from 'react';
@@ -10,13 +9,12 @@ import { memo } from 'react';
export const RasterLayerMenuItems = memo(() => {
return (
<>
<CanvasEntityMenuItemsTransform />
<CanvasEntityMenuItemsFilter />
<RasterLayerMenuItemsRasterToControl />
<CanvasEntityMenuItemsTransform />
<MenuDivider />
<CanvasEntityMenuItemsArrange />
<MenuDivider />
<CanvasEntityMenuItemsReset />
<CanvasEntityMenuItemsDelete />
</>
);

View File

@@ -1,6 +1,5 @@
import { Spacer } from '@invoke-ai/ui-library';
import { CanvasEntityContainer } from 'features/controlLayers/components/common/CanvasEntityContainer';
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
import { CanvasEntityPreviewImage } from 'features/controlLayers/components/common/CanvasEntityPreviewImage';
@@ -13,7 +12,6 @@ import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types'
import { memo, useMemo } from 'react';
import { RegionalGuidanceMaskFillColorPicker } from './RegionalGuidanceMaskFillColorPicker';
import { RegionalGuidanceSettingsPopover } from './RegionalGuidanceSettingsPopover';
type Props = {
id: string;
@@ -28,13 +26,11 @@ export const RegionalGuidance = memo(({ id }: Props) => {
<CanvasEntityContainer>
<CanvasEntityHeader>
<CanvasEntityPreviewImage />
<CanvasEntityEnabledToggle />
<CanvasEntityEditableTitle />
<Spacer />
<RegionalGuidanceBadges />
<RegionalGuidanceMaskFillColorPicker />
<RegionalGuidanceSettingsPopover />
<CanvasEntityDeleteButton />
<CanvasEntityEnabledToggle />
</CanvasEntityHeader>
<RegionalGuidanceSettings />
</CanvasEntityContainer>

View File

@@ -12,7 +12,7 @@ export const RegionalGuidanceBadges = memo(() => {
return (
<>
{autoNegative === 'invert' && (
{autoNegative && (
<Badge color="base.300" bg="transparent" borderWidth={1} userSelect="none">
{t('controlLayers.autoNegative')}
</Badge>

View File

@@ -2,7 +2,6 @@ import { Flex, Popover, PopoverBody, PopoverContent, PopoverTrigger } from '@inv
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import RgbColorPicker from 'common/components/RgbColorPicker';
import { rgbColorToString } from 'common/util/colorCodeTransformers';
import { stopPropagation } from 'common/util/stopPropagation';
import { MaskFillStyle } from 'features/controlLayers/components/common/MaskFillStyle';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { rgFillColorChanged, rgFillStyleChanged } from 'features/controlLayers/store/canvasV2Slice';
@@ -37,10 +36,9 @@ export const RegionalGuidanceMaskFillColorPicker = memo(() => {
borderRadius="full"
borderWidth={1}
bg={rgbColorToString(fill.color)}
w={8}
h={8}
w="22px"
h="22px"
tabIndex={-1}
onDoubleClick={stopPropagation} // double click expands the layer
/>
</PopoverTrigger>
<PopoverContent>

View File

@@ -1,8 +1,9 @@
import { MenuDivider } from '@invoke-ai/ui-library';
import { CanvasEntityMenuItemsArrange } from 'features/controlLayers/components/common/CanvasEntityMenuItemsArrange';
import { CanvasEntityMenuItemsDelete } from 'features/controlLayers/components/common/CanvasEntityMenuItemsDelete';
import { CanvasEntityMenuItemsReset } from 'features/controlLayers/components/common/CanvasEntityMenuItemsReset';
import { CanvasEntityMenuItemsTransform } from 'features/controlLayers/components/common/CanvasEntityMenuItemsTransform';
import { RegionalGuidanceMenuItemsAddPromptsAndIPAdapter } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsAddPromptsAndIPAdapter';
import { RegionalGuidanceMenuItemsAutoNegative } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsAutoNegative';
import { memo } from 'react';
export const RegionalGuidanceMenuItems = memo(() => {
@@ -10,9 +11,11 @@ export const RegionalGuidanceMenuItems = memo(() => {
<>
<RegionalGuidanceMenuItemsAddPromptsAndIPAdapter />
<MenuDivider />
<CanvasEntityMenuItemsTransform />
<RegionalGuidanceMenuItemsAutoNegative />
<MenuDivider />
<CanvasEntityMenuItemsArrange />
<MenuDivider />
<CanvasEntityMenuItemsReset />
<CanvasEntityMenuItemsDelete />
</>
);

View File

@@ -0,0 +1,26 @@
import { MenuItem } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { rgAutoNegativeToggled } from 'features/controlLayers/store/canvasV2Slice';
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiSelectionInverseBold } from 'react-icons/pi';
export const RegionalGuidanceMenuItemsAutoNegative = memo(() => {
const { id } = useEntityIdentifierContext();
const { t } = useTranslation();
const dispatch = useAppDispatch();
const autoNegative = useAppSelector((s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, id).autoNegative);
const onClick = useCallback(() => {
dispatch(rgAutoNegativeToggled({ id }));
}, [dispatch, id]);
return (
<MenuItem icon={<PiSelectionInverseBold />} onClick={onClick}>
{autoNegative ? t('controlLayers.disableAutoNegative') : t('controlLayers.enableAutoNegative')}
</MenuItem>
);
});
RegionalGuidanceMenuItemsAutoNegative.displayName = 'RegionalGuidanceMenuItemsAutoNegative';

View File

@@ -1,63 +0,0 @@
import {
Checkbox,
Flex,
FormControl,
FormLabel,
IconButton,
Popover,
PopoverArrow,
PopoverBody,
PopoverContent,
PopoverTrigger,
} from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { stopPropagation } from 'common/util/stopPropagation';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { rgAutoNegativeChanged } from 'features/controlLayers/store/canvasV2Slice';
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
import type { ChangeEvent } from 'react';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiGearSixBold } from 'react-icons/pi';
export const RegionalGuidanceSettingsPopover = memo(() => {
const entityIdentifier = useEntityIdentifierContext();
const { t } = useTranslation();
const dispatch = useAppDispatch();
const autoNegative = useAppSelector((s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, entityIdentifier.id).autoNegative);
const onChange = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
dispatch(rgAutoNegativeChanged({ id: entityIdentifier.id, autoNegative: e.target.checked ? 'invert' : 'off' }));
},
[dispatch, entityIdentifier.id]
);
return (
<Popover isLazy>
<PopoverTrigger>
<IconButton
tooltip={t('common.settingsLabel')}
aria-label={t('common.settingsLabel')}
size="sm"
icon={<PiGearSixBold />}
onDoubleClick={stopPropagation} // double click expands the layer
/>
</PopoverTrigger>
<PopoverContent>
<PopoverArrow />
<PopoverBody>
<Flex direction="column" gap={2}>
<FormControl gap={2}>
<FormLabel flexGrow={1} minW={32} m={0}>
{t('controlLayers.autoNegative')}
</FormLabel>
<Checkbox size="md" isChecked={autoNegative === 'invert'} onChange={onChange} />
</FormControl>
</Flex>
</PopoverBody>
</PopoverContent>
</Popover>
);
});
RegionalGuidanceSettingsPopover.displayName = 'RegionalGuidanceSettingsPopover';

View File

@@ -18,7 +18,7 @@ export const BeginEndStepPct = memo(({ beginEndStepPct, onChange }: Props) => {
}, [onChange]);
return (
<FormControl orientation="horizontal">
<FormControl orientation="horizontal" pe={1}>
<InformationalPopover feature="controlNetBeginEnd">
<FormLabel m={0}>{t('controlnet.beginEndStepPercentShort')}</FormLabel>
</InformationalPopover>

View File

@@ -26,7 +26,6 @@ export const CanvasEntityEnabledToggle = memo(() => {
variant="outline"
icon={isEnabled ? <PiCheckBold /> : undefined}
onClick={onClick}
colorScheme={isEnabled ? 'invokeBlue' : 'base'}
onDoubleClick={stopPropagation} // double click expands the layer
/>
);

View File

@@ -2,6 +2,7 @@ import type { FlexProps } from '@invoke-ai/ui-library';
import { ContextMenu, Flex, MenuList } from '@invoke-ai/ui-library';
import { ControlLayerMenuItems } from 'features/controlLayers/components/ControlLayer/ControlLayerMenuItems';
import { InpaintMaskMenuItems } from 'features/controlLayers/components/InpaintMask/InpaintMaskMenuItems';
import { IPAdapterMenuItems } from 'features/controlLayers/components/IPAdapter/IPAdapterMenuItems';
import { RasterLayerMenuItems } from 'features/controlLayers/components/RasterLayer/RasterLayerMenuItems';
import { RegionalGuidanceMenuItems } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItems';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
@@ -44,7 +45,11 @@ export const CanvasEntityHeader = memo(({ children, ...rest }: FlexProps) => {
}
if (entityIdentifier.type === 'ip_adapter') {
return <MenuList>{/* no items for IP adapter yet */}</MenuList>;
return (
<MenuList>
<IPAdapterMenuItems />
</MenuList>
);
}
assert(false, 'Unhandled entity type');
@@ -53,7 +58,7 @@ export const CanvasEntityHeader = memo(({ children, ...rest }: FlexProps) => {
return (
<ContextMenu renderMenu={renderMenu}>
{(ref) => (
<Flex ref={ref} h={16} gap={2} alignItems="center" p={2} {...rest}>
<Flex ref={ref} gap={2} alignItems="center" p={2} {...rest}>
{children}
</Flex>
)}

View File

@@ -33,6 +33,16 @@ const getIndexAndCount = (
index: canvasV2.regions.entities.findIndex((entity) => entity.id === id),
count: canvasV2.regions.entities.length,
};
} else if (type === 'inpaint_mask') {
return {
index: canvasV2.inpaintMasks.entities.findIndex((entity) => entity.id === id),
count: canvasV2.inpaintMasks.entities.length,
};
} else if (type === 'ip_adapter') {
return {
index: canvasV2.ipAdapters.entities.findIndex((entity) => entity.id === id),
count: canvasV2.ipAdapters.entities.length,
};
} else {
return {
index: -1,

View File

@@ -4,7 +4,7 @@ import { memo } from 'react';
export const CanvasEntitySettingsWrapper = memo(({ children }: PropsWithChildren) => {
return (
<Flex flexDir="column" gap={3} px={3} pb={3}>
<Flex flexDir="column" gap={2} px={2} pb={2}>
{children}
</Flex>
);