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';