feat(ui): add + buttons to entity categories

This commit is contained in:
psychedelicious 2024-08-29 10:15:35 +10:00
parent 0098c33f81
commit 085cc82926
7 changed files with 111 additions and 32 deletions

View File

@ -1684,27 +1684,34 @@
"addPositivePrompt": "Add $t(common.positivePrompt)", "addPositivePrompt": "Add $t(common.positivePrompt)",
"addNegativePrompt": "Add $t(common.negativePrompt)", "addNegativePrompt": "Add $t(common.negativePrompt)",
"addIPAdapter": "Add $t(common.ipAdapter)", "addIPAdapter": "Add $t(common.ipAdapter)",
"addRasterLayer": "Add $t(controlLayers.rasterLayer)",
"addControlLayer": "Add $t(controlLayers.controlLayer)",
"addInpaintMask": "Add $t(controlLayers.inpaintMask)",
"addRegionalGuidance": "Add $t(controlLayers.regionalGuidance)",
"regionalGuidanceLayer": "$t(controlLayers.regionalGuidance) $t(unifiedCanvas.layer)", "regionalGuidanceLayer": "$t(controlLayers.regionalGuidance) $t(unifiedCanvas.layer)",
"raster": "Raster", "raster": "Raster",
"rasterLayer_one": "Raster Layer", "rasterLayer": "Raster Layer",
"controlLayer_one": "Control Layer", "controlLayer": "Control Layer",
"inpaintMask_one": "Inpaint Mask", "inpaintMask": "Inpaint Mask",
"regionalGuidance_one": "Regional Guidance", "regionalGuidance": "Regional Guidance",
"ipAdapter_one": "IP Adapter", "ipAdapter": "IP Adapter",
"rasterLayer_other": "Raster Layers", "rasterLayer_withCount_one": "$t(controlLayers.rasterLayer)",
"controlLayer_other": "Control Layers", "controlLayer_withCount_one": "$t(controlLayers.controlLayer)",
"inpaintMask_other": "Inpaint Masks", "inpaintMask_withCount_one": "$t(controlLayers.inpaintMask)",
"regionalGuidance_other": "Regional Guidance", "regionalGuidance_withCount_one": "$t(controlLayers.regionalGuidance)",
"ipAdapter_other": "IP Adapters", "ipAdapter_withCount_one": "$t(controlLayers.ipAdapter)",
"rasterLayer_withCount_other": "Raster Layers",
"controlLayer_withCount_other": "Control Layers",
"inpaintMask_withCount_other": "Inpaint Masks",
"regionalGuidance_withCount_other": "Regional Guidance",
"ipAdapter_withCount_other": "IP Adapters",
"opacity": "Opacity", "opacity": "Opacity",
"regionalGuidance_withCount_hidden": "Regional Guidance ({{count}} hidden)", "regionalGuidance_withCount_hidden": "Regional Guidance ({{count}} hidden)",
"controlAdapters_withCount_hidden": "Control Adapters ({{count}} hidden)",
"controlLayers_withCount_hidden": "Control Layers ({{count}} hidden)", "controlLayers_withCount_hidden": "Control Layers ({{count}} hidden)",
"rasterLayers_withCount_hidden": "Raster Layers ({{count}} hidden)", "rasterLayers_withCount_hidden": "Raster Layers ({{count}} hidden)",
"ipAdapters_withCount_hidden": "IP Adapters ({{count}} hidden)", "ipAdapters_withCount_hidden": "IP Adapters ({{count}} hidden)",
"inpaintMasks_withCount_hidden": "Inpaint Masks ({{count}} hidden)", "inpaintMasks_withCount_hidden": "Inpaint Masks ({{count}} hidden)",
"regionalGuidance_withCount_visible": "Regional Guidance ({{count}})", "regionalGuidance_withCount_visible": "Regional Guidance ({{count}})",
"controlAdapters_withCount_visible": "Control Adapters ({{count}})",
"controlLayers_withCount_visible": "Control Layers ({{count}})", "controlLayers_withCount_visible": "Control Layers ({{count}})",
"rasterLayers_withCount_visible": "Raster Layers ({{count}})", "rasterLayers_withCount_visible": "Raster Layers ({{count}})",
"ipAdapters_withCount_visible": "IP Adapters ({{count}})", "ipAdapters_withCount_visible": "IP Adapters ({{count}})",

View File

@ -34,19 +34,19 @@ export const CanvasAddEntityButtons = memo(() => {
<Flex flexDir="column" w="full" h="full" alignItems="center" justifyContent="center"> <Flex flexDir="column" w="full" h="full" alignItems="center" justifyContent="center">
<ButtonGroup orientation="vertical" isAttached={false}> <ButtonGroup orientation="vertical" isAttached={false}>
<Button variant="ghost" justifyContent="flex-start" leftIcon={<PiPlusBold />} onClick={addInpaintMask}> <Button variant="ghost" justifyContent="flex-start" leftIcon={<PiPlusBold />} onClick={addInpaintMask}>
{t('controlLayers.inpaintMask', { count: 1 })} {t('controlLayers.inpaintMask')}
</Button> </Button>
<Button variant="ghost" justifyContent="flex-start" leftIcon={<PiPlusBold />} onClick={addRegionalGuidance}> <Button variant="ghost" justifyContent="flex-start" leftIcon={<PiPlusBold />} onClick={addRegionalGuidance}>
{t('controlLayers.regionalGuidance', { count: 1 })} {t('controlLayers.regionalGuidance')}
</Button> </Button>
<Button variant="ghost" justifyContent="flex-start" leftIcon={<PiPlusBold />} onClick={addRasterLayer}> <Button variant="ghost" justifyContent="flex-start" leftIcon={<PiPlusBold />} onClick={addRasterLayer}>
{t('controlLayers.rasterLayer', { count: 1 })} {t('controlLayers.rasterLayer')}
</Button> </Button>
<Button variant="ghost" justifyContent="flex-start" leftIcon={<PiPlusBold />} onClick={addControlLayer}> <Button variant="ghost" justifyContent="flex-start" leftIcon={<PiPlusBold />} onClick={addControlLayer}>
{t('controlLayers.controlLayer', { count: 1 })} {t('controlLayers.controlLayer')}
</Button> </Button>
<Button variant="ghost" justifyContent="flex-start" leftIcon={<PiPlusBold />} onClick={addIPAdapter}> <Button variant="ghost" justifyContent="flex-start" leftIcon={<PiPlusBold />} onClick={addIPAdapter}>
{t('controlLayers.ipAdapter', { count: 1 })} {t('controlLayers.ipAdapter')}
</Button> </Button>
</ButtonGroup> </ButtonGroup>
</Flex> </Flex>

View File

@ -33,19 +33,19 @@ export const CanvasEntityListMenuItems = memo(() => {
return ( return (
<> <>
<MenuItem icon={<PiPlusBold />} onClick={addInpaintMask}> <MenuItem icon={<PiPlusBold />} onClick={addInpaintMask}>
{t('controlLayers.inpaintMask', { count: 1 })} {t('controlLayers.inpaintMask')}
</MenuItem> </MenuItem>
<MenuItem icon={<PiPlusBold />} onClick={addRegionalGuidance}> <MenuItem icon={<PiPlusBold />} onClick={addRegionalGuidance}>
{t('controlLayers.regionalGuidance', { count: 1 })} {t('controlLayers.regionalGuidance')}
</MenuItem> </MenuItem>
<MenuItem icon={<PiPlusBold />} onClick={addRasterLayer}> <MenuItem icon={<PiPlusBold />} onClick={addRasterLayer}>
{t('controlLayers.rasterLayer', { count: 1 })} {t('controlLayers.rasterLayer')}
</MenuItem> </MenuItem>
<MenuItem icon={<PiPlusBold />} onClick={addControlLayer}> <MenuItem icon={<PiPlusBold />} onClick={addControlLayer}>
{t('controlLayers.controlLayer', { count: 1 })} {t('controlLayers.controlLayer')}
</MenuItem> </MenuItem>
<MenuItem icon={<PiPlusBold />} onClick={addIPAdapter}> <MenuItem icon={<PiPlusBold />} onClick={addIPAdapter}>
{t('controlLayers.ipAdapter', { count: 1 })} {t('controlLayers.ipAdapter')}
</MenuItem> </MenuItem>
</> </>
); );

View File

@ -0,0 +1,70 @@
import { IconButton } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks';
import {
controlLayerAdded,
inpaintMaskAdded,
ipaAdded,
rasterLayerAdded,
rgAdded,
} from 'features/controlLayers/store/canvasSlice';
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiPlusBold } from 'react-icons/pi';
type Props = {
type: CanvasEntityIdentifier['type'];
};
export const CanvasEntityAddOfTypeButton = memo(({ type }: Props) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const onClick = useCallback(() => {
switch (type) {
case 'inpaint_mask':
dispatch(inpaintMaskAdded({ isSelected: true }));
break;
case 'regional_guidance':
dispatch(rgAdded({ isSelected: true }));
break;
case 'raster_layer':
dispatch(rasterLayerAdded({ isSelected: true }));
break;
case 'control_layer':
dispatch(controlLayerAdded({ isSelected: true }));
break;
case 'ip_adapter':
dispatch(ipaAdded({ isSelected: true }));
break;
}
}, [dispatch, type]);
const label = useMemo(() => {
switch (type) {
case 'inpaint_mask':
return t('controlLayers.addInpaintMask');
case 'regional_guidance':
return t('controlLayers.addRegionalGuidance');
case 'raster_layer':
return t('controlLayers.addRasterLayer');
case 'control_layer':
return t('controlLayers.addControlLayer');
case 'ip_adapter':
return t('controlLayers.addIPAdapter');
}
}, [type, t]);
return (
<IconButton
size="sm"
aria-label={label}
tooltip={label}
variant="link"
icon={<PiPlusBold />}
onClick={onClick}
alignSelf="stretch"
/>
);
});
CanvasEntityAddOfTypeButton.displayName = 'CanvasEntityAddOfTypeButton';

View File

@ -1,6 +1,7 @@
import type { SystemStyleObject } from '@invoke-ai/ui-library'; import type { SystemStyleObject } from '@invoke-ai/ui-library';
import { Button, Collapse, Flex, Icon, Spacer, Text } from '@invoke-ai/ui-library'; import { Button, Collapse, Flex, Icon, Spacer, Text } from '@invoke-ai/ui-library';
import { useBoolean } from 'common/hooks/useBoolean'; import { useBoolean } from 'common/hooks/useBoolean';
import { CanvasEntityAddOfTypeButton } from 'features/controlLayers/components/common/CanvasEntityAddOfTypeButton';
import { CanvasEntityTypeIsHiddenToggle } from 'features/controlLayers/components/common/CanvasEntityTypeIsHiddenToggle'; import { CanvasEntityTypeIsHiddenToggle } from 'features/controlLayers/components/common/CanvasEntityTypeIsHiddenToggle';
import { useEntityTypeTitle } from 'features/controlLayers/hooks/useEntityTypeTitle'; import { useEntityTypeTitle } from 'features/controlLayers/hooks/useEntityTypeTitle';
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types'; import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
@ -53,6 +54,7 @@ export const CanvasEntityGroupList = memo(({ isSelected, type, children }: Props
</Text> </Text>
<Spacer /> <Spacer />
</Flex> </Flex>
<CanvasEntityAddOfTypeButton type={type} />
{type !== 'ip_adapter' && <CanvasEntityTypeIsHiddenToggle type={type} />} {type !== 'ip_adapter' && <CanvasEntityTypeIsHiddenToggle type={type} />}
</Flex> </Flex>
<Collapse in={collapse.isTrue}> <Collapse in={collapse.isTrue}>

View File

@ -29,15 +29,15 @@ export const useEntityTitle = (entityIdentifier: CanvasEntityIdentifier) => {
const parts: string[] = []; const parts: string[] = [];
if (entityIdentifier.type === 'inpaint_mask') { if (entityIdentifier.type === 'inpaint_mask') {
parts.push(t('controlLayers.inpaintMask', { count: 1 })); parts.push(t('controlLayers.inpaintMask'));
} else if (entityIdentifier.type === 'control_layer') { } else if (entityIdentifier.type === 'control_layer') {
parts.push(t('controlLayers.controlLayer', { count: 1 })); parts.push(t('controlLayers.controlLayer'));
} else if (entityIdentifier.type === 'raster_layer') { } else if (entityIdentifier.type === 'raster_layer') {
parts.push(t('controlLayers.rasterLayer', { count: 1 })); parts.push(t('controlLayers.rasterLayer'));
} else if (entityIdentifier.type === 'ip_adapter') { } else if (entityIdentifier.type === 'ip_adapter') {
parts.push(t('common.ipAdapter', { count: 1 })); parts.push(t('common.ipAdapter'));
} else if (entityIdentifier.type === 'regional_guidance') { } else if (entityIdentifier.type === 'regional_guidance') {
parts.push(t('controlLayers.regionalGuidance', { count: 1 })); parts.push(t('controlLayers.regionalGuidance'));
} else { } else {
assert(false, 'Unexpected entity type'); assert(false, 'Unexpected entity type');
} }

View File

@ -8,15 +8,15 @@ export const useEntityTypeString = (type: CanvasEntityIdentifier['type']): strin
const typeString = useMemo(() => { const typeString = useMemo(() => {
switch (type) { switch (type) {
case 'control_layer': case 'control_layer':
return t('controlLayers.controlLayer', { count: 0 }); return t('controlLayers.controlLayer');
case 'raster_layer': case 'raster_layer':
return t('controlLayers.rasterLayer', { count: 0 }); return t('controlLayers.rasterLayer');
case 'inpaint_mask': case 'inpaint_mask':
return t('controlLayers.inpaintMask', { count: 0 }); return t('controlLayers.inpaintMask');
case 'regional_guidance': case 'regional_guidance':
return t('controlLayers.regionalGuidance', { count: 0 }); return t('controlLayers.regionalGuidance');
case 'ip_adapter': case 'ip_adapter':
return t('controlLayers.ipAdapter', { count: 0 }); return t('controlLayers.ipAdapter');
default: default:
return ''; return '';
} }