feat(ui): separate ca layer opacity

This commit is contained in:
psychedelicious 2024-04-30 11:57:16 +10:00 committed by Kent Keirsey
parent e5ec529f0f
commit 048bd18e10
4 changed files with 104 additions and 2 deletions

View File

@ -1536,6 +1536,7 @@
"maskedGuidance": "Masked Guidance", "maskedGuidance": "Masked Guidance",
"maskedGuidanceLayer": "$t(regionalPrompts.maskedGuidance) $t(unifiedCanvas.layer)", "maskedGuidanceLayer": "$t(regionalPrompts.maskedGuidance) $t(unifiedCanvas.layer)",
"controlNetLayer": "$t(common.controlNet) $t(unifiedCanvas.layer)", "controlNetLayer": "$t(common.controlNet) $t(unifiedCanvas.layer)",
"ipAdapterLayer": "$t(common.ipAdapter) $t(unifiedCanvas.layer)" "ipAdapterLayer": "$t(common.ipAdapter) $t(unifiedCanvas.layer)",
"opacity": "Opacity"
} }
} }

View File

@ -0,0 +1,81 @@
import {
CompositeNumberInput,
CompositeSlider,
Flex,
FormControl,
FormLabel,
IconButton,
Popover,
PopoverArrow,
PopoverBody,
PopoverContent,
PopoverTrigger,
} from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks';
import { useLayerOpacity } from 'features/regionalPrompts/hooks/layerStateHooks';
import { layerOpacityChanged } from 'features/regionalPrompts/store/regionalPromptsSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiDropHalfFill } from 'react-icons/pi';
type Props = {
layerId: string;
};
const marks = [0, 25, 50, 75, 100];
const formatPct = (v: number | string) => `${v} %`;
const CALayerOpacity = ({ layerId }: Props) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const opacity = useLayerOpacity(layerId);
const onChange = useCallback(
(v: number) => {
dispatch(layerOpacityChanged({ layerId, opacity: v / 100 }));
},
[dispatch, layerId]
);
return (
<Popover isLazy>
<PopoverTrigger>
<IconButton
aria-label={t('regionalPrompts.opacity')}
size="sm"
icon={<PiDropHalfFill size={16} />}
variant="ghost"
/>
</PopoverTrigger>
<PopoverContent>
<PopoverArrow />
<PopoverBody>
<Flex direction="column" gap={2}>
<FormControl orientation="horizontal" minW={96}>
<FormLabel m={0}>{t('regionalPrompts.opacity')}</FormLabel>
<CompositeSlider
min={0}
max={100}
step={1}
value={opacity}
defaultValue={100}
onChange={onChange}
marks={marks}
/>
<CompositeNumberInput
min={0}
max={100}
step={1}
value={opacity}
defaultValue={100}
onChange={onChange}
minW={24}
format={formatPct}
/>
</FormControl>
</Flex>
</PopoverBody>
</PopoverContent>
</Popover>
);
};
export default memo(CALayerOpacity);

View File

@ -1,6 +1,7 @@
import { Flex, Spacer } from '@invoke-ai/ui-library'; import { Flex, Spacer } from '@invoke-ai/ui-library';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import CALayerOpacity from 'features/regionalPrompts/components/CALayerOpacity';
import ControlAdapterLayerConfig from 'features/regionalPrompts/components/controlAdapterOverrides/ControlAdapterLayerConfig'; import ControlAdapterLayerConfig from 'features/regionalPrompts/components/controlAdapterOverrides/ControlAdapterLayerConfig';
import { LayerTitle } from 'features/regionalPrompts/components/LayerTitle'; import { LayerTitle } from 'features/regionalPrompts/components/LayerTitle';
import { RPLayerDeleteButton } from 'features/regionalPrompts/components/RPLayerDeleteButton'; import { RPLayerDeleteButton } from 'features/regionalPrompts/components/RPLayerDeleteButton';
@ -52,6 +53,7 @@ export const ControlAdapterLayerListItem = memo(({ layerId }: Props) => {
<RPLayerVisibilityToggle layerId={layerId} /> <RPLayerVisibilityToggle layerId={layerId} />
<LayerTitle type="control_adapter_layer" /> <LayerTitle type="control_adapter_layer" />
<Spacer /> <Spacer />
<CALayerOpacity layerId={layerId} />
<RPLayerMenu layerId={layerId} /> <RPLayerMenu layerId={layerId} />
<RPLayerDeleteButton layerId={layerId} /> <RPLayerDeleteButton layerId={layerId} />
</Flex> </Flex>

View File

@ -1,6 +1,10 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { isMaskedGuidanceLayer, selectRegionalPromptsSlice } from 'features/regionalPrompts/store/regionalPromptsSlice'; import {
isControlAdapterLayer,
isMaskedGuidanceLayer,
selectRegionalPromptsSlice,
} from 'features/regionalPrompts/store/regionalPromptsSlice';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { assert } from 'tsafe'; import { assert } from 'tsafe';
@ -61,3 +65,17 @@ export const useLayerType = (layerId: string) => {
const type = useAppSelector(selectLayer); const type = useAppSelector(selectLayer);
return type; return type;
}; };
export const useLayerOpacity = (layerId: string) => {
const selectLayer = useMemo(
() =>
createSelector(selectRegionalPromptsSlice, (regionalPrompts) => {
const layer = regionalPrompts.present.layers.filter(isControlAdapterLayer).find((l) => l.id === layerId);
assert(layer, `Layer ${layerId} not found`);
return Math.round(layer.opacity * 100);
}),
[layerId]
);
const opacity = useAppSelector(selectLayer);
return opacity;
};