feat(ui): revise generation mode logic

- Canvas generation mode is replace with a boolean `sendToCanvas` flag. When off, images generated on the canvas go to the gallery. When on, they get added to the staging area.
- When an image result is received, if its destination is the canvas, staging is automatically started.
- Updated queue list to show the destination column.
- Added `IconSwitch` component to represent binary choices, used for the new `sendToCanvas` flag and image viewer toggle.
- Remove the queue actions menu in `QueueControls`. Move the queue count badge to the cancel button.
- Redo layout of `QueueControls` to prevent duplicate queue count badges.
- Fix issue where gallery and options panels could show thru transparent regions of queue tab.
- Disable panel hotkeys when on mm/queue tabs.
This commit is contained in:
psychedelicious
2024-08-29 17:58:02 +10:00
parent 749ff3eb71
commit e8335fe7c4
36 changed files with 486 additions and 234 deletions

View File

@@ -1,4 +1,4 @@
import { Flex } from '@invoke-ai/ui-library';
import { Box, Flex } from '@invoke-ai/ui-library';
import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks';
import { useScopeOnFocus } from 'common/hooks/interactionScopes';
@@ -7,6 +7,7 @@ import GalleryPanelContent from 'features/gallery/components/GalleryPanelContent
import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer';
import { useIsImageViewerOpen } from 'features/gallery/components/ImageViewer/useImageViewer';
import NodeEditorPanelGroup from 'features/nodes/components/sidePanel/NodeEditorPanelGroup';
import QueueControls from 'features/queue/components/QueueControls';
import FloatingGalleryButton from 'features/ui/components/FloatingGalleryButton';
import FloatingParametersPanelButtons from 'features/ui/components/FloatingParametersPanelButtons';
import ParametersPanelTextToImage from 'features/ui/components/ParametersPanels/ParametersPanelTextToImage';
@@ -89,8 +90,14 @@ export const AppContent = memo(() => {
const galleryPanel = usePanel(galleryPanelUsePanelOptions);
useHotkeys('g', galleryPanel.toggle, [galleryPanel.toggle]);
useHotkeys(['t', 'o'], optionsPanel.toggle, [optionsPanel.toggle]);
useHotkeys('g', galleryPanel.toggle, { enabled: shouldShowGalleryPanel }, [
galleryPanel.toggle,
shouldShowGalleryPanel,
]);
useHotkeys(['t', 'o'], optionsPanel.toggle, { enabled: shouldShowOptionsPanel }, [
optionsPanel.toggle,
shouldShowOptionsPanel,
]);
useHotkeys(
'shift+r',
() => {
@@ -133,21 +140,26 @@ export const AppContent = memo(() => {
storage={panelStorage}
>
<Panel order={0} collapsible style={panelStyles} {...optionsPanel.panelProps}>
<TabMountGate tab="generation">
<TabVisibilityGate tab="generation">
<ParametersPanelTextToImage />
</TabVisibilityGate>
</TabMountGate>
<TabMountGate tab="upscaling">
<TabVisibilityGate tab="upscaling">
<ParametersPanelUpscale />
</TabVisibilityGate>
</TabMountGate>
<TabMountGate tab="workflows">
<TabVisibilityGate tab="workflows">
<NodeEditorPanelGroup />
</TabVisibilityGate>
</TabMountGate>
<Flex flexDir="column" w="full" h="full" gap={2}>
<QueueControls />
<Box position="relative" w="full" h="full">
<TabMountGate tab="generation">
<TabVisibilityGate tab="generation">
<ParametersPanelTextToImage />
</TabVisibilityGate>
</TabMountGate>
<TabMountGate tab="upscaling">
<TabVisibilityGate tab="upscaling">
<ParametersPanelUpscale />
</TabVisibilityGate>
</TabMountGate>
<TabMountGate tab="workflows">
<TabVisibilityGate tab="workflows">
<NodeEditorPanelGroup />
</TabVisibilityGate>
</TabMountGate>
</Box>
</Flex>
</Panel>
<ResizeHandle id="options-main-handle" orientation="vertical" {...optionsPanel.resizeHandleProps} />
<Panel id="main-panel" order={1} minSize={20} style={panelStyles}>

View File

@@ -1,13 +1,13 @@
import type { SystemStyleObject } from '@invoke-ai/ui-library';
import { ButtonGroup, Flex, Icon, IconButton, Portal, spinAnimation } from '@invoke-ai/ui-library';
import CancelCurrentQueueItemIconButton from 'features/queue/components/CancelCurrentQueueItemIconButton';
import { ClearAllQueueIconButton } from 'features/queue/components/ClearQueueIconButton';
import { QueueButtonTooltip } from 'features/queue/components/QueueButtonTooltip';
import { useClearQueue } from 'features/queue/hooks/useClearQueue';
import { useQueueBack } from 'features/queue/hooks/useQueueBack';
import type { UsePanelReturn } from 'features/ui/hooks/usePanel';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiCircleNotchBold, PiSlidersHorizontalBold } from 'react-icons/pi';
import { PiCircleNotchBold, PiSlidersHorizontalBold, PiTrashSimpleBold } from 'react-icons/pi';
import { RiSparklingFill } from 'react-icons/ri';
import { useGetQueueStatusQuery } from 'services/api/endpoints/queue';
@@ -23,6 +23,7 @@ type Props = {
const FloatingSidePanelButtons = (props: Props) => {
const { t } = useTranslation();
const { queueBack, isLoading, isDisabled } = useQueueBack();
const clearQueue = useClearQueue();
const { data: queueStatus } = useGetQueueStatusQuery();
const queueButtonIcon = useMemo(() => {
@@ -71,7 +72,17 @@ const FloatingSidePanelButtons = (props: Props) => {
</QueueButtonTooltip>
<CancelCurrentQueueItemIconButton sx={floatingButtonStyles} />
</ButtonGroup>
<ClearAllQueueIconButton sx={floatingButtonStyles} />
<IconButton
isDisabled={clearQueue.isDisabled}
isLoading={clearQueue.isLoading}
aria-label={t('queue.clear')}
tooltip={t('queue.clearTooltip')}
icon={<PiTrashSimpleBold />}
colorScheme="error"
onClick={clearQueue.openDialog}
data-testid={t('queue.clear')}
sx={floatingButtonStyles}
/>
</Flex>
</Portal>
);

View File

@@ -8,7 +8,6 @@ import { selectIsSDXL } from 'features/controlLayers/store/paramsSlice';
import { selectEntityCount } from 'features/controlLayers/store/selectors';
import { isImageViewerOpenChanged } from 'features/gallery/store/gallerySlice';
import { Prompts } from 'features/parameters/components/Prompts/Prompts';
import QueueControls from 'features/queue/components/QueueControls';
import { AdvancedSettingsAccordion } from 'features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion';
import { CompositingSettingsAccordion } from 'features/settingsAccordions/components/CompositingSettingsAccordion/CompositingSettingsAccordion';
import { GenerationSettingsAccordion } from 'features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion';
@@ -64,7 +63,6 @@ const ParametersPanelTextToImage = () => {
return (
<Flex w="full" h="full" flexDir="column" gap={2}>
<QueueControls />
<StylePresetMenuTrigger />
<Flex w="full" h="full" position="relative">
<Box position="absolute" top={0} left={0} right={0} bottom={0} ref={ref}>

View File

@@ -2,7 +2,6 @@ import { Box, Flex } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
import { Prompts } from 'features/parameters/components/Prompts/Prompts';
import QueueControls from 'features/queue/components/QueueControls';
import { AdvancedSettingsAccordion } from 'features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion';
import { GenerationSettingsAccordion } from 'features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion';
import { UpscaleSettingsAccordion } from 'features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleSettingsAccordion';
@@ -23,7 +22,6 @@ const ParametersPanelUpscale = () => {
return (
<Flex w="full" h="full" flexDir="column" gap={2}>
<QueueControls />
<StylePresetMenuTrigger />
<Flex w="full" h="full" position="relative">
<Box position="absolute" top={0} left={0} right={0} bottom={0}>

View File

@@ -3,30 +3,33 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { setActiveTab } from 'features/ui/store/uiSlice';
import type { TabName } from 'features/ui/store/uiTypes';
import { memo, type ReactElement, useCallback } from 'react';
import { forwardRef, memo, type ReactElement, useCallback } from 'react';
export const TabButton = memo(({ tab, icon, label }: { tab: TabName; icon: ReactElement; label: string }) => {
const dispatch = useAppDispatch();
const activeTabName = useAppSelector(selectActiveTab);
const onClick = useCallback(() => {
dispatch(setActiveTab(tab));
}, [dispatch, tab]);
export const TabButton = memo(
forwardRef(({ tab, icon, label }: { tab: TabName; icon: ReactElement; label: string }, ref) => {
const dispatch = useAppDispatch();
const activeTabName = useAppSelector(selectActiveTab);
const onClick = useCallback(() => {
dispatch(setActiveTab(tab));
}, [dispatch, tab]);
return (
<Tooltip label={label} placement="end">
<IconButton
p={0}
onClick={onClick}
icon={icon}
size="md"
fontSize="24px"
variant="appTab"
data-selected={activeTabName === tab}
aria-label={label}
data-testid={label}
/>
</Tooltip>
);
});
return (
<Tooltip label={label} placement="end">
<IconButton
ref={ref}
p={0}
onClick={onClick}
icon={icon}
size="md"
fontSize="24px"
variant="appTab"
data-selected={activeTabName === tab}
aria-label={label}
data-testid={label}
/>
</Tooltip>
);
})
);
TabButton.displayName = 'TabButton';

View File

@@ -5,7 +5,7 @@ import { memo } from 'react';
const ModelManagerTab = () => {
return (
<Flex w="full" h="full" gap="2">
<Flex layerStyle="body" w="full" h="full" gap="2">
<ModelManager />
<ModelPane />
</Flex>

View File

@@ -4,7 +4,7 @@ import { memo } from 'react';
const QueueTab = () => {
return (
<Flex w="full" h="full">
<Flex layerStyle="body" w="full" h="full">
<QueueTabContent />
</Flex>
);