fix: updating connections in the db multiple times (#6859)

* fix: updating connections in the DB recursively

* chore: cleanup
This commit is contained in:
Beka Westberg
2023-03-01 15:00:05 -08:00
committed by GitHub
parent c2df9037f6
commit bb6124a7c3
3 changed files with 118 additions and 16 deletions

View File

@@ -143,6 +143,15 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg,
private translation = '';
/**
* The location of the top left of this block (in workspace coordinates)
* relative to either its parent block, or the workspace origin if it has no
* parent.
*
* @internal
*/
relativeCoords = new Coordinate(0, 0);
/**
* @param workspace The block's workspace.
* @param prototypeName Name of the language object containing type-specific
@@ -382,6 +391,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg,
*/
translate(x: number, y: number) {
this.translation = `translate(${x}, ${y})`;
this.relativeCoords = new Coordinate(x, y);
this.getSvgRoot().setAttribute('transform', this.getTranslation());
}
@@ -1339,7 +1349,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg,
*
* @param all If true, return all connections even hidden ones.
* Otherwise, for a non-rendered block return an empty list, and for a
* collapsed block don't return inputs connections.
* collapsed block don't return inputs connections.
* @returns Array of connections.
* @internal
*/
@@ -1586,6 +1596,43 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg,
}
}
/**
* Renders this block in a way that's compatible with the more efficient
* render management system.
*
* @internal
*/
renderEfficiently() {
this.rendered = true;
dom.startTextWidthCache();
if (this.isCollapsed()) {
this.updateCollapsed_();
}
this.workspace.getRenderer().render(this);
this.tightenChildrenEfficiently();
dom.stopTextWidthCache();
this.updateMarkers_();
}
/**
* Tightens all children of this block so they are snuggly rendered against
* their parent connections.
*
* Does not update connection locations, so that they can be updated more
* efficiently by the render management system.
*
* @internal
*/
tightenChildrenEfficiently() {
for (const input of this.inputList) {
const conn = input.connection as RenderedConnection;
if (conn) conn.tightenEfficiently();
}
if (this.nextConnection) this.nextConnection.tightenEfficiently();
}
/** Redraw any attached marker or cursor svgs if needed. */
protected updateMarkers_() {
if (this.workspace.keyboardAccessibilityMode && this.pathObject.cursorSvg) {

View File

@@ -4,11 +4,12 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {BlockSvg} from './block_svg';
import {BlockSvg} from './block_svg.js';
import {Coordinate} from './utils/coordinate.js';
const rootBlocks = new Set<BlockSvg>();
const dirtyBlocks = new WeakSet<BlockSvg>();
let dirtyBlocks = new WeakSet<BlockSvg>();
let pid = 0;
/**
@@ -43,11 +44,24 @@ function queueBlock(block: BlockSvg) {
* Rerenders all of the blocks in the queue.
*/
function doRenders() {
const workspaces = new Set([...rootBlocks].map((block) => block.workspace));
for (const block of rootBlocks) {
// No need to render a dead block.
if (block.isDisposed()) continue;
// A render for this block may have been queued, and then the block was
// connected to a parent, so it is no longer a root block.
// Rendering will be triggered through the real root block.
if (block.getParent()) continue;
renderBlock(block);
updateConnectionLocations(block, block.getRelativeToSurfaceXY());
}
for (const workspace of workspaces) {
workspace.resizeContents();
}
rootBlocks.clear();
dirtyBlocks = new Set();
pid = 0;
}
@@ -58,14 +72,29 @@ function doRenders() {
* @param block The block to rerender.
*/
function renderBlock(block: BlockSvg) {
if (!dirtyBlocks.has(block)) return;
for (const child of block.getChildren(false)) {
renderBlock(child);
}
if (dirtyBlocks.has(block)) {
dirtyBlocks.delete(block);
rootBlocks.delete(block);
block.render(false);
} else {
block.updateConnectionLocations();
block.renderEfficiently();
}
/**
* Updates the connection database with the new locations of all of the
* connections that are children of the given block.
*
* @param block The block to update the connection locations of.
* @param blockOrigin The top left of the given block in workspace coordinates.
*/
function updateConnectionLocations(block: BlockSvg, blockOrigin: Coordinate) {
for (const conn of block.getConnections_(false)) {
const moved = conn.moveToOffset(blockOrigin);
const target = conn.targetBlock();
if (!conn.isSuperior()) continue;
if (!target) continue;
if (moved || dirtyBlocks.has(target)) {
updateConnectionLocations(
target, Coordinate.sum(blockOrigin, target.relativeCoords));
}
}
}

View File

@@ -183,23 +183,31 @@ export class RenderedConnection extends Connection {
*
* @param x New absolute x coordinate, in workspace coordinates.
* @param y New absolute y coordinate, in workspace coordinates.
* @return True if the position of the connection in the connection db
* was updated.
*/
moveTo(x: number, y: number) {
moveTo(x: number, y: number): boolean {
const dx = this.x - x;
const dy = this.y - y;
let moved = false;
if (this.trackedState_ === RenderedConnection.TrackedState.WILL_TRACK) {
this.db_.addConnection(this, y);
this.trackedState_ = RenderedConnection.TrackedState.TRACKED;
moved = true;
} else if (
this.trackedState_ === RenderedConnection.TrackedState.TRACKED &&
(dx !== 0 || dy !== 0)) {
this.db_.removeConnection(this, this.y);
this.db_.addConnection(this, y);
moved = true;
}
this.x = x;
this.y = y;
return moved;
}
/**
@@ -207,9 +215,11 @@ export class RenderedConnection extends Connection {
*
* @param dx Change to x coordinate, in workspace units.
* @param dy Change to y coordinate, in workspace units.
* @return True if the position of the connection in the connection db
* was updated.
*/
moveBy(dx: number, dy: number) {
this.moveTo(this.x + dx, this.y + dy);
moveBy(dx: number, dy: number): boolean {
return this.moveTo(this.x + dx, this.y + dy);
}
/**
@@ -218,9 +228,11 @@ export class RenderedConnection extends Connection {
*
* @param blockTL The location of the top left corner of the block, in
* workspace coordinates.
* @return True if the position of the connection in the connection db
* was updated.
*/
moveToOffset(blockTL: Coordinate) {
this.moveTo(
moveToOffset(blockTL: Coordinate): boolean {
return this.moveTo(
blockTL.x + this.offsetInBlock_.x, blockTL.y + this.offsetInBlock_.y);
}
@@ -261,12 +273,26 @@ export class RenderedConnection extends Connection {
}
// Workspace coordinates.
const xy = svgMath.getRelativeXY(svgRoot);
block!.getSvgRoot().setAttribute(
'transform', 'translate(' + (xy.x - dx) + ',' + (xy.y - dy) + ')');
block!.translate(xy.x - dx, xy.y - dy);
block!.moveConnections(-dx, -dy);
}
}
/**
* Moves the blocks on either side of this connection right next to
* each other, based on their local offsets, not global positions.
*
* @internal
*/
tightenEfficiently() {
const target = this.targetConnection;
const block = this.targetBlock();
if (!target || !block) return;
const offset =
Coordinate.difference(this.offsetInBlock_, target.offsetInBlock_);
block.translate(offset.x, offset.y);
}
/**
* Find the closest compatible connection to this connection.
* All parameters are in workspace units.