mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
switchable foreground/background colors
This commit is contained in:
committed by
psychedelicious
parent
27f4af0eb4
commit
031d25ed63
@@ -589,9 +589,9 @@
|
||||
"title": "Prev Layer",
|
||||
"desc": "Select the previous layer in the list."
|
||||
},
|
||||
"setFillToWhite": {
|
||||
"title": "Set Color to White",
|
||||
"desc": "Set the current tool color to white."
|
||||
"toggleFillColor": {
|
||||
"title": "Toggle Fill Color",
|
||||
"desc": "Toggle the current tool fill color."
|
||||
},
|
||||
"filterSelected": {
|
||||
"title": "Filter",
|
||||
@@ -2268,6 +2268,8 @@
|
||||
},
|
||||
"fill": {
|
||||
"fillColor": "Fill Color",
|
||||
"fillColor1": "Color1",
|
||||
"fillColor2": "Color2",
|
||||
"fillStyle": "Fill Style",
|
||||
"solid": "Solid",
|
||||
"grid": "Grid",
|
||||
|
||||
@@ -12,29 +12,49 @@ import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import RgbaColorPicker from 'common/components/ColorPicker/RgbaColorPicker';
|
||||
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { selectCanvasSettingsSlice, settingsColorChanged } from 'features/controlLayers/store/canvasSettingsSlice';
|
||||
import {
|
||||
selectCanvasSettingsSlice,
|
||||
settingsActiveColorToggled,
|
||||
settingsColor1Changed,
|
||||
settingsColor2Changed,
|
||||
} from 'features/controlLayers/store/canvasSettingsSlice';
|
||||
import type { RgbaColor } from 'features/controlLayers/store/types';
|
||||
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selectColor = createSelector(selectCanvasSettingsSlice, (settings) => settings.color);
|
||||
const selectActiveColor = createSelector(selectCanvasSettingsSlice, (settings) => settings.activeColor);
|
||||
const selectColor1 = createSelector(selectCanvasSettingsSlice, (settings) => settings.color1);
|
||||
const selectColor2 = createSelector(selectCanvasSettingsSlice, (settings) => settings.color2);
|
||||
|
||||
export const ToolColorPicker = memo(() => {
|
||||
export const ToolFillColorPicker = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const fill = useAppSelector(selectColor);
|
||||
const activeColorType = useAppSelector(selectActiveColor);
|
||||
const color1 = useAppSelector(selectColor1);
|
||||
const color2 = useAppSelector(selectColor2);
|
||||
const { activeColor, tooltip, color1zIndex, color2zIndex } = useMemo(() => {
|
||||
if (activeColorType === 'color1') {
|
||||
return { activeColor: color1, tooltip: t('controlLayers.fill.fillColor1'), color1zIndex: 2, color2zIndex: 1 };
|
||||
} else {
|
||||
return { activeColor: color2, tooltip: t('controlLayers.fill.fillColor2'), color1zIndex: 1, color2zIndex: 2 };
|
||||
}
|
||||
}, [activeColorType, color1, color2, t]);
|
||||
const dispatch = useAppDispatch();
|
||||
const onChange = useCallback(
|
||||
const onColorChange = useCallback(
|
||||
(color: RgbaColor) => {
|
||||
dispatch(settingsColorChanged(color));
|
||||
if (activeColorType === 'color1') {
|
||||
dispatch(settingsColor1Changed(color));
|
||||
} else {
|
||||
dispatch(settingsColor2Changed(color));
|
||||
}
|
||||
},
|
||||
[dispatch]
|
||||
[activeColorType, dispatch]
|
||||
);
|
||||
|
||||
useRegisteredHotkeys({
|
||||
id: 'setFillToWhite',
|
||||
id: 'toggleFillColor',
|
||||
category: 'canvas',
|
||||
callback: () => dispatch(settingsColorChanged({ r: 255, g: 255, b: 255, a: 1 })),
|
||||
callback: () => dispatch(settingsActiveColorToggled()),
|
||||
options: { preventDefault: true },
|
||||
dependencies: [dispatch],
|
||||
});
|
||||
@@ -43,15 +63,31 @@ export const ToolColorPicker = memo(() => {
|
||||
<Popover isLazy>
|
||||
<PopoverTrigger>
|
||||
<Flex role="button" aria-label={t('controlLayers.fill.fillColor')} tabIndex={-1} w={8} h={8}>
|
||||
<Tooltip label={t('controlLayers.fill.fillColor')}>
|
||||
<Flex w="full" h="full" alignItems="center" justifyContent="center">
|
||||
<Tooltip label={tooltip}>
|
||||
<Flex alignItems="center" justifyContent="center" position="relative" w="full" h="full">
|
||||
<Box
|
||||
borderRadius="full"
|
||||
borderColor="base.600"
|
||||
w={6}
|
||||
h={6}
|
||||
borderWidth={2}
|
||||
bg={rgbaColorToString(fill)}
|
||||
bg={rgbaColorToString(color1)}
|
||||
position="absolute"
|
||||
top="0"
|
||||
left="0"
|
||||
zIndex={color1zIndex}
|
||||
/>
|
||||
<Box
|
||||
borderRadius="full"
|
||||
borderColor="base.600"
|
||||
w={6}
|
||||
h={6}
|
||||
borderWidth={2}
|
||||
bg={rgbaColorToString(color2)}
|
||||
position="absolute"
|
||||
top="2"
|
||||
left="2"
|
||||
zIndex={color2zIndex}
|
||||
/>
|
||||
</Flex>
|
||||
</Tooltip>
|
||||
@@ -60,11 +96,11 @@ export const ToolColorPicker = memo(() => {
|
||||
<PopoverContent>
|
||||
<PopoverArrow />
|
||||
<PopoverBody minH={64}>
|
||||
<RgbaColorPicker color={fill} onChange={onChange} withNumberInput withSwatches />
|
||||
<RgbaColorPicker color={activeColor} onChange={onColorChange} withNumberInput withSwatches />
|
||||
</PopoverBody>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
});
|
||||
|
||||
ToolColorPicker.displayName = 'ToolFillColorPicker';
|
||||
ToolFillColorPicker.displayName = 'ToolFillColorPicker';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Divider, Flex } from '@invoke-ai/ui-library';
|
||||
import { CanvasSettingsPopover } from 'features/controlLayers/components/Settings/CanvasSettingsPopover';
|
||||
import { ToolColorPicker } from 'features/controlLayers/components/Tool/ToolFillColorPicker';
|
||||
import { ToolFillColorPicker } from 'features/controlLayers/components/Tool/ToolFillColorPicker';
|
||||
import { ToolSettings } from 'features/controlLayers/components/Tool/ToolSettings';
|
||||
import { CanvasToolbarFitBboxToLayersButton } from 'features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToLayersButton';
|
||||
import { CanvasToolbarFitBboxToMasksButton } from 'features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToMasksButton';
|
||||
@@ -36,7 +36,7 @@ export const CanvasToolbar = memo(() => {
|
||||
|
||||
return (
|
||||
<Flex w="full" gap={2} alignItems="center" px={2}>
|
||||
<ToolColorPicker />
|
||||
<ToolFillColorPicker />
|
||||
<ToolSettings />
|
||||
<Flex alignItems="center" h="full" flexGrow={1} justifyContent="flex-end">
|
||||
<CanvasToolbarScale />
|
||||
|
||||
@@ -11,7 +11,8 @@ import { createReduxSubscription, getPrefixedId } from 'features/controlLayers/k
|
||||
import {
|
||||
selectCanvasSettingsSlice,
|
||||
settingsBrushWidthChanged,
|
||||
settingsColorChanged,
|
||||
settingsColor1Changed,
|
||||
settingsColor2Changed,
|
||||
settingsEraserWidthChanged,
|
||||
} from 'features/controlLayers/store/canvasSettingsSlice';
|
||||
import {
|
||||
@@ -232,7 +233,9 @@ export class CanvasStateApiModule extends CanvasModuleBase {
|
||||
* Sets the drawing color, pushing state to redux.
|
||||
*/
|
||||
setColor = (color: Partial<RgbaColor>) => {
|
||||
return this.store.dispatch(settingsColorChanged(color));
|
||||
return this.getSettings().activeColor === 'color1'
|
||||
? this.store.dispatch(settingsColor1Changed(color))
|
||||
: this.store.dispatch(settingsColor2Changed(color));
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -421,7 +424,8 @@ export class CanvasStateApiModule extends CanvasModuleBase {
|
||||
* consistency with conventional black and white mask images, we use black as the color for these entities.
|
||||
*/
|
||||
getCurrentColor = (): RgbaColor => {
|
||||
let color: RgbaColor = this.getSettings().color;
|
||||
let color: RgbaColor =
|
||||
this.getSettings().activeColor === 'color1' ? this.getSettings().color1 : this.getSettings().color2;
|
||||
const selectedEntity = this.getSelectedEntityAdapter();
|
||||
if (selectedEntity) {
|
||||
// These two entity types use a compositing rect for opacity. Their fill is always a solid color.
|
||||
@@ -449,7 +453,7 @@ export class CanvasStateApiModule extends CanvasModuleBase {
|
||||
// selected entity's fill color with 50% opacity.
|
||||
return { ...selectedEntity.state.fill.color, a: 0.5 };
|
||||
} else {
|
||||
return this.getSettings().color;
|
||||
return this.getSettings().activeColor === 'color1' ? this.getSettings().color1 : this.getSettings().color2;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -328,6 +328,7 @@ export class CanvasColorPickerToolModule extends CanvasModuleBase {
|
||||
const colorPickerOuterRadius = this.manager.stage.unscale(this.config.RING_OUTER_RADIUS);
|
||||
const onePixel = this.manager.stage.unscale(1);
|
||||
const twoPixels = this.manager.stage.unscale(2);
|
||||
const color = settings.activeColor === 'color1' ? settings.color1 : settings.color2;
|
||||
|
||||
this.konva.ringCandidateColor.setAttrs({
|
||||
x,
|
||||
@@ -339,7 +340,7 @@ export class CanvasColorPickerToolModule extends CanvasModuleBase {
|
||||
this.konva.ringCurrentColor.setAttrs({
|
||||
x,
|
||||
y,
|
||||
fill: rgbColorToString(settings.color),
|
||||
fill: rgbColorToString(color),
|
||||
innerRadius: colorPickerInnerRadius,
|
||||
outerRadius: colorPickerOuterRadius,
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { PayloadAction, Selector } from '@reduxjs/toolkit';
|
||||
import { createSelector, createSlice } from '@reduxjs/toolkit';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import type { SliceConfig } from 'app/store/types';
|
||||
import type { RgbaColor } from 'features/controlLayers/store/types';
|
||||
import { zRgbaColor } from 'features/controlLayers/store/types';
|
||||
import { z } from 'zod';
|
||||
|
||||
@@ -35,9 +36,11 @@ const zCanvasSettingsState = z.object({
|
||||
*/
|
||||
eraserWidth: z.int().gt(0),
|
||||
/**
|
||||
* The color to use when drawing lines or filling shapes.
|
||||
* The colors to use when drawing lines or filling shapes.
|
||||
*/
|
||||
color: zRgbaColor,
|
||||
activeColor: z.enum(['color1', 'color2']),
|
||||
color1: zRgbaColor,
|
||||
color2: zRgbaColor,
|
||||
/**
|
||||
* Whether to composite inpainted/outpainted regions back onto the source image when saving canvas generations.
|
||||
*
|
||||
@@ -100,7 +103,9 @@ const getInitialState = (): CanvasSettingsState => ({
|
||||
invertScrollForToolWidth: false,
|
||||
brushWidth: 50,
|
||||
eraserWidth: 50,
|
||||
color: { r: 31, g: 160, b: 224, a: 1 }, // invokeBlue.500
|
||||
activeColor: 'color1',
|
||||
color1: { r: 31, g: 160, b: 224, a: 1 }, // invokeBlue.500
|
||||
color2: { r: 0, g: 0, b: 0, a: 1 }, // black
|
||||
outputOnlyMaskedRegions: true,
|
||||
autoProcess: true,
|
||||
snapToGrid: true,
|
||||
@@ -134,8 +139,14 @@ const slice = createSlice({
|
||||
settingsEraserWidthChanged: (state, action: PayloadAction<CanvasSettingsState['eraserWidth']>) => {
|
||||
state.eraserWidth = Math.round(action.payload);
|
||||
},
|
||||
settingsColorChanged: (state, action: PayloadAction<Partial<CanvasSettingsState['color']>>) => {
|
||||
state.color = { ...state.color, ...action.payload };
|
||||
settingsActiveColorToggled: (state) => {
|
||||
state.activeColor = state.activeColor === 'color1' ? 'color2' : 'color1';
|
||||
},
|
||||
settingsColor1Changed: (state, action: PayloadAction<Partial<RgbaColor>>) => {
|
||||
state.color1 = { ...state.color1, ...action.payload };
|
||||
},
|
||||
settingsColor2Changed: (state, action: PayloadAction<Partial<RgbaColor>>) => {
|
||||
state.color2 = { ...state.color2, ...action.payload };
|
||||
},
|
||||
settingsInvertScrollForToolWidthChanged: (
|
||||
state,
|
||||
@@ -191,7 +202,9 @@ export const {
|
||||
settingsShowHUDToggled,
|
||||
settingsBrushWidthChanged,
|
||||
settingsEraserWidthChanged,
|
||||
settingsColorChanged,
|
||||
settingsActiveColorToggled,
|
||||
settingsColor1Changed,
|
||||
settingsColor2Changed,
|
||||
settingsInvertScrollForToolWidthChanged,
|
||||
settingsOutputOnlyMaskedRegionsToggled,
|
||||
settingsAutoProcessToggled,
|
||||
|
||||
@@ -118,7 +118,7 @@ export const useHotkeyData = (): HotkeysData => {
|
||||
addHotkey('canvas', 'selectRectTool', ['u']);
|
||||
addHotkey('canvas', 'selectViewTool', ['h']);
|
||||
addHotkey('canvas', 'selectColorPickerTool', ['i']);
|
||||
addHotkey('canvas', 'setFillToWhite', ['d']);
|
||||
addHotkey('canvas', 'toggleFillColor', ['x']);
|
||||
addHotkey('canvas', 'fitLayersToCanvas', ['mod+0']);
|
||||
addHotkey('canvas', 'fitBboxToCanvas', ['mod+shift+0']);
|
||||
addHotkey('canvas', 'fitBboxToLayers', ['shift+n']);
|
||||
|
||||
Reference in New Issue
Block a user