From 1b65884dbe6909690e152cd246b99264ab9ee472 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 9 Sep 2024 10:06:06 +1000 Subject: [PATCH] feat(ui): add selected entity status to HUD --- invokeai/frontend/web/public/locales/en.json | 11 +- .../components/HUD/CanvasHUD.tsx | 35 +++--- .../components/HUD/CanvasHUDItem.tsx | 13 ++- .../HUD/CanvasHUDItemSelectedEntityStatus.tsx | 110 ++++++++++++++++++ 4 files changed, 151 insertions(+), 18 deletions(-) create mode 100644 invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUDItemSelectedEntityStatus.tsx diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index a0025df8c7..ad22a875d7 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -1824,7 +1824,16 @@ "HUD": { "bbox": "Bbox", "scaledBbox": "Scaled Bbox", - "autoSave": "Auto Save" + "autoSave": "Auto Save", + "entityStatus": { + "selectedEntity": "Selected Entity", + "filtering": "Filtering", + "transforming": "Transforming", + "locked": "Locked", + "hidden": "Hidden", + "disabled": "Disabled", + "enabled": "Enabled" + } } }, "upscaling": { diff --git a/invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUD.tsx b/invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUD.tsx index 5e7d45eb7a..2b44833a44 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUD.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUD.tsx @@ -2,26 +2,31 @@ import { Grid } from '@invoke-ai/ui-library'; import { CanvasHUDItemAutoSave } from 'features/controlLayers/components/HUD/CanvasHUDItemAutoSave'; import { CanvasHUDItemBbox } from 'features/controlLayers/components/HUD/CanvasHUDItemBbox'; import { CanvasHUDItemScaledBbox } from 'features/controlLayers/components/HUD/CanvasHUDItemScaledBbox'; +import { CanvasHUDItemSelectedEntityStatus } from 'features/controlLayers/components/HUD/CanvasHUDItemSelectedEntityStatus'; import { CanvasHUDItemSnapToGrid } from 'features/controlLayers/components/HUD/CanvasHUDItemSnapToGrid'; +import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; import { memo } from 'react'; export const CanvasHUD = memo(() => { return ( - - - - - - + + + + + + + + + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUDItem.tsx b/invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUDItem.tsx index 7187da1151..8119eb51cf 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUDItem.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUDItem.tsx @@ -1,14 +1,23 @@ import { GridItem, Text } from '@invoke-ai/ui-library'; +import type { Property } from 'csstype'; import { memo } from 'react'; -export const CanvasHUDItem = memo(({ label, value }: { label: string; value: string | number }) => { +type Props = { + label: string; + value: string | number; + color?: Property.Color; +}; + +export const CanvasHUDItem = memo(({ label, value, color }: Props) => { return ( <> {label}: - {value} + + {value} + ); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUDItemSelectedEntityStatus.tsx b/invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUDItemSelectedEntityStatus.tsx new file mode 100644 index 0000000000..a402c2fe88 --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUDItemSelectedEntityStatus.tsx @@ -0,0 +1,110 @@ +import { useStore } from '@nanostores/react'; +import { createSelector } from '@reduxjs/toolkit'; +import { useAppSelector } from 'app/store/storeHooks'; +import type { Property } from 'csstype'; +import { CanvasHUDItem } from 'features/controlLayers/components/HUD/CanvasHUDItem'; +import { useEntityAdapter } from 'features/controlLayers/hooks/useEntityAdapter'; +import { useEntityTypeIsHidden } from 'features/controlLayers/hooks/useEntityTypeIsHidden'; +import { + selectCanvasSlice, + selectEntityOrThrow, + selectSelectedEntityIdentifier, +} from 'features/controlLayers/store/selectors'; +import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types'; +import { atom } from 'nanostores'; +import { memo, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; + +type ContentProps = { + entityIdentifier: CanvasEntityIdentifier; +}; + +const $isFilteringFallback = atom(false); + +type EntityStatus = { + value: string; + color?: Property.Color; +}; + +const CanvasHUDItemSelectedEntityStatusContent = memo(({ entityIdentifier }: ContentProps) => { + const { t } = useTranslation(); + const adapter = useEntityAdapter(entityIdentifier); + const selectIsEnabled = useMemo( + () => createSelector(selectCanvasSlice, (canvas) => selectEntityOrThrow(canvas, entityIdentifier).isEnabled), + [entityIdentifier] + ); + const selectIsLocked = useMemo( + () => createSelector(selectCanvasSlice, (canvas) => selectEntityOrThrow(canvas, entityIdentifier).isLocked), + [entityIdentifier] + ); + const isEnabled = useAppSelector(selectIsEnabled); + const isLocked = useAppSelector(selectIsLocked); + const isHidden = useEntityTypeIsHidden(entityIdentifier.type); + const isFiltering = useStore(adapter.filterer?.$isFiltering ?? $isFilteringFallback); + const isTransforming = useStore(adapter.transformer.$isTransforming); + + const status = useMemo(() => { + if (isFiltering) { + return { + value: t('controlLayers.HUD.entityStatus.filtering'), + color: 'invokeYellow.300', + }; + } + + if (isTransforming) { + return { + value: t('controlLayers.HUD.entityStatus.transforming'), + color: 'invokeYellow.300', + }; + } + + if (isHidden) { + return { + value: t('controlLayers.HUD.entityStatus.hidden'), + color: 'invokePurple.300', + }; + } + + if (isLocked) { + return { + value: t('controlLayers.HUD.entityStatus.locked'), + color: 'invokeRed.300', + }; + } + + if (!isEnabled) { + return { + value: t('controlLayers.HUD.entityStatus.disabled'), + color: 'invokeRed.300', + }; + } + + return { + value: t('controlLayers.HUD.entityStatus.enabled'), + }; + }, [isFiltering, isTransforming, isHidden, isLocked, isEnabled, t]); + + return ( + <> + + + ); +}); + +CanvasHUDItemSelectedEntityStatusContent.displayName = 'CanvasHUDItemSelectedEntityStatusContent'; + +export const CanvasHUDItemSelectedEntityStatus = memo(() => { + const selectedEntityIdentifier = useAppSelector(selectSelectedEntityIdentifier); + + if (!selectedEntityIdentifier) { + return null; + } + + return ; +}); + +CanvasHUDItemSelectedEntityStatus.displayName = 'CanvasHUDItemSelectedEntityStatus';