mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): hotkeys for brush/eraser size
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user