Skip to content

Commit da3f85d

Browse files
psychedelicioushipsterusername
authored andcommitted
fix(ui): edge case where entity isn't visible until interacting with canvas
To trigger the edge case: - Have an empty layer and non-empty layer - Select the non-empty layer - Refresh the page - Select to the empty layer without doing any other action - You may be unable to draw on the layer - Zoom in/out slightly - You can now draw on it The problem was not syncing visibility when a layer is selected, leaving the layer hidden. This indirectly disabled interactions. The fix is to listen for changes to the layer's selected status and sync visibility when that changes.
1 parent 7185363 commit da3f85d

File tree

2 files changed

+29
-23
lines changed

2 files changed

+29
-23
lines changed

invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterBase.ts

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
} from 'features/controlLayers/store/canvasSettingsSlice';
1818
import {
1919
buildEntityIsHiddenSelector,
20+
buildSelectIsSelected,
2021
selectBboxRect,
2122
selectCanvasSlice,
2223
selectEntity,
@@ -165,6 +166,7 @@ export abstract class CanvasEntityAdapterBase<
165166
};
166167

167168
selectIsHidden: Selector<RootState, boolean>;
169+
selectIsSelected: Selector<RootState, boolean>;
168170

169171
/**
170172
* The Konva nodes that make up the entity adapter:
@@ -249,6 +251,7 @@ export abstract class CanvasEntityAdapterBase<
249251
this.state = state;
250252

251253
this.selectIsHidden = buildEntityIsHiddenSelector(this.entityIdentifier);
254+
this.selectIsSelected = buildSelectIsSelected(this.entityIdentifier);
252255

253256
/**
254257
* There are a number of reason we may need to show or hide a layer:
@@ -257,6 +260,7 @@ export abstract class CanvasEntityAdapterBase<
257260
* - Staging status changes and `isolatedStagingPreview` is enabled
258261
* - Global filtering status changes and `isolatedFilteringPreview` is enabled
259262
* - Global transforming status changes and `isolatedTransformingPreview` is enabled
263+
* - The entity is selected or deselected (only selected and onscreen entities are rendered)
260264
*/
261265
this.subscriptions.add(this.manager.stateApi.createStoreSubscription(this.selectIsHidden, this.syncVisibility));
262266
this.subscriptions.add(
@@ -267,6 +271,7 @@ export abstract class CanvasEntityAdapterBase<
267271
this.manager.stateApi.createStoreSubscription(selectIsolatedTransformingPreview, this.syncVisibility)
268272
);
269273
this.subscriptions.add(this.manager.stateApi.$transformingAdapter.listen(this.syncVisibility));
274+
this.subscriptions.add(this.manager.stateApi.createStoreSubscription(this.selectIsSelected, this.syncVisibility));
270275

271276
/**
272277
* The tool preview may need to be updated when the entity is locked or disabled. For example, when we disable the
@@ -305,21 +310,8 @@ export abstract class CanvasEntityAdapterBase<
305310

306311
syncIsOnscreen = () => {
307312
const stageRect = this.manager.stage.getScaledStageRect();
308-
const entityRect = this.transformer.$pixelRect.get();
309-
const position = this.manager.stateApi.runSelector(this.selectPosition);
310-
if (!position) {
311-
return;
312-
}
313-
const entityRectRelativeToStage = {
314-
x: entityRect.x + position.x,
315-
y: entityRect.y + position.y,
316-
width: entityRect.width,
317-
height: entityRect.height,
318-
};
319-
320-
const intersection = getRectIntersection(stageRect, entityRectRelativeToStage);
313+
const isOnScreen = this.checkIntersection(stageRect);
321314
const prevIsOnScreen = this.$isOnScreen.get();
322-
const isOnScreen = intersection.width > 0 && intersection.height > 0;
323315
this.$isOnScreen.set(isOnScreen);
324316
if (prevIsOnScreen !== isOnScreen) {
325317
this.log.trace(`Moved ${isOnScreen ? 'on-screen' : 'off-screen'}`);
@@ -329,25 +321,29 @@ export abstract class CanvasEntityAdapterBase<
329321

330322
syncIntersectsBbox = () => {
331323
const bboxRect = this.manager.stateApi.getBbox().rect;
324+
const intersectsBbox = this.checkIntersection(bboxRect);
325+
const prevIntersectsBbox = this.$intersectsBbox.get();
326+
this.$intersectsBbox.set(intersectsBbox);
327+
if (prevIntersectsBbox !== intersectsBbox) {
328+
this.log.trace(`Moved ${intersectsBbox ? 'into bbox' : 'out of bbox'}`);
329+
}
330+
};
331+
332+
checkIntersection = (rect: Rect): boolean => {
332333
const entityRect = this.transformer.$pixelRect.get();
333334
const position = this.manager.stateApi.runSelector(this.selectPosition);
334335
if (!position) {
335-
return;
336+
return false;
336337
}
337338
const entityRectRelativeToStage = {
338339
x: entityRect.x + position.x,
339340
y: entityRect.y + position.y,
340341
width: entityRect.width,
341342
height: entityRect.height,
342343
};
343-
344-
const intersection = getRectIntersection(bboxRect, entityRectRelativeToStage);
345-
const prevIntersectsBbox = this.$intersectsBbox.get();
346-
const intersectsBbox = intersection.width > 0 && intersection.height > 0;
347-
this.$intersectsBbox.set(intersectsBbox);
348-
if (prevIntersectsBbox !== intersectsBbox) {
349-
this.log.trace(`Moved ${intersectsBbox ? 'into bbox' : 'out of bbox'}`);
350-
}
344+
const intersection = getRectIntersection(rect, entityRectRelativeToStage);
345+
const doesIntersect = intersection.width > 0 && intersection.height > 0;
346+
return doesIntersect;
351347
};
352348

353349
initialize = async () => {

invokeai/frontend/web/src/features/controlLayers/store/selectors.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,16 @@ export const buildEntityIsHiddenSelector = (entityIdentifier: CanvasEntityIdenti
339339
);
340340
};
341341

342+
/**
343+
* Builds a selector taht selects if the entity is selected.
344+
*/
345+
export const buildSelectIsSelected = (entityIdentifier: CanvasEntityIdentifier) => {
346+
return createSelector(
347+
selectSelectedEntityIdentifier,
348+
(selectedEntityIdentifier) => selectedEntityIdentifier?.id === entityIdentifier.id
349+
);
350+
};
351+
342352
export const selectWidth = createSelector(selectCanvasSlice, (canvas) => canvas.bbox.rect.width);
343353
export const selectHeight = createSelector(selectCanvasSlice, (canvas) => canvas.bbox.rect.height);
344354
export const selectAspectRatioID = createSelector(selectCanvasSlice, (canvas) => canvas.bbox.aspectRatio.id);

0 commit comments

Comments
 (0)