mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
fix(ui): fix layer arrangement
This commit is contained in:
parent
af25d00964
commit
af3e910ad3
@ -15,7 +15,7 @@ import {
|
|||||||
layerTranslated,
|
layerTranslated,
|
||||||
selectRegionalPromptsSlice,
|
selectRegionalPromptsSlice,
|
||||||
} from 'features/regionalPrompts/store/regionalPromptsSlice';
|
} from 'features/regionalPrompts/store/regionalPromptsSlice';
|
||||||
import { debouncedRenderers, renderers } from 'features/regionalPrompts/util/renderers';
|
import { debouncedRenderers, renderers as normalRenderers } from 'features/regionalPrompts/util/renderers';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import type { IRect } from 'konva/lib/types';
|
import type { IRect } from 'konva/lib/types';
|
||||||
import type { MutableRefObject } from 'react';
|
import type { MutableRefObject } from 'react';
|
||||||
@ -52,20 +52,8 @@ const useStageRenderer = (
|
|||||||
const lastMouseDownPos = useStore($lastMouseDownPos);
|
const lastMouseDownPos = useStore($lastMouseDownPos);
|
||||||
const isMouseOver = useStore($isMouseOver);
|
const isMouseOver = useStore($isMouseOver);
|
||||||
const selectedLayerIdColor = useAppSelector(selectSelectedLayerColor);
|
const selectedLayerIdColor = useAppSelector(selectSelectedLayerColor);
|
||||||
|
const layerIds = useMemo(() => state.layers.map((l) => l.id), [state.layers]);
|
||||||
const renderLayers = useMemo(
|
const renderers = useMemo(() => (asPreview ? debouncedRenderers : normalRenderers), [asPreview]);
|
||||||
() => (asPreview ? debouncedRenderers.renderLayers : renderers.renderLayers),
|
|
||||||
[asPreview]
|
|
||||||
);
|
|
||||||
const renderToolPreview = useMemo(
|
|
||||||
() => (asPreview ? debouncedRenderers.renderToolPreview : renderers.renderToolPreview),
|
|
||||||
[asPreview]
|
|
||||||
);
|
|
||||||
const renderBbox = useMemo(() => (asPreview ? debouncedRenderers.renderBbox : renderers.renderBbox), [asPreview]);
|
|
||||||
const renderBackground = useMemo(
|
|
||||||
() => (asPreview ? debouncedRenderers.renderBackground : renderers.renderBackground),
|
|
||||||
[asPreview]
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLayerPosChanged = useCallback(
|
const onLayerPosChanged = useCallback(
|
||||||
(layerId: string, x: number, y: number) => {
|
(layerId: string, x: number, y: number) => {
|
||||||
@ -152,11 +140,12 @@ const useStageRenderer = (
|
|||||||
}, [stageRef, width, height, wrapper]);
|
}, [stageRef, width, height, wrapper]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
log.trace('Rendering brush preview');
|
log.trace('Rendering tool preview');
|
||||||
if (asPreview) {
|
if (asPreview) {
|
||||||
|
// Preview should not display tool
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
renderToolPreview(
|
renderers.renderToolPreview(
|
||||||
stageRef.current,
|
stageRef.current,
|
||||||
tool,
|
tool,
|
||||||
selectedLayerIdColor,
|
selectedLayerIdColor,
|
||||||
@ -176,29 +165,36 @@ const useStageRenderer = (
|
|||||||
lastMouseDownPos,
|
lastMouseDownPos,
|
||||||
isMouseOver,
|
isMouseOver,
|
||||||
state.brushSize,
|
state.brushSize,
|
||||||
renderToolPreview,
|
renderers,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
log.trace('Rendering layers');
|
log.trace('Rendering layers');
|
||||||
renderLayers(stageRef.current, state.layers, state.globalMaskLayerOpacity, tool, onLayerPosChanged);
|
renderers.renderLayers(stageRef.current, state.layers, state.globalMaskLayerOpacity, tool, onLayerPosChanged);
|
||||||
}, [stageRef, state.layers, state.globalMaskLayerOpacity, tool, onLayerPosChanged, renderLayers]);
|
}, [stageRef, state.layers, state.globalMaskLayerOpacity, tool, onLayerPosChanged, renderers]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
log.trace('Rendering bbox');
|
log.trace('Rendering bbox');
|
||||||
if (asPreview) {
|
if (asPreview) {
|
||||||
|
// Preview should not display bboxes
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
renderBbox(stageRef.current, state.layers, state.selectedLayerId, tool, onBboxChanged, onBboxMouseDown);
|
renderers.renderBbox(stageRef.current, state.layers, state.selectedLayerId, tool, onBboxChanged, onBboxMouseDown);
|
||||||
}, [stageRef, asPreview, state.layers, state.selectedLayerId, tool, onBboxChanged, onBboxMouseDown, renderBbox]);
|
}, [stageRef, asPreview, state.layers, state.selectedLayerId, tool, onBboxChanged, onBboxMouseDown, renderers]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
log.trace('Rendering background');
|
log.trace('Rendering background');
|
||||||
if (asPreview) {
|
if (asPreview) {
|
||||||
|
// The preview should not have a background
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
renderBackground(stageRef.current, width, height);
|
renderers.renderBackground(stageRef.current, width, height);
|
||||||
}, [stageRef, asPreview, width, height, renderBackground]);
|
}, [stageRef, asPreview, width, height, renderers]);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
log.trace('Arranging layers');
|
||||||
|
renderers.arrangeLayers(stageRef.current, layerIds);
|
||||||
|
}, [stageRef, layerIds, renderers]);
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -267,9 +267,6 @@ const createVectorMaskLayer = (
|
|||||||
|
|
||||||
stage.add(konvaLayer);
|
stage.add(konvaLayer);
|
||||||
|
|
||||||
// When a layer is added, it ends up on top of the brush preview - we need to move the preview back to the top.
|
|
||||||
stage.findOne<Konva.Layer>(`#${TOOL_PREVIEW_LAYER_ID}`)?.moveToTop();
|
|
||||||
|
|
||||||
return konvaLayer;
|
return konvaLayer;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -326,7 +323,6 @@ const createVectorMaskRect = (reduxObject: VectorMaskRect, konvaGroup: Konva.Gro
|
|||||||
const renderVectorMaskLayer = (
|
const renderVectorMaskLayer = (
|
||||||
stage: Konva.Stage,
|
stage: Konva.Stage,
|
||||||
reduxLayer: VectorMaskLayer,
|
reduxLayer: VectorMaskLayer,
|
||||||
reduxLayerIndex: number,
|
|
||||||
globalMaskLayerOpacity: number,
|
globalMaskLayerOpacity: number,
|
||||||
tool: Tool,
|
tool: Tool,
|
||||||
onLayerPosChanged?: (layerId: string, x: number, y: number) => void
|
onLayerPosChanged?: (layerId: string, x: number, y: number) => void
|
||||||
@ -339,10 +335,6 @@ const renderVectorMaskLayer = (
|
|||||||
listening: tool === 'move', // The layer only listens when using the move tool - otherwise the stage is handling mouse events
|
listening: tool === 'move', // The layer only listens when using the move tool - otherwise the stage is handling mouse events
|
||||||
x: Math.floor(reduxLayer.x),
|
x: Math.floor(reduxLayer.x),
|
||||||
y: Math.floor(reduxLayer.y),
|
y: Math.floor(reduxLayer.y),
|
||||||
// We have a konva layer for each redux layer, plus a brush preview layer, which should always be on top. We can
|
|
||||||
// therefore use the index of the redux layer as the zIndex for konva layers. If more layers are added to the
|
|
||||||
// stage, this may no longer be work.
|
|
||||||
zIndex: reduxLayerIndex,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Convert the color to a string, stripping the alpha - the object group will handle opacity.
|
// Convert the color to a string, stripping the alpha - the object group will handle opacity.
|
||||||
@ -433,11 +425,9 @@ const renderLayers = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let layerIndex = 0; layerIndex < reduxLayers.length; layerIndex++) {
|
for (const reduxLayer of reduxLayers) {
|
||||||
const reduxLayer = reduxLayers[layerIndex];
|
|
||||||
assert(reduxLayer, `Layer at index ${layerIndex} is undefined`);
|
|
||||||
if (isVectorMaskLayer(reduxLayer)) {
|
if (isVectorMaskLayer(reduxLayer)) {
|
||||||
renderVectorMaskLayer(stage, reduxLayer, layerIndex, globalMaskLayerOpacity, tool, onLayerPosChanged);
|
renderVectorMaskLayer(stage, reduxLayer, globalMaskLayerOpacity, tool, onLayerPosChanged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -593,11 +583,29 @@ const renderBackground = (stage: Konva.Stage, width: number, height: number) =>
|
|||||||
background.fillPatternOffset(stagePos);
|
background.fillPatternOffset(stagePos);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Arranges all layers in the z-axis by updating their z-indices.
|
||||||
|
* @param stage The konva stage
|
||||||
|
* @param layerIds An array of redux layer ids, in their z-index order
|
||||||
|
*/
|
||||||
|
export const arrangeLayers = (stage: Konva.Stage, layerIds: string[]): void => {
|
||||||
|
let nextZIndex = 0;
|
||||||
|
// Background is the first layer
|
||||||
|
stage.findOne<Konva.Layer>(`#${BACKGROUND_LAYER_ID}`)?.zIndex(nextZIndex++);
|
||||||
|
// Then arrange the redux layers in order
|
||||||
|
for (const layerId of layerIds) {
|
||||||
|
stage.findOne<Konva.Layer>(`#${layerId}`)?.zIndex(nextZIndex++);
|
||||||
|
}
|
||||||
|
// Finally, the tool preview layer is always on top
|
||||||
|
stage.findOne<Konva.Layer>(`#${TOOL_PREVIEW_LAYER_ID}`)?.zIndex(nextZIndex++);
|
||||||
|
};
|
||||||
|
|
||||||
export const renderers = {
|
export const renderers = {
|
||||||
renderToolPreview,
|
renderToolPreview,
|
||||||
renderLayers,
|
renderLayers,
|
||||||
renderBbox,
|
renderBbox,
|
||||||
renderBackground,
|
renderBackground,
|
||||||
|
arrangeLayers,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEBOUNCE_MS = 300;
|
const DEBOUNCE_MS = 300;
|
||||||
@ -607,4 +615,5 @@ export const debouncedRenderers = {
|
|||||||
renderLayers: debounce(renderLayers, DEBOUNCE_MS),
|
renderLayers: debounce(renderLayers, DEBOUNCE_MS),
|
||||||
renderBbox: debounce(renderBbox, DEBOUNCE_MS),
|
renderBbox: debounce(renderBbox, DEBOUNCE_MS),
|
||||||
renderBackground: debounce(renderBackground, DEBOUNCE_MS),
|
renderBackground: debounce(renderBackground, DEBOUNCE_MS),
|
||||||
|
arrangeLayers: debounce(arrangeLayers, DEBOUNCE_MS),
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user