feat(ui): hotkeys for brush/eraser size

This commit is contained in:
psychedelicious
2024-09-01 16:02:45 +10:00
parent ff0109db52
commit f0332efdf3
4 changed files with 68 additions and 10 deletions

View File

@@ -11,24 +11,48 @@ import {
} from '@invoke-ai/ui-library';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
import { brushWidthChanged, selectToolSlice } from 'features/controlLayers/store/toolSlice';
import { clamp } from 'lodash-es';
import { memo, useCallback } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
const marks = [0, 100, 200, 300];
const marks = [1, 100, 200, 300];
const formatPx = (v: number | string) => `${v} px`;
const selectBrushWidth = createSelector(selectToolSlice, (tool) => tool.brush.width);
export const ToolBrushWidth = memo(() => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const isSelected = useToolIsSelected('brush');
const width = useAppSelector(selectBrushWidth);
const onChange = useCallback(
(v: number) => {
dispatch(brushWidthChanged(Math.round(v)));
dispatch(brushWidthChanged(clamp(Math.round(v), 1, 600)));
},
[dispatch]
);
const increment = useCallback(() => {
let newWidth = Math.round(width * 1.15);
if (newWidth === width) {
newWidth += 1;
}
onChange(newWidth);
}, [onChange, width]);
const decrement = useCallback(() => {
let newWidth = Math.round(width * 0.85);
if (newWidth === width) {
newWidth -= 1;
}
onChange(newWidth);
}, [onChange, width]);
useHotkeys('[', decrement, { enabled: isSelected }, [decrement, isSelected]);
useHotkeys(']', increment, { enabled: isSelected }, [increment, isSelected]);
return (
<FormControl w="min-content" gap={2}>
<FormLabel m={0}>{t('controlLayers.width')}</FormLabel>

View File

@@ -11,24 +11,47 @@ import {
} from '@invoke-ai/ui-library';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
import { eraserWidthChanged, selectToolSlice } from 'features/controlLayers/store/toolSlice';
import { clamp } from 'lodash-es';
import { memo, useCallback } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
const marks = [0, 100, 200, 300];
const marks = [1, 100, 200, 300];
const formatPx = (v: number | string) => `${v} px`;
const selectEraserWidth = createSelector(selectToolSlice, (tool) => tool.eraser.width);
export const ToolEraserWidth = memo(() => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const isSelected = useToolIsSelected('eraser');
const width = useAppSelector(selectEraserWidth);
const onChange = useCallback(
(v: number) => {
dispatch(eraserWidthChanged(Math.round(v)));
dispatch(eraserWidthChanged(clamp(Math.round(v), 1, 600)));
},
[dispatch]
);
const increment = useCallback(() => {
let newWidth = Math.round(width * 1.15);
if (newWidth === width) {
newWidth += 1;
}
onChange(newWidth);
}, [onChange, width]);
const decrement = useCallback(() => {
let newWidth = Math.round(width * 0.85);
if (newWidth === width) {
newWidth -= 1;
}
onChange(newWidth);
}, [onChange, width]);
useHotkeys('[', decrement, { enabled: isSelected }, [decrement, isSelected]);
useHotkeys(']', increment, { enabled: isSelected }, [increment, isSelected]);
return (
<FormControl w="min-content" gap={2}>
<FormLabel m={0}>{t('controlLayers.width')}</FormLabel>

View File

@@ -56,6 +56,11 @@ export class CanvasRenderingModule extends CanvasModuleBase {
const prevState = this.state;
this.state = state;
this.manager.stateApi.$toolState.set(this.manager.stateApi.getToolState());
this.manager.stateApi.$selectedEntityIdentifier.set(state.selectedEntityIdentifier);
this.manager.stateApi.$selectedEntity.set(this.manager.stateApi.getSelectedEntity());
this.manager.stateApi.$currentFill.set(this.manager.stateApi.getCurrentFill());
if (prevState === state) {
// No changes to state - no need to render
return;
@@ -67,11 +72,6 @@ export class CanvasRenderingModule extends CanvasModuleBase {
await this.renderInpaintMasks(state, prevState);
await this.renderBbox(state, prevState);
this.arrangeEntities(state, prevState);
this.manager.stateApi.$toolState.set(this.manager.stateApi.getToolState());
this.manager.stateApi.$selectedEntityIdentifier.set(state.selectedEntityIdentifier);
this.manager.stateApi.$selectedEntity.set(this.manager.stateApi.getSelectedEntity());
this.manager.stateApi.$currentFill.set(this.manager.stateApi.getCurrentFill());
};
renderSettings = () => {

View File

@@ -80,7 +80,18 @@ export class CanvasToolModule extends CanvasModuleBase {
this.konva.group.add(this.colorPickerToolPreview.konva.group);
this.subscriptions.add(this.manager.stateApi.$stageAttrs.listen(this.render));
this.subscriptions.add(this.manager.stateApi.$toolState.listen(this.render));
this.subscriptions.add(
this.manager.stateApi.$toolState.listen((value, oldValue) => {
if (
value !== oldValue ||
value.brush.width !== oldValue.brush.width ||
value.eraser.width !== oldValue.eraser.width ||
value.fill !== oldValue.fill
) {
this.render();
}
})
);
this.subscriptions.add(this.manager.stateApi.$tool.listen(this.render));
const cleanupListeners = this.setEventListeners();