mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): masking UX (wip - interaction state issue)
This commit is contained in:
@@ -553,6 +553,7 @@ export abstract class CanvasEntityAdapterBase<
|
||||
transformer: this.transformer.repr(),
|
||||
renderer: this.renderer.repr(),
|
||||
bufferRenderer: this.bufferRenderer.repr(),
|
||||
segmentAnything: this.segmentAnything?.repr(),
|
||||
filterer: this.filterer?.repr(),
|
||||
hasCache: this.$canvasCache.get() !== null,
|
||||
isLocked: this.$isLocked.get(),
|
||||
|
||||
@@ -591,9 +591,10 @@ export class CanvasEntityTransformer extends CanvasModuleBase {
|
||||
syncInteractionState = () => {
|
||||
this.log.trace('Syncing interaction state');
|
||||
|
||||
if (this.manager.$isBusy.get() && !this.$isTransforming.get()) {
|
||||
// The canvas is busy, we can't interact with the transformer
|
||||
this.parent.konva.layer.listening(false);
|
||||
if (this.parent.segmentAnything?.$isSegmenting.get()) {
|
||||
// When segmenting, the layer should listen but the transformer should not be interactable
|
||||
console.log('segmenting');
|
||||
this.parent.konva.layer.listening(true);
|
||||
this._setInteractionMode('off');
|
||||
return;
|
||||
}
|
||||
@@ -601,6 +602,7 @@ export class CanvasEntityTransformer extends CanvasModuleBase {
|
||||
// Not all entities have a filterer - only raster layer and control layer adapters
|
||||
if (this.parent.filterer?.$isFiltering.get()) {
|
||||
// May not interact with the entity when the filter is active
|
||||
console.log('filtering');
|
||||
this.parent.konva.layer.listening(false);
|
||||
this._setInteractionMode('off');
|
||||
return;
|
||||
@@ -608,6 +610,7 @@ export class CanvasEntityTransformer extends CanvasModuleBase {
|
||||
|
||||
if (this.manager.stateApi.$isTransforming.get() && !this.$isTransforming.get()) {
|
||||
// If another entity is being transformed, we can't interact with this transformer
|
||||
console.log('other entity transforming');
|
||||
this.parent.konva.layer.listening(false);
|
||||
this._setInteractionMode('off');
|
||||
return;
|
||||
@@ -618,6 +621,7 @@ export class CanvasEntityTransformer extends CanvasModuleBase {
|
||||
|
||||
if (isPendingRectCalculation || pixelRect.width === 0 || pixelRect.height === 0) {
|
||||
// If the rect is being calculated, or if the rect has no width or height, we can't interact with the transformer
|
||||
console.log('pending rect calculation');
|
||||
this.parent.konva.layer.listening(false);
|
||||
this._setInteractionMode('off');
|
||||
return;
|
||||
@@ -626,35 +630,49 @@ export class CanvasEntityTransformer extends CanvasModuleBase {
|
||||
const tool = this.manager.tool.$tool.get();
|
||||
const isSelected = this.manager.stateApi.getIsSelected(this.parent.id);
|
||||
|
||||
if (this.parent.$isEmpty.get()) {
|
||||
// The layer is totally empty, we can just disable the layer
|
||||
if (!isSelected) {
|
||||
console.log('not selected');
|
||||
// The layer is not selected
|
||||
this.parent.konva.layer.listening(false);
|
||||
this._setInteractionMode('off');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSelected && !this.$isTransforming.get() && tool === 'move') {
|
||||
if (this.parent.$isEmpty.get()) {
|
||||
// The layer is totally empty, we can just disable the layer
|
||||
console.log('empty');
|
||||
this.parent.konva.layer.listening(false);
|
||||
this._setInteractionMode('off');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.$isTransforming.get() && tool === 'move') {
|
||||
// We are moving this layer, it must be listening
|
||||
console.log('moving');
|
||||
this.parent.konva.layer.listening(true);
|
||||
this._setInteractionMode('drag');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSelected && this.$isTransforming.get()) {
|
||||
if (this.$isTransforming.get()) {
|
||||
console.log('transforming...');
|
||||
// When transforming, we want the stage to still be movable if the view tool is selected. If the transformer is
|
||||
// active, it will interrupt the stage drag events. So we should disable listening when the view tool is selected.
|
||||
if (tool === 'view') {
|
||||
console.log('...with view tool');
|
||||
this.parent.konva.layer.listening(false);
|
||||
this._setInteractionMode('off');
|
||||
} else {
|
||||
console.log('...not with view tool');
|
||||
this.parent.konva.layer.listening(true);
|
||||
this._setInteractionMode('all');
|
||||
}
|
||||
} else {
|
||||
// The layer is not selected, or we are using a tool that doesn't need the layer to be listening - disable interaction stuff
|
||||
this.parent.konva.layer.listening(false);
|
||||
this._setInteractionMode('off');
|
||||
return;
|
||||
}
|
||||
|
||||
// The layer is not selected
|
||||
this.parent.konva.layer.listening(false);
|
||||
this._setInteractionMode('off');
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -113,8 +113,8 @@ export class CanvasManager extends CanvasModuleBase {
|
||||
this.stagingArea.$isStaging,
|
||||
this.compositor.$isBusy,
|
||||
],
|
||||
(isFiltering, isTransforming, isRasterizing, isStaging, isCompositing) => {
|
||||
return isFiltering || isTransforming || isRasterizing || isStaging || isCompositing;
|
||||
(isFiltering, isTransforming, isRasterizing, isSegmenting, isStaging, isCompositing) => {
|
||||
return isFiltering || isTransforming || isRasterizing || isSegmenting || isStaging || isCompositing;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -6,7 +6,13 @@ import type { CanvasEntityAdapterRasterLayer } from 'features/controlLayers/konv
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
import { CanvasObjectImage } from 'features/controlLayers/konva/CanvasObject/CanvasObjectImage';
|
||||
import { floorCoord, getKonvaNodeDebugAttrs, getPrefixedId, offsetCoord } from 'features/controlLayers/konva/util';
|
||||
import {
|
||||
addCoords,
|
||||
floorCoord,
|
||||
getKonvaNodeDebugAttrs,
|
||||
getPrefixedId,
|
||||
offsetCoord,
|
||||
} from 'features/controlLayers/konva/util';
|
||||
import type {
|
||||
CanvasImageState,
|
||||
Coordinate,
|
||||
@@ -227,9 +233,11 @@ export class CanvasSegmentAnythingModule extends CanvasModuleBase {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to offset the cursor position by the parent entity's position + pixel rect to get the correct position
|
||||
const pixelRect = this.parent.transformer.$pixelRect.get();
|
||||
const position = addCoords(this.parent.state.position, pixelRect);
|
||||
|
||||
const normalizedPoint = offsetCoord(cursorPos.relative, { x: pixelRect.x, y: pixelRect.y });
|
||||
const normalizedPoint = offsetCoord(cursorPos.relative, position);
|
||||
const samPoint = this.createPoint(normalizedPoint, this.$pointType.get());
|
||||
this.points.push(samPoint);
|
||||
};
|
||||
@@ -256,15 +264,11 @@ export class CanvasSegmentAnythingModule extends CanvasModuleBase {
|
||||
point.konva.circle.destroy();
|
||||
}
|
||||
this.points = [];
|
||||
this.parent.konva.layer.add(this.konva.group);
|
||||
console.log({
|
||||
position: this.parent.state.position,
|
||||
pixelRect: this.parent.transformer.$pixelRect.get(),
|
||||
nodeRect: this.parent.transformer.$nodeRect.get(),
|
||||
getRelativeRect: this.parent.transformer.getRelativeRect(),
|
||||
});
|
||||
// Update the konva group's position to match the parent entity
|
||||
const pixelRect = this.parent.transformer.$pixelRect.get();
|
||||
this.konva.group.setAttrs({ x: pixelRect.x, y: pixelRect.y });
|
||||
const position = addCoords(this.parent.state.position, pixelRect);
|
||||
this.konva.group.setAttrs(position);
|
||||
this.parent.konva.layer.add(this.konva.group);
|
||||
this.parent.konva.layer.listening(true);
|
||||
|
||||
this.setSegmentingEventListeners();
|
||||
@@ -369,12 +373,12 @@ export class CanvasSegmentAnythingModule extends CanvasModuleBase {
|
||||
}
|
||||
this.konva.compositingRect.visible(false);
|
||||
this.konva.maskGroup.clearCache();
|
||||
this.$pointType.set(1);
|
||||
this.$isSegmenting.set(false);
|
||||
this.$hasProcessed.set(false);
|
||||
this.manager.stateApi.$segmentingAdapter.set(null);
|
||||
this.konva.group.remove();
|
||||
this.parent.konva.layer.listening(false);
|
||||
this.removeSegmentingEventListeners();
|
||||
this.$isSegmenting.set(false);
|
||||
};
|
||||
|
||||
reset = () => {
|
||||
@@ -395,7 +399,6 @@ export class CanvasSegmentAnythingModule extends CanvasModuleBase {
|
||||
this.parent.bufferRenderer.clearBuffer();
|
||||
this.parent.transformer.updatePosition();
|
||||
this.parent.renderer.syncKonvaCache(true);
|
||||
this.parent.konva.layer.listening(false);
|
||||
this.imageState = null;
|
||||
this.$hasProcessed.set(false);
|
||||
};
|
||||
@@ -404,11 +407,12 @@ export class CanvasSegmentAnythingModule extends CanvasModuleBase {
|
||||
this.log.trace('Stopping segment anything');
|
||||
this.reset();
|
||||
this.$isProcessing.set(false);
|
||||
this.$isSegmenting.set(false);
|
||||
this.$hasProcessed.set(false);
|
||||
this.manager.stateApi.$segmentingAdapter.set(null);
|
||||
this.konva.group.remove();
|
||||
this.parent.konva.layer.listening(false);
|
||||
this.removeSegmentingEventListeners();
|
||||
this.$isSegmenting.set(false);
|
||||
};
|
||||
|
||||
getSAMPointColor(label: SAMPointLabel): RgbaColor {
|
||||
@@ -428,12 +432,18 @@ export class CanvasSegmentAnythingModule extends CanvasModuleBase {
|
||||
type: this.type,
|
||||
path: this.path,
|
||||
parent: this.parent.id,
|
||||
points: this.getSAMPoints(),
|
||||
points: this.points.map(({ id, konva, label }) => ({
|
||||
id,
|
||||
label,
|
||||
circle: getKonvaNodeDebugAttrs(konva.circle),
|
||||
})),
|
||||
config: deepClone(this.config),
|
||||
isSegmenting: this.$isSegmenting.get(),
|
||||
konva: {
|
||||
group: getKonvaNodeDebugAttrs(this.konva.group),
|
||||
compositingRect: getKonvaNodeDebugAttrs(this.konva.compositingRect),
|
||||
maskGroup: getKonvaNodeDebugAttrs(this.konva.maskGroup),
|
||||
pointGroup: getKonvaNodeDebugAttrs(this.konva.pointGroup),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -80,6 +80,18 @@ export const offsetCoord = (coord: Coordinate, offset: Coordinate): Coordinate =
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds two coordinates together.
|
||||
* @param a The first coordinate
|
||||
* @param b The second coordinate
|
||||
*/
|
||||
export const addCoords = (a: Coordinate, b: Coordinate): Coordinate => {
|
||||
return {
|
||||
x: a.x + b.x,
|
||||
y: a.y + b.y,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Snaps a position to the edge of the stage if within a threshold of the edge
|
||||
* @param pos The position to snap
|
||||
@@ -616,6 +628,7 @@ export const getKonvaNodeDebugAttrs = (node: Konva.Node) => {
|
||||
isCached: node.isCached(),
|
||||
visible: node.visible(),
|
||||
listening: node.listening(),
|
||||
zIndex: node.zIndex(),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user