mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): lock down bbox while staging
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import { bboxOptimalDimensionChanged, bboxSyncedToOptimalDimension } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { loraDeleted } from 'features/controlLayers/store/lorasSlice';
|
||||
import { modelChanged, vaeSelected } from 'features/controlLayers/store/paramsSlice';
|
||||
import { modelSelected } from 'features/parameters/store/actions';
|
||||
import { zParameterModel } from 'features/parameters/types/parameterSchemas';
|
||||
import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { t } from 'i18next';
|
||||
|
||||
@@ -68,6 +71,11 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
|
||||
}
|
||||
|
||||
dispatch(modelChanged({ model: newModel, previousModel: state.params.model }));
|
||||
// When staging, we don't want to change the bbox, but we must keep the optimal dimension in sync.
|
||||
dispatch(bboxOptimalDimensionChanged({ optimalDimension: getOptimalDimension(newModel) }));
|
||||
if (!selectIsStaging(state)) {
|
||||
dispatch(bboxSyncedToOptimalDimension());
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3,12 +3,13 @@ import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'
|
||||
import type { AppDispatch, RootState } from 'app/store/store';
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import {
|
||||
bboxHeightChanged,
|
||||
bboxWidthChanged,
|
||||
bboxOptimalDimensionChanged,
|
||||
bboxSyncedToOptimalDimension,
|
||||
controlLayerModelChanged,
|
||||
referenceImageIPAdapterModelChanged,
|
||||
rgIPAdapterModelChanged,
|
||||
} from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { loraDeleted } from 'features/controlLayers/store/lorasSlice';
|
||||
import {
|
||||
clipEmbedModelSelected,
|
||||
@@ -20,10 +21,9 @@ import {
|
||||
} from 'features/controlLayers/store/paramsSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { getEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { calculateNewSize } from 'features/parameters/components/Bbox/calculateNewSize';
|
||||
import { postProcessingModelChanged, upscaleModelChanged } from 'features/parameters/store/upscaleSlice';
|
||||
import { zParameterModel, zParameterVAEModel } from 'features/parameters/types/parameterSchemas';
|
||||
import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
|
||||
import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
|
||||
import type { Logger } from 'roarr';
|
||||
import { modelConfigsAdapterSelectors, modelsApi } from 'services/api/endpoints/models';
|
||||
import type { AnyModelConfig } from 'services/api/types';
|
||||
@@ -95,15 +95,11 @@ const handleMainModels: ModelHandler = (models, state, dispatch, log) => {
|
||||
const result = zParameterModel.safeParse(defaultModelInList);
|
||||
if (result.success) {
|
||||
dispatch(modelChanged({ model: defaultModelInList, previousModel: currentModel }));
|
||||
const { bbox } = selectCanvasSlice(state);
|
||||
const optimalDimension = getOptimalDimension(defaultModelInList);
|
||||
if (getIsSizeOptimal(bbox.rect.width, bbox.rect.height, optimalDimension)) {
|
||||
return;
|
||||
// When staging, we don't want to change the bbox, but we must keep the optimal dimension in sync.
|
||||
dispatch(bboxOptimalDimensionChanged({ optimalDimension: getOptimalDimension(defaultModelInList) }));
|
||||
if (!selectIsStaging(state)) {
|
||||
dispatch(bboxSyncedToOptimalDimension());
|
||||
}
|
||||
const { width, height } = calculateNewSize(bbox.aspectRatio.value, optimalDimension * optimalDimension);
|
||||
|
||||
dispatch(bboxWidthChanged({ width }));
|
||||
dispatch(bboxHeightChanged({ height }));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -116,6 +112,11 @@ const handleMainModels: ModelHandler = (models, state, dispatch, log) => {
|
||||
}
|
||||
|
||||
dispatch(modelChanged({ model: result.data, previousModel: currentModel }));
|
||||
// When staging, we don't want to change the bbox, but we must keep the optimal dimension in sync.
|
||||
dispatch(bboxOptimalDimensionChanged({ optimalDimension: getOptimalDimension(result.data) }));
|
||||
if (!selectIsStaging(state)) {
|
||||
dispatch(bboxSyncedToOptimalDimension());
|
||||
}
|
||||
};
|
||||
|
||||
const handleRefinerModels: ModelHandler = (models, state, dispatch, _log) => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import { bboxHeightChanged, bboxWidthChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import {
|
||||
setCfgRescaleMultiplier,
|
||||
setCfgScale,
|
||||
@@ -96,13 +97,15 @@ export const addSetDefaultSettingsListener = (startAppListening: AppStartListeni
|
||||
}
|
||||
}
|
||||
const setSizeOptions = { updateAspectRatio: true, clamp: true };
|
||||
if (width) {
|
||||
|
||||
const isStaging = selectIsStaging(getState());
|
||||
if (!isStaging && width) {
|
||||
if (isParameterWidth(width)) {
|
||||
dispatch(bboxWidthChanged({ width, ...setSizeOptions }));
|
||||
}
|
||||
}
|
||||
|
||||
if (height) {
|
||||
if (!isStaging && height) {
|
||||
if (isParameterHeight(height)) {
|
||||
dispatch(bboxHeightChanged({ height, ...setSizeOptions }));
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import IAIDndImage from 'common/components/IAIDndImage';
|
||||
import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
|
||||
import { useNanoid } from 'common/hooks/useNanoid';
|
||||
import { bboxHeightChanged, bboxWidthChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { selectOptimalDimension } from 'features/controlLayers/store/selectors';
|
||||
import type { ImageWithDims } from 'features/controlLayers/store/types';
|
||||
import type { ImageDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||
@@ -27,6 +28,7 @@ type Props = {
|
||||
export const IPAdapterImagePreview = memo(({ image, onChangeImage, droppableData, postUploadAction }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isStaging = useAppSelector(selectIsStaging);
|
||||
const isConnected = useStore($isConnected);
|
||||
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||
const shift = useShiftModifier();
|
||||
@@ -95,6 +97,7 @@ export const IPAdapterImagePreview = memo(({ image, onChangeImage, droppableData
|
||||
onClick={handleSetControlImageToDimensions}
|
||||
icon={<PiRulerBold size={16} />}
|
||||
tooltip={shift ? t('controlLayers.useSizeIgnoreModel') : t('controlLayers.useSizeOptimizeForModel')}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
@@ -6,7 +6,6 @@ import { deepClone } from 'common/util/deepClone';
|
||||
import { roundDownToMultiple, roundToMultiple } from 'common/util/roundDownToMultiple';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { canvasReset } from 'features/controlLayers/store/actions';
|
||||
import { modelChanged } from 'features/controlLayers/store/paramsSlice';
|
||||
import {
|
||||
selectAllEntities,
|
||||
selectAllEntitiesOfType,
|
||||
@@ -25,7 +24,7 @@ import { simplifyFlatNumbersArray } from 'features/controlLayers/util/simplify';
|
||||
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||
import { calculateNewSize } from 'features/parameters/components/Bbox/calculateNewSize';
|
||||
import { ASPECT_RATIO_MAP } from 'features/parameters/components/Bbox/constants';
|
||||
import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
|
||||
import { getIsSizeOptimal } from 'features/parameters/util/optimalDimension';
|
||||
import type { IRect } from 'konva/lib/types';
|
||||
import { merge, omit } from 'lodash-es';
|
||||
import type { UndoableOptions } from 'redux-undo';
|
||||
@@ -749,6 +748,22 @@ export const canvasSlice = createSlice({
|
||||
|
||||
syncScaledSize(state);
|
||||
},
|
||||
bboxOptimalDimensionChanged: (state, action: PayloadAction<{ optimalDimension: number }>) => {
|
||||
// When staging, we don't want to change the bbox, but we must keep the optimal dimension in sync.
|
||||
// This action does the syncing. `bboxSyncedToOptimalDimension` below will actually change the bbox,
|
||||
// and is only called when we are not staging.
|
||||
const { optimalDimension } = action.payload;
|
||||
state.bbox.optimalDimension = optimalDimension;
|
||||
},
|
||||
bboxSyncedToOptimalDimension: (state) => {
|
||||
const { optimalDimension } = state.bbox;
|
||||
if (!getIsSizeOptimal(state.bbox.rect.width, state.bbox.rect.height, optimalDimension)) {
|
||||
const bboxDims = calculateNewSize(state.bbox.aspectRatio.value, optimalDimension * optimalDimension);
|
||||
state.bbox.rect.width = bboxDims.width;
|
||||
state.bbox.rect.height = bboxDims.height;
|
||||
syncScaledSize(state);
|
||||
}
|
||||
},
|
||||
//#region Shared entity
|
||||
entitySelected: (state, action: PayloadAction<EntityIdentifierPayload>) => {
|
||||
const { entityIdentifier } = action.payload;
|
||||
@@ -1052,27 +1067,6 @@ export const canvasSlice = createSlice({
|
||||
canvasClearHistory: () => {},
|
||||
},
|
||||
extraReducers(builder) {
|
||||
builder.addCase(modelChanged, (state, action) => {
|
||||
const { model, previousModel } = action.payload;
|
||||
|
||||
// If the model base changes (e.g. SD1.5 -> SDXL), we need to change a few things
|
||||
if (model === null || previousModel?.base === model.base) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the bbox size to match the new model's optimal size
|
||||
const optimalDimension = getOptimalDimension(model);
|
||||
|
||||
state.bbox.optimalDimension = optimalDimension;
|
||||
|
||||
if (!getIsSizeOptimal(state.bbox.rect.width, state.bbox.rect.height, optimalDimension)) {
|
||||
const bboxDims = calculateNewSize(state.bbox.aspectRatio.value, optimalDimension * optimalDimension);
|
||||
state.bbox.rect.width = bboxDims.width;
|
||||
state.bbox.rect.height = bboxDims.height;
|
||||
syncScaledSize(state);
|
||||
}
|
||||
});
|
||||
|
||||
builder.addCase(canvasReset, (state) => {
|
||||
return resetState(state);
|
||||
});
|
||||
@@ -1135,6 +1129,8 @@ export const {
|
||||
bboxAspectRatioIdChanged,
|
||||
bboxDimensionsSwapped,
|
||||
bboxSizeOptimized,
|
||||
bboxOptimalDimensionChanged,
|
||||
bboxSyncedToOptimalDimension,
|
||||
// Raster layers
|
||||
rasterLayerAdded,
|
||||
// rasterLayerRecalled,
|
||||
@@ -1215,6 +1211,11 @@ export const canvasUndoableConfig: UndoableOptions<CanvasState, UnknownAction> =
|
||||
if (!action.type.startsWith(canvasSlice.name)) {
|
||||
return false;
|
||||
}
|
||||
if (bboxOptimalDimensionChanged.match(action)) {
|
||||
// This action is not triggered by the user. it's dispatched when the model is changed and will have no visible
|
||||
// effect on the canvas.
|
||||
return false;
|
||||
}
|
||||
// Throttle rapid actions of the same type
|
||||
filter = actionsThrottlingFilter(action);
|
||||
return filter;
|
||||
|
||||
@@ -4,6 +4,7 @@ import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { adHocPostProcessingRequested } from 'app/store/middleware/listenerMiddleware/listeners/addAdHocPostProcessingRequestedListener';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { INTERACTION_SCOPES } from 'common/hooks/interactionScopes';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { DeleteImageButton } from 'features/deleteImageModal/components/DeleteImageButton';
|
||||
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
|
||||
import SingleSelectionMenuItems from 'features/gallery/components/ImageContextMenu/SingleSelectionMenuItems';
|
||||
@@ -34,6 +35,7 @@ import { $isConnected, $progressImage } from 'services/events/stores';
|
||||
const CurrentImageButtons = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const isConnected = useStore($isConnected);
|
||||
const isStaging = useAppSelector(selectIsStaging);
|
||||
const lastSelectedImage = useAppSelector(selectLastSelectedImage);
|
||||
const progressImage = useStore($progressImage);
|
||||
const shouldDisableToolbarButtons = useMemo(() => {
|
||||
@@ -59,8 +61,11 @@ const CurrentImageButtons = () => {
|
||||
}, [getAndLoadEmbeddedWorkflow, lastSelectedImage]);
|
||||
|
||||
const handleUseSize = useCallback(() => {
|
||||
if (isStaging) {
|
||||
return;
|
||||
}
|
||||
parseAndRecallImageDimensions(lastSelectedImage);
|
||||
}, [lastSelectedImage]);
|
||||
}, [isStaging, lastSelectedImage]);
|
||||
const handleClickUpscale = useCallback(() => {
|
||||
if (!imageDTO) {
|
||||
return;
|
||||
@@ -179,6 +184,7 @@ const CurrentImageButtons = () => {
|
||||
tooltip={`${t('parameters.useSize')} (D)`}
|
||||
aria-label={`${t('parameters.useSize')} (D)`}
|
||||
onClick={handleUseSize}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
<IconButton
|
||||
isLoading={isLoadingMetadata}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { handlers, parseAndRecallAllMetadata, parseAndRecallPrompts } from 'features/metadata/util/handlers';
|
||||
import { $stylePresetModalState } from 'features/stylePresets/store/stylePresetModal';
|
||||
import {
|
||||
@@ -17,6 +18,7 @@ export const useImageActions = (image_name?: string) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const activeStylePresetId = useAppSelector(selectStylePresetActivePresetId);
|
||||
const isStaging = useAppSelector(selectIsStaging);
|
||||
const activeTabName = useAppSelector(selectActiveTab);
|
||||
const { metadata, isLoading: isLoadingMetadata } = useDebouncedMetadata(image_name);
|
||||
const [hasMetadata, setHasMetadata] = useState(false);
|
||||
@@ -66,9 +68,9 @@ export const useImageActions = (image_name?: string) => {
|
||||
}, [dispatch, activeStylePresetId, t]);
|
||||
|
||||
const recallAll = useCallback(() => {
|
||||
parseAndRecallAllMetadata(metadata, activeTabName === 'canvas');
|
||||
parseAndRecallAllMetadata(metadata, activeTabName === 'canvas', isStaging ? ['width', 'height'] : []);
|
||||
clearStylePreset();
|
||||
}, [activeTabName, metadata, clearStylePreset]);
|
||||
}, [metadata, activeTabName, isStaging, clearStylePreset]);
|
||||
|
||||
const remix = useCallback(() => {
|
||||
// Recalls all metadata parameters except seed
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import type { SingleValue } from 'chakra-react-select';
|
||||
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
||||
import { bboxAspectRatioIdChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { selectAspectRatioID } from 'features/controlLayers/store/selectors';
|
||||
import { isAspectRatioID } from 'features/controlLayers/store/types';
|
||||
import { ASPECT_RATIO_OPTIONS } from 'features/parameters/components/Bbox/constants';
|
||||
@@ -14,6 +15,7 @@ export const BboxAspectRatioSelect = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const id = useAppSelector(selectAspectRatioID);
|
||||
const isStaging = useAppSelector(selectIsStaging);
|
||||
|
||||
const onChange = useCallback(
|
||||
(v: SingleValue<ComboboxOption>) => {
|
||||
@@ -28,7 +30,7 @@ export const BboxAspectRatioSelect = memo(() => {
|
||||
const value = useMemo(() => ASPECT_RATIO_OPTIONS.filter((o) => o.value === id)[0], [id]);
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
<FormControl isDisabled={isStaging}>
|
||||
<InformationalPopover feature="paramAspect">
|
||||
<FormLabel>{t('parameters.aspect')}</FormLabel>
|
||||
</InformationalPopover>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
||||
import { bboxHeightChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { selectHeight, selectOptimalDimension } from 'features/controlLayers/store/selectors';
|
||||
import { selectHeightConfig } from 'features/system/store/configSlice';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
@@ -13,6 +14,7 @@ export const BboxHeight = memo(() => {
|
||||
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||
const height = useAppSelector(selectHeight);
|
||||
const config = useAppSelector(selectHeightConfig);
|
||||
const isStaging = useAppSelector(selectIsStaging);
|
||||
|
||||
const onChange = useCallback(
|
||||
(v: number) => {
|
||||
@@ -27,7 +29,7 @@ export const BboxHeight = memo(() => {
|
||||
);
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
<FormControl isDisabled={isStaging}>
|
||||
<InformationalPopover feature="paramHeight">
|
||||
<FormLabel>{t('parameters.height')}</FormLabel>
|
||||
</InformationalPopover>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { bboxAspectRatioLockToggled } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -13,6 +14,7 @@ export const BboxLockAspectRatioButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isLocked = useAppSelector(selectAspectRatioIsLocked);
|
||||
const isStaging = useAppSelector(selectIsStaging);
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(bboxAspectRatioLockToggled());
|
||||
}, [dispatch]);
|
||||
@@ -25,6 +27,7 @@ export const BboxLockAspectRatioButton = memo(() => {
|
||||
variant={isLocked ? 'outline' : 'ghost'}
|
||||
size="sm"
|
||||
icon={isLocked ? <PiLockSimpleFill /> : <PiLockSimpleOpenBold />}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
||||
import { bboxScaleMethodChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { isBoundingBoxScaleMethod } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
@@ -15,6 +16,7 @@ const BboxScaleMethod = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const scaleMethod = useAppSelector(selectScaleMethod);
|
||||
const isStaging = useAppSelector(selectIsStaging);
|
||||
|
||||
const OPTIONS: ComboboxOption[] = useMemo(
|
||||
() => [
|
||||
@@ -38,7 +40,7 @@ const BboxScaleMethod = () => {
|
||||
const value = useMemo(() => OPTIONS.find((o) => o.value === scaleMethod), [scaleMethod, OPTIONS]);
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
<FormControl isDisabled={isStaging}>
|
||||
<InformationalPopover feature="scaleBeforeProcessing">
|
||||
<FormLabel>{t('parameters.scaleBeforeProcessing')}</FormLabel>
|
||||
</InformationalPopover>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { bboxScaledHeightChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { selectCanvasSlice, selectOptimalDimension } from 'features/controlLayers/store/selectors';
|
||||
import { selectConfigSlice } from 'features/system/store/configSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
@@ -17,6 +18,7 @@ const selectScaledBoundingBoxHeightConfig = createSelector(
|
||||
const BboxScaledHeight = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isStaging = useAppSelector(selectIsStaging);
|
||||
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||
const isManual = useAppSelector(selectIsManual);
|
||||
const scaledHeight = useAppSelector(selectScaledHeight);
|
||||
@@ -30,7 +32,7 @@ const BboxScaledHeight = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<FormControl isDisabled={!isManual}>
|
||||
<FormControl isDisabled={!isManual || isStaging}>
|
||||
<FormLabel>{t('parameters.scaledHeight')}</FormLabel>
|
||||
<CompositeSlider
|
||||
min={config.sliderMin}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { bboxScaledWidthChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { selectCanvasSlice, selectOptimalDimension } from 'features/controlLayers/store/selectors';
|
||||
import { selectConfigSlice } from 'features/system/store/configSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
@@ -17,6 +18,7 @@ const selectScaledBoundingBoxWidthConfig = createSelector(
|
||||
const BboxScaledWidth = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isStaging = useAppSelector(selectIsStaging);
|
||||
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||
const isManual = useAppSelector(selectIsManual);
|
||||
const scaledWidth = useAppSelector(selectScaledWidth);
|
||||
@@ -29,7 +31,7 @@ const BboxScaledWidth = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<FormControl isDisabled={!isManual}>
|
||||
<FormControl isDisabled={!isManual || isStaging}>
|
||||
<FormLabel>{t('parameters.scaledWidth')}</FormLabel>
|
||||
<CompositeSlider
|
||||
min={config.sliderMin}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { bboxSizeOptimized } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { selectCanvasSlice, selectOptimalDimension } from 'features/controlLayers/store/selectors';
|
||||
import { getIsSizeTooLarge, getIsSizeTooSmall } from 'features/parameters/util/optimalDimension';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
@@ -14,6 +15,7 @@ const selectHeight = createSelector(selectCanvasSlice, (canvas) => canvas.bbox.r
|
||||
export const BboxSetOptimalSizeButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isStaging = useAppSelector(selectIsStaging);
|
||||
const width = useAppSelector(selectWidth);
|
||||
const height = useAppSelector(selectHeight);
|
||||
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||
@@ -47,6 +49,7 @@ export const BboxSetOptimalSizeButton = memo(() => {
|
||||
size="sm"
|
||||
icon={<PiSparkleFill />}
|
||||
colorScheme={isSizeTooSmall || isSizeTooLarge ? 'warning' : 'base'}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { bboxDimensionsSwapped } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiArrowsDownUpBold } from 'react-icons/pi';
|
||||
@@ -8,6 +9,7 @@ import { PiArrowsDownUpBold } from 'react-icons/pi';
|
||||
export const BboxSwapDimensionsButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isStaging = useAppSelector(selectIsStaging);
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(bboxDimensionsSwapped());
|
||||
}, [dispatch]);
|
||||
@@ -19,6 +21,7 @@ export const BboxSwapDimensionsButton = memo(() => {
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
icon={<PiArrowsDownUpBold />}
|
||||
isDisabled={isStaging}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
||||
import { bboxWidthChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { selectOptimalDimension, selectWidth } from 'features/controlLayers/store/selectors';
|
||||
import { selectWidthConfig } from 'features/system/store/configSlice';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
@@ -13,6 +14,7 @@ export const BboxWidth = memo(() => {
|
||||
const width = useAppSelector(selectWidth);
|
||||
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||
const config = useAppSelector(selectWidthConfig);
|
||||
const isStaging = useAppSelector(selectIsStaging);
|
||||
|
||||
const onChange = useCallback(
|
||||
(v: number) => {
|
||||
@@ -27,7 +29,7 @@ export const BboxWidth = memo(() => {
|
||||
);
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
<FormControl isDisabled={isStaging}>
|
||||
<InformationalPopover feature="paramWidth">
|
||||
<FormLabel>{t('parameters.width')}</FormLabel>
|
||||
</InformationalPopover>
|
||||
|
||||
Reference in New Issue
Block a user