feat(ui): background and staging area modules have own store subscription and render themselves

This commit is contained in:
psychedelicious
2024-09-04 22:54:07 +10:00
parent b201541cb0
commit c3b52a1853
4 changed files with 67 additions and 87 deletions

View File

@@ -28,6 +28,8 @@ export class CanvasBackgroundModule extends CanvasModuleBase {
readonly manager: CanvasManager;
readonly log: Logger;
private _dynamicGrid: boolean | null = null;
subscriptions = new Set<() => void>();
config: CanvasBackgroundModuleConfig = DEFAULT_CONFIG;
@@ -60,15 +62,33 @@ export class CanvasBackgroundModule extends CanvasModuleBase {
* - size
*/
this.subscriptions.add(this.manager.stage.$stageAttrs.listen(this.render));
this.subscriptions.add(this.manager.stateApi.store.subscribe(this.sync));
}
sync = (): boolean => {
this._dynamicGrid = this.manager.stateApi.getSettings().dynamicGrid;
return this._dynamicGrid;
};
get dynamicGrid(): boolean {
if (this._dynamicGrid === null) {
return this.sync();
}
return this._dynamicGrid;
}
set dynamicGrid(dynamicGrid: boolean) {
if (this._dynamicGrid !== dynamicGrid) {
this._dynamicGrid = dynamicGrid;
this.render();
}
}
/**
* Renders the background grid.
*/
render = () => {
const settings = this.manager.stateApi.getSettings();
if (!settings.dynamicGrid) {
if (!this.dynamicGrid) {
this.konva.layer.visible(false);
return;
}

View File

@@ -6,23 +6,20 @@ import { CanvasRasterLayerAdapter } from 'features/controlLayers/konva/CanvasRas
import { CanvasRegionalGuidanceAdapter } from 'features/controlLayers/konva/CanvasRegionalGuidanceAdapter';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import type { CanvasSessionState } from 'features/controlLayers/store/canvasSessionSlice';
import type { CanvasSettingsState } from 'features/controlLayers/store/canvasSettingsSlice';
import { type CanvasState, getEntityIdentifier } from 'features/controlLayers/store/types';
import type { Logger } from 'roarr';
export class CanvasRenderingModule extends CanvasModuleBase {
readonly type = 'canvas_renderer';
export class CanvasEntityRendererModule extends CanvasModuleBase {
readonly type = 'entity_renderer';
readonly id: string;
readonly path: string[];
readonly log: Logger;
readonly parent: CanvasManager;
readonly manager: CanvasManager;
state: CanvasState | null = null;
settings: CanvasSettingsState | null = null;
session: CanvasSessionState | null = null;
private _state: CanvasState | null = null;
isFirstRender = true;
subscriptions = new Set<() => void>();
constructor(manager: CanvasManager) {
super();
@@ -33,29 +30,15 @@ export class CanvasRenderingModule extends CanvasModuleBase {
this.log = this.manager.buildLogger(this);
this.log.debug('Creating module');
this.subscriptions.add(this.manager.stateApi.store.subscribe(this.render));
}
render = () => {
if (!this.state || !this.settings || !this.session) {
this.log.trace('First render');
}
this.renderCanvas();
this.renderSettings();
this.renderSession();
// We have no prev state for the first render
if (this.isFirstRender) {
this.isFirstRender = false;
this.manager.setCanvasManager();
}
};
renderCanvas = () => {
const state = this.manager.stateApi.getCanvasState();
const prevState = this.state;
this.state = state;
const prevState = this._state;
this._state = state;
this.manager.stateApi.$settingsState.set(this.manager.stateApi.getSettings());
this.manager.stateApi.$selectedEntityIdentifier.set(state.selectedEntityIdentifier);
@@ -76,48 +59,6 @@ export class CanvasRenderingModule extends CanvasModuleBase {
this.manager.tool.syncCursorStyle();
};
renderSettings = () => {
const settings = this.manager.stateApi.getSettings();
if (!this.settings) {
this.log.trace('First settings render');
}
const prevSettings = this.settings;
this.settings = settings;
if (prevSettings === settings) {
// No changes to state - no need to render
return;
}
this.renderBackground(settings, prevSettings);
};
renderSession = async () => {
const session = this.manager.stateApi.getSession();
if (!this.session) {
this.log.trace('First session render');
}
const prevSession = this.session;
this.session = session;
if (prevSession === session) {
// No changes to state - no need to render
return;
}
await this.renderStagingArea(session, prevSession);
};
renderBackground = (settings: CanvasSettingsState, prevSettings: CanvasSettingsState | null) => {
if (!prevSettings || settings.dynamicGrid !== prevSettings.dynamicGrid) {
this.manager.background.render();
}
};
renderRasterLayers = async (state: CanvasState, prevState: CanvasState | null) => {
const adapterMap = this.manager.adapters.rasterLayers;
@@ -298,4 +239,10 @@ export class CanvasRenderingModule extends CanvasModuleBase {
this.manager.konva.previewLayer.zIndex(++zIndex);
}
};
destroy = () => {
this.log.trace('Destroying module');
this.subscriptions.forEach((unsubscribe) => unsubscribe());
this.subscriptions.clear();
};
}

View File

@@ -7,13 +7,13 @@ import { CanvasBboxModule } from 'features/controlLayers/konva/CanvasBboxModule'
import { CanvasCacheModule } from 'features/controlLayers/konva/CanvasCacheModule';
import { CanvasCompositorModule } from 'features/controlLayers/konva/CanvasCompositorModule';
import type { CanvasControlLayerAdapter } from 'features/controlLayers/konva/CanvasControlLayerAdapter';
import { CanvasEntityRendererModule } from 'features/controlLayers/konva/CanvasEntityRendererModule';
import { CanvasFilterModule } from 'features/controlLayers/konva/CanvasFilterModule';
import type { CanvasInpaintMaskAdapter } from 'features/controlLayers/konva/CanvasInpaintMaskAdapter';
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
import { CanvasProgressImageModule } from 'features/controlLayers/konva/CanvasProgressImageModule';
import type { CanvasRasterLayerAdapter } from 'features/controlLayers/konva/CanvasRasterLayerAdapter';
import type { CanvasRegionalGuidanceAdapter } from 'features/controlLayers/konva/CanvasRegionalGuidanceAdapter';
import { CanvasRenderingModule } from 'features/controlLayers/konva/CanvasRenderingModule';
import { CanvasStageModule } from 'features/controlLayers/konva/CanvasStageModule';
import { CanvasStagingAreaModule } from 'features/controlLayers/konva/CanvasStagingAreaModule';
import { CanvasToolModule } from 'features/controlLayers/konva/CanvasToolModule';
@@ -40,8 +40,6 @@ export class CanvasManager extends CanvasModuleBase {
store: AppStore;
socket: AppSocket;
subscriptions = new Set<() => void>();
adapters = {
rasterLayers: new SyncableMap<string, CanvasRasterLayerAdapter>(),
controlLayers: new SyncableMap<string, CanvasControlLayerAdapter>(),
@@ -68,7 +66,7 @@ export class CanvasManager extends CanvasModuleBase {
stage: CanvasStageModule;
worker: CanvasWorkerModule;
cache: CanvasCacheModule;
renderer: CanvasRenderingModule;
entityRenderer: CanvasEntityRendererModule;
compositor: CanvasCompositorModule;
tool: CanvasToolModule;
bbox: CanvasBboxModule;
@@ -110,7 +108,7 @@ export class CanvasManager extends CanvasModuleBase {
this.stage = new CanvasStageModule(stage, container, this);
this.worker = new CanvasWorkerModule(this);
this.cache = new CanvasCacheModule(this);
this.renderer = new CanvasRenderingModule(this);
this.entityRenderer = new CanvasEntityRendererModule(this);
this.filter = new CanvasFilterModule(this);
this.compositor = new CanvasCompositorModule(this);
@@ -158,15 +156,13 @@ export class CanvasManager extends CanvasModuleBase {
this.stateApi.$currentFill.set(this.stateApi.getCurrentColor());
this.stateApi.$selectedEntity.set(this.stateApi.getSelectedEntity());
this.subscriptions.add(this.store.subscribe(this.renderer.render));
this.stage.initialize();
$canvasManager.set(this);
};
destroy = () => {
this.log.debug('Destroying module');
this.subscriptions.forEach((unsubscribe) => unsubscribe());
for (const adapter of this.adapters.getAll()) {
adapter.destroy();
}
@@ -181,18 +177,13 @@ export class CanvasManager extends CanvasModuleBase {
this.background.destroy();
this.filter.destroy();
this.worker.destroy();
this.renderer.destroy();
this.entityRenderer.destroy();
this.compositor.destroy();
this.stage.destroy();
$canvasManager.set(null);
};
setCanvasManager = () => {
this.log.debug('Setting canvas manager global');
$canvasManager.set(this);
};
repr = () => {
return {
id: this.id,
@@ -210,7 +201,7 @@ export class CanvasManager extends CanvasModuleBase {
background: this.background.repr(),
filter: this.filter.repr(),
worker: this.worker.repr(),
renderer: this.renderer.repr(),
entityRenderer: this.entityRenderer.repr(),
compositor: this.compositor.repr(),
stage: this.stage.repr(),
};

View File

@@ -2,6 +2,7 @@ import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
import { CanvasObjectImage } from 'features/controlLayers/konva/CanvasObjectImage';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import type { CanvasSessionState } from 'features/controlLayers/store/canvasSessionSlice';
import { imageDTOToImageWithDims, type StagingAreaImage } from 'features/controlLayers/store/types';
import Konva from 'konva';
import { atom } from 'nanostores';
@@ -15,6 +16,8 @@ export class CanvasStagingAreaModule extends CanvasModuleBase {
readonly manager: CanvasManager;
readonly log: Logger;
private _state: CanvasSessionState | null = null;
subscriptions: Set<() => void> = new Set();
konva: { group: Konva.Group };
image: CanvasObjectImage | null;
@@ -37,15 +40,34 @@ export class CanvasStagingAreaModule extends CanvasModuleBase {
this.selectedImage = null;
this.subscriptions.add(this.$shouldShowStagedImage.listen(this.render));
this.subscriptions.add(this.manager.stateApi.store.subscribe(this.sync));
}
sync = (): CanvasSessionState => {
this.state = this.manager.stateApi.getSession();
return this.state;
};
get state(): CanvasSessionState {
if (!this._state) {
return this.manager.stateApi.getSession();
}
return this._state;
}
set state(state: CanvasSessionState) {
if (this._state !== state) {
this._state = state;
this.render();
}
}
render = async () => {
this.log.trace('Rendering staging area');
const session = this.manager.stateApi.getSession();
const { x, y, width, height } = this.manager.stateApi.getBbox().rect;
const shouldShowStagedImage = this.$shouldShowStagedImage.get();
this.selectedImage = session.stagedImages[session.selectedStagedImageIndex] ?? null;
this.selectedImage = this.state.stagedImages[this.state.selectedStagedImageIndex] ?? null;
this.konva.group.position({ x, y });
if (this.selectedImage) {