mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): separate ca layer opacity
This commit is contained in:
parent
e5ec529f0f
commit
048bd18e10
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
@ -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>
|
||||||
|
@ -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;
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user