mirror of
https://github.com/google/blockly.git
synced 2026-01-07 00:50:27 +01:00
fix: updating connections in the db multiple times (#6859)
* fix: updating connections in the DB recursively * chore: cleanup
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user