Skip to content

Commit b7510ce

Browse files
psychedelicioushipsterusername
authored andcommitted
feat(ui): filter, select object and transform UI buttons
- Restore dedicated `Apply` buttons - Remove icons from the buttons, too much noise when the words are short and clear - Update loading state to show a spinner next to the `Process` button instead of on _every_ button
1 parent 5739799 commit b7510ce

File tree

5 files changed

+44
-52
lines changed

5 files changed

+44
-52
lines changed

invokeai/frontend/web/public/locales/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1894,6 +1894,7 @@
18941894
"include": "Include",
18951895
"exclude": "Exclude",
18961896
"neutral": "Neutral",
1897+
"apply": "Apply",
18971898
"reset": "Reset",
18981899
"saveAs": "Save As",
18991900
"cancel": "Cancel",

invokeai/frontend/web/src/features/controlLayers/components/Filters/Filter.tsx

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
MenuItem,
99
MenuList,
1010
Spacer,
11+
Spinner,
1112
} from '@invoke-ai/ui-library';
1213
import { useStore } from '@nanostores/react';
1314
import { useAppSelector } from 'app/store/storeHooks';
@@ -25,7 +26,7 @@ import { IMAGE_FILTERS } from 'features/controlLayers/store/filters';
2526
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
2627
import { memo, useCallback, useMemo, useRef } from 'react';
2728
import { useTranslation } from 'react-i18next';
28-
import { PiArrowsCounterClockwiseBold, PiFloppyDiskBold, PiPlayFill, PiXBold } from 'react-icons/pi';
29+
import { PiCaretDownBold } from 'react-icons/pi';
2930

3031
const FilterContent = memo(
3132
({ adapter }: { adapter: CanvasEntityAdapterRasterLayer | CanvasEntityAdapterControlLayer }) => {
@@ -115,39 +116,41 @@ const FilterContent = memo(
115116
<ButtonGroup isAttached={false} size="sm" w="full">
116117
<Button
117118
variant="ghost"
118-
leftIcon={<PiPlayFill />}
119119
onClick={adapter.filterer.processImmediate}
120-
isLoading={isProcessing}
121120
loadingText={t('controlLayers.filter.process')}
122-
isDisabled={!isValid || autoProcess}
121+
isDisabled={isProcessing || !isValid || autoProcess}
123122
>
124123
{t('controlLayers.filter.process')}
124+
{isProcessing && <Spinner ms={3} boxSize={5} color="base.600" />}
125125
</Button>
126126
<Spacer />
127127
<Button
128-
leftIcon={<PiArrowsCounterClockwiseBold />}
129128
onClick={adapter.filterer.reset}
130-
isLoading={isProcessing}
129+
isDisabled={isProcessing}
131130
loadingText={t('controlLayers.filter.reset')}
132131
variant="ghost"
133132
>
134133
{t('controlLayers.filter.reset')}
135134
</Button>
135+
<Button
136+
onClick={adapter.filterer.apply}
137+
loadingText={t('controlLayers.filter.apply')}
138+
variant="ghost"
139+
isDisabled={isProcessing || !isValid || !hasProcessed}
140+
>
141+
{t('controlLayers.filter.apply')}
142+
</Button>
136143
<Menu>
137144
<MenuButton
138145
as={Button}
139-
leftIcon={<PiFloppyDiskBold />}
140-
isLoading={isProcessing}
141146
loadingText={t('controlLayers.selectObject.saveAs')}
142147
variant="ghost"
143-
isDisabled={!isValid || !hasProcessed}
148+
isDisabled={isProcessing || !isValid || !hasProcessed}
149+
rightIcon={<PiCaretDownBold />}
144150
>
145151
{t('controlLayers.selectObject.saveAs')}
146152
</MenuButton>
147153
<MenuList>
148-
<MenuItem isDisabled={!isValid || !hasProcessed} onClick={adapter.filterer.apply}>
149-
{t('controlLayers.replaceCurrent')}
150-
</MenuItem>
151154
<MenuItem isDisabled={!isValid || !hasProcessed} onClick={saveAsInpaintMask}>
152155
{t('controlLayers.newInpaintMask')}
153156
</MenuItem>
@@ -162,12 +165,7 @@ const FilterContent = memo(
162165
</MenuItem>
163166
</MenuList>
164167
</Menu>
165-
<Button
166-
variant="ghost"
167-
leftIcon={<PiXBold />}
168-
onClick={adapter.filterer.cancel}
169-
loadingText={t('controlLayers.filter.cancel')}
170-
>
168+
<Button variant="ghost" onClick={adapter.filterer.cancel} loadingText={t('controlLayers.filter.cancel')}>
171169
{t('controlLayers.filter.cancel')}
172170
</Button>
173171
</ButtonGroup>

invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObject.tsx

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
MenuItem,
1111
MenuList,
1212
Spacer,
13+
Spinner,
1314
Text,
1415
Tooltip,
1516
UnorderedList,
@@ -29,7 +30,7 @@ import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/us
2930
import type { PropsWithChildren } from 'react';
3031
import { memo, useCallback, useRef } from 'react';
3132
import { Trans, useTranslation } from 'react-i18next';
32-
import { PiArrowsCounterClockwiseBold, PiFloppyDiskBold, PiInfoBold, PiPlayFill, PiXBold } from 'react-icons/pi';
33+
import { PiCaretDownBold, PiInfoBold } from 'react-icons/pi';
3334

3435
const SelectObjectContent = memo(
3536
({ adapter }: { adapter: CanvasEntityAdapterRasterLayer | CanvasEntityAdapterControlLayer }) => {
@@ -42,10 +43,6 @@ const SelectObjectContent = memo(
4243
const hasImageState = useStore(adapter.segmentAnything.$hasImageState);
4344
const autoProcess = useAppSelector(selectAutoProcess);
4445

45-
const replaceCurrent = useCallback(() => {
46-
adapter.segmentAnything.apply();
47-
}, [adapter.segmentAnything]);
48-
4946
const saveAsInpaintMask = useCallback(() => {
5047
adapter.segmentAnything.saveAs('inpaint_mask');
5148
}, [adapter.segmentAnything]);
@@ -115,58 +112,59 @@ const SelectObjectContent = memo(
115112

116113
<ButtonGroup isAttached={false} size="sm" w="full">
117114
<Button
118-
leftIcon={<PiPlayFill />}
119115
onClick={adapter.segmentAnything.processImmediate}
120-
isLoading={isProcessing}
121116
loadingText={t('controlLayers.selectObject.process')}
122117
variant="ghost"
123-
isDisabled={!hasPoints || autoProcess}
118+
isDisabled={isProcessing || !hasPoints || autoProcess}
124119
>
125120
{t('controlLayers.selectObject.process')}
121+
{isProcessing && <Spinner ms={3} boxSize={5} color="base.600" />}
126122
</Button>
127123
<Spacer />
128124
<Button
129-
leftIcon={<PiArrowsCounterClockwiseBold />}
130125
onClick={adapter.segmentAnything.reset}
131-
isLoading={isProcessing}
126+
isDisabled={isProcessing || !hasPoints}
132127
loadingText={t('controlLayers.selectObject.reset')}
133128
variant="ghost"
134129
>
135130
{t('controlLayers.selectObject.reset')}
136131
</Button>
132+
<Button
133+
onClick={adapter.segmentAnything.apply}
134+
loadingText={t('controlLayers.selectObject.apply')}
135+
variant="ghost"
136+
isDisabled={isProcessing || !hasImageState}
137+
>
138+
{t('controlLayers.selectObject.apply')}
139+
</Button>
137140
<Menu>
138141
<MenuButton
139142
as={Button}
140-
leftIcon={<PiFloppyDiskBold />}
141-
isLoading={isProcessing}
142143
loadingText={t('controlLayers.selectObject.saveAs')}
143144
variant="ghost"
144-
isDisabled={!hasImageState}
145+
isDisabled={isProcessing || !hasImageState}
146+
rightIcon={<PiCaretDownBold />}
145147
>
146148
{t('controlLayers.selectObject.saveAs')}
147149
</MenuButton>
148150
<MenuList>
149-
<MenuItem isDisabled={!hasImageState} onClick={replaceCurrent}>
150-
{t('controlLayers.replaceCurrent')}
151-
</MenuItem>
152-
<MenuItem isDisabled={!hasImageState} onClick={saveAsInpaintMask}>
151+
<MenuItem isDisabled={isProcessing || !hasImageState} onClick={saveAsInpaintMask}>
153152
{t('controlLayers.newInpaintMask')}
154153
</MenuItem>
155-
<MenuItem isDisabled={!hasImageState} onClick={saveAsRegionalGuidance}>
154+
<MenuItem isDisabled={isProcessing || !hasImageState} onClick={saveAsRegionalGuidance}>
156155
{t('controlLayers.newRegionalGuidance')}
157156
</MenuItem>
158-
<MenuItem isDisabled={!hasImageState} onClick={saveAsControlLayer}>
157+
<MenuItem isDisabled={isProcessing || !hasImageState} onClick={saveAsControlLayer}>
159158
{t('controlLayers.newControlLayer')}
160159
</MenuItem>
161-
<MenuItem isDisabled={!hasImageState} onClick={saveAsRasterLayer}>
160+
<MenuItem isDisabled={isProcessing || !hasImageState} onClick={saveAsRasterLayer}>
162161
{t('controlLayers.newRasterLayer')}
163162
</MenuItem>
164163
</MenuList>
165164
</Menu>
166165
<Button
167-
leftIcon={<PiXBold />}
168166
onClick={adapter.segmentAnything.cancel}
169-
isLoading={isProcessing}
167+
isDisabled={isProcessing}
170168
loadingText={t('common.cancel')}
171169
variant="ghost"
172170
>

invokeai/frontend/web/src/features/controlLayers/components/Transform/Transform.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Button, ButtonGroup, Flex, Heading, Spacer } from '@invoke-ai/ui-library';
1+
import { Button, ButtonGroup, Flex, Heading, Spacer, Spinner } from '@invoke-ai/ui-library';
22
import { useStore } from '@nanostores/react';
33
import { useFocusRegion, useIsRegionFocused } from 'common/hooks/focus';
44
import { CanvasOperationIsolatedLayerPreviewSwitch } from 'features/controlLayers/components/CanvasOperationIsolatedLayerPreviewSwitch';
@@ -8,7 +8,6 @@ import type { CanvasEntityAdapter } from 'features/controlLayers/konva/CanvasEnt
88
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
99
import { memo, useRef } from 'react';
1010
import { useTranslation } from 'react-i18next';
11-
import { PiArrowsCounterClockwiseBold, PiCheckBold, PiXBold } from 'react-icons/pi';
1211

1312
const TransformContent = memo(({ adapter }: { adapter: CanvasEntityAdapter }) => {
1413
const { t } = useTranslation();
@@ -62,30 +61,28 @@ const TransformContent = memo(({ adapter }: { adapter: CanvasEntityAdapter }) =>
6261

6362
<TransformFitToBboxButtons adapter={adapter} />
6463

65-
<ButtonGroup isAttached={false} size="sm" w="full">
64+
<ButtonGroup isAttached={false} size="sm" w="full" alignItems="center">
65+
{isProcessing && <Spinner ms={3} boxSize={5} color="base.600" />}
6666
<Spacer />
6767
<Button
68-
leftIcon={<PiArrowsCounterClockwiseBold />}
6968
onClick={adapter.transformer.resetTransform}
70-
isLoading={isProcessing}
69+
isDisabled={isProcessing}
7170
loadingText={t('controlLayers.transform.reset')}
7271
variant="ghost"
7372
>
7473
{t('controlLayers.transform.reset')}
7574
</Button>
7675
<Button
77-
leftIcon={<PiCheckBold />}
7876
onClick={adapter.transformer.applyTransform}
79-
isLoading={isProcessing}
77+
isDisabled={isProcessing}
8078
loadingText={t('controlLayers.transform.apply')}
8179
variant="ghost"
8280
>
8381
{t('controlLayers.transform.apply')}
8482
</Button>
8583
<Button
86-
leftIcon={<PiXBold />}
8784
onClick={adapter.transformer.stopTransform}
88-
isLoading={isProcessing}
85+
isDisabled={isProcessing}
8986
loadingText={t('common.cancel')}
9087
variant="ghost"
9188
>

invokeai/frontend/web/src/features/controlLayers/components/Transform/TransformFitToBboxButtons.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { useStore } from '@nanostores/react';
44
import type { CanvasEntityAdapter } from 'features/controlLayers/konva/CanvasEntity/types';
55
import { memo, useCallback, useMemo, useState } from 'react';
66
import { useTranslation } from 'react-i18next';
7-
import { PiArrowsOutBold } from 'react-icons/pi';
87
import type { Equals } from 'tsafe';
98
import { assert } from 'tsafe';
109
import { z } from 'zod';
@@ -60,10 +59,9 @@ export const TransformFitToBboxButtons = memo(({ adapter }: { adapter: CanvasEnt
6059
<Combobox options={options} value={value} onChange={onChange} isSearchable={false} isClearable={false} />
6160
</FormControl>
6261
<Button
63-
leftIcon={<PiArrowsOutBold />}
6462
size="sm"
6563
onClick={onClick}
66-
isLoading={isProcessing}
64+
isDisabled={isProcessing}
6765
loadingText={t('controlLayers.transform.fitToBbox')}
6866
variant="ghost"
6967
>

0 commit comments

Comments
 (0)