From 048bd18e107c68304e07a78d7cce1135bf739f52 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 30 Apr 2024 11:57:16 +1000 Subject: [PATCH] feat(ui): separate ca layer opacity --- invokeai/frontend/web/public/locales/en.json | 3 +- .../components/CALayerOpacity.tsx | 81 +++++++++++++++++++ .../ControlAdapterLayerListItem.tsx | 2 + .../regionalPrompts/hooks/layerStateHooks.ts | 20 ++++- 4 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 invokeai/frontend/web/src/features/regionalPrompts/components/CALayerOpacity.tsx diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index d901c4be74..3a6f88e404 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -1536,6 +1536,7 @@ "maskedGuidance": "Masked Guidance", "maskedGuidanceLayer": "$t(regionalPrompts.maskedGuidance) $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" } } diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/CALayerOpacity.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/CALayerOpacity.tsx new file mode 100644 index 0000000000..9d7649be2e --- /dev/null +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/CALayerOpacity.tsx @@ -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 ( + + + } + variant="ghost" + /> + + + + + + + {t('regionalPrompts.opacity')} + + + + + + + + ); +}; + +export default memo(CALayerOpacity); diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/ControlAdapterLayerListItem.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/ControlAdapterLayerListItem.tsx index ef94b4c75a..f9ec37e159 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/ControlAdapterLayerListItem.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/ControlAdapterLayerListItem.tsx @@ -1,6 +1,7 @@ import { Flex, Spacer } from '@invoke-ai/ui-library'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import CALayerOpacity from 'features/regionalPrompts/components/CALayerOpacity'; import ControlAdapterLayerConfig from 'features/regionalPrompts/components/controlAdapterOverrides/ControlAdapterLayerConfig'; import { LayerTitle } from 'features/regionalPrompts/components/LayerTitle'; import { RPLayerDeleteButton } from 'features/regionalPrompts/components/RPLayerDeleteButton'; @@ -52,6 +53,7 @@ export const ControlAdapterLayerListItem = memo(({ layerId }: Props) => { + diff --git a/invokeai/frontend/web/src/features/regionalPrompts/hooks/layerStateHooks.ts b/invokeai/frontend/web/src/features/regionalPrompts/hooks/layerStateHooks.ts index a8fd34d50f..dd0042431c 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/hooks/layerStateHooks.ts +++ b/invokeai/frontend/web/src/features/regionalPrompts/hooks/layerStateHooks.ts @@ -1,6 +1,10 @@ import { createSelector } from '@reduxjs/toolkit'; 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 { assert } from 'tsafe'; @@ -61,3 +65,17 @@ export const useLayerType = (layerId: string) => { const type = useAppSelector(selectLayer); 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; +};