diff --git a/core/rendered_connection.ts b/core/rendered_connection.ts index d26e34d44..f66b64005 100644 --- a/core/rendered_connection.ts +++ b/core/rendered_connection.ts @@ -36,6 +36,7 @@ export class RenderedConnection extends Connection { private readonly dbOpposite: ConnectionDB; private readonly offsetInBlock: Coordinate; private trackedState: TrackedState; + private highlighted: boolean = false; /** Connection this connection connects to. Null if not connected. */ override targetConnection: RenderedConnection | null = null; @@ -287,12 +288,19 @@ export class RenderedConnection extends Connection { /** Add highlighting around this connection. */ highlight() { - this.getSourceBlock().workspace.getRenderer().highlightConnection(this); + this.highlighted = true; + this.getSourceBlock().queueRender(); } /** Remove the highlighting around this connection. */ unhighlight() { - this.getSourceBlock().workspace.getRenderer().unhighlightConnection(this); + this.highlighted = false; + this.getSourceBlock().queueRender(); + } + + /** Returns true if this connection is highlighted, false otherwise. */ + isHighlighted(): boolean { + return this.highlighted; } /** diff --git a/core/renderers/common/drawer.ts b/core/renderers/common/drawer.ts index bda4303af..83732516c 100644 --- a/core/renderers/common/drawer.ts +++ b/core/renderers/common/drawer.ts @@ -22,7 +22,6 @@ import {isDynamicShape, isNotch, isPuzzleTab} from './constants.js'; import type {ConstantProvider, Notch, PuzzleTab} from './constants.js'; import type {RenderInfo} from './info.js'; import * as deprecation from '../../utils/deprecation.js'; -import type {RenderedConnection} from '../../rendered_connection.js'; import {ConnectionType} from '../../connection_type.js'; /** @@ -62,6 +61,7 @@ export class Drawer { draw() { this.drawOutline_(); this.drawInternals_(); + this.updateConnectionHighlights(); this.block_.pathObject.setPath(this.outlinePath_ + '\n' + this.inlinePath_); if (this.info_.RTL) { @@ -443,13 +443,29 @@ export class Drawer { } } - /** Returns a path to highlight the given connection. */ - drawConnectionHighlightPath(conn: RenderedConnection) { - const measurable = this.info_.getMeasureableForConnection(conn); - if (!measurable) { - throw new Error('Could not find measurable for connection'); - } + /** + * Updates the path object to reflect which connections on the block are + * highlighted. + */ + protected updateConnectionHighlights() { + for (const row of this.info_.rows) { + for (const elem of row.elements) { + if (!(elem instanceof Connection)) continue; + if (elem.highlighted) { + this.drawConnectionHighlightPath(elem); + } else { + this.block_.pathObject.removeConnectionHighlight?.( + elem.connectionModel, + ); + } + } + } + } + + /** Returns a path to highlight the given connection. */ + drawConnectionHighlightPath(measurable: Connection) { + const conn = measurable.connectionModel; let path = ''; if ( conn.type === ConnectionType.INPUT_VALUE || diff --git a/core/renderers/common/path_object.ts b/core/renderers/common/path_object.ts index 35e1f83c5..d5c0850a1 100644 --- a/core/renderers/common/path_object.ts +++ b/core/renderers/common/path_object.ts @@ -40,12 +40,11 @@ export class PathObject implements IPathObject { constants: ConstantProvider; style: BlockStyle; - /** - * Highlight paths associated with connections. - * - * @protected - */ - connectionHighlights = new WeakMap(); + /** Highlight paths associated with connections. */ + private connectionHighlights = new WeakMap(); + + /** Locations of connection highlights. */ + private highlightOffsets = new WeakMap(); /** * @param root The root SVG element. @@ -273,7 +272,13 @@ export class PathObject implements IPathObject { offset: Coordinate, rtl: boolean, ) { - if (this.connectionHighlights.has(connection)) return; + if (this.connectionHighlights.has(connection)) { + if (this.currentHighlightMatchesNew(connection, connectionPath, offset)) { + return; + } + this.removeConnectionHighlight(connection); + } + const highlight = dom.createSvgElement( Svg.PATH, { @@ -287,6 +292,18 @@ export class PathObject implements IPathObject { this.connectionHighlights.set(connection, highlight); } + private currentHighlightMatchesNew( + connection: RenderedConnection, + newPath: string, + newOffset: Coordinate, + ): boolean { + const currPath = this.connectionHighlights + .get(connection) + ?.getAttribute('d'); + const currOffset = this.highlightOffsets.get(connection); + return currPath === newPath && Coordinate.equals(currOffset, newOffset); + } + /** * Removes any highlight associated with the given connection, if it exists. */ diff --git a/core/renderers/common/renderer.ts b/core/renderers/common/renderer.ts index d171f8b09..3a53322dd 100644 --- a/core/renderers/common/renderer.ts +++ b/core/renderers/common/renderer.ts @@ -257,25 +257,6 @@ export class Renderer implements IRegistrable { return InsertionMarkerManager.PREVIEW_TYPE.INSERTION_MARKER; } - /** - * Visually highlights the given connection with a border, if it is not - * already highlighted. - */ - highlightConnection(conn: RenderedConnection): void { - const block = conn.getSourceBlock(); - - const info = this.makeRenderInfo_(block); - info.measure(); - const drawer = this.makeDrawer_(block, info); - - drawer.drawConnectionHighlightPath(conn); - } - - /** Visually unhighlights the given connection, if it is highlighted. */ - unhighlightConnection(conn: RenderedConnection): void { - conn.getSourceBlock().pathObject.removeConnectionHighlight?.(conn); - } - /** * Render the block. * diff --git a/core/renderers/geras/drawer.ts b/core/renderers/geras/drawer.ts index fd9a0795e..29bcbfab4 100644 --- a/core/renderers/geras/drawer.ts +++ b/core/renderers/geras/drawer.ts @@ -40,6 +40,7 @@ export class Drawer extends BaseDrawer { override draw() { this.drawOutline_(); this.drawInternals_(); + this.updateConnectionHighlights(); const pathObject = this.block_.pathObject as PathObject; pathObject.setPath(this.outlinePath_ + '\n' + this.inlinePath_); diff --git a/core/renderers/measurables/connection.ts b/core/renderers/measurables/connection.ts index e25215770..5744eaab4 100644 --- a/core/renderers/measurables/connection.ts +++ b/core/renderers/measurables/connection.ts @@ -20,6 +20,7 @@ import {Types} from './types.js'; export class Connection extends Measurable { shape: Shape; isDynamicShape: boolean; + highlighted: boolean; /** * @param constants The rendering constants provider. @@ -32,9 +33,11 @@ export class Connection extends Measurable { ) { super(constants); - this.shape = this.constants_.shapeFor(connectionModel); - - this.isDynamicShape = 'isDynamic' in this.shape && this.shape.isDynamic; this.type |= Types.CONNECTION; + + this.shape = this.constants_.shapeFor(connectionModel); + this.isDynamicShape = 'isDynamic' in this.shape && this.shape.isDynamic; + + this.highlighted = connectionModel.isHighlighted(); } } diff --git a/core/renderers/zelos/drawer.ts b/core/renderers/zelos/drawer.ts index 1f78047f0..009247aea 100644 --- a/core/renderers/zelos/drawer.ts +++ b/core/renderers/zelos/drawer.ts @@ -8,10 +8,10 @@ import type {BlockSvg} from '../../block_svg.js'; import {ConnectionType} from '../../connection_type.js'; -import {RenderedConnection} from '../../rendered_connection.js'; import * as svgPaths from '../../utils/svg_paths.js'; import type {BaseShape, DynamicShape, Notch} from '../common/constants.js'; import {Drawer as BaseDrawer} from '../common/drawer.js'; +import {Connection} from '../measurables/connection.js'; import type {InlineInput} from '../measurables/inline_input.js'; import {OutputConnection} from '../measurables/output_connection.js'; import type {Row} from '../measurables/row.js'; @@ -44,6 +44,7 @@ export class Drawer extends BaseDrawer { pathObject.beginDrawing(); this.drawOutline_(); this.drawInternals_(); + this.updateConnectionHighlights(); pathObject.setPath(this.outlinePath_ + '\n' + this.inlinePath_); if (this.info_.RTL) { @@ -236,17 +237,14 @@ export class Drawer extends BaseDrawer { } /** Returns a path to highlight the given connection. */ - drawConnectionHighlightPath(conn: RenderedConnection) { - const measurable = this.info_.getMeasureableForConnection(conn); - if (!measurable) { - throw new Error('Could not find measurable for connection'); - } + drawConnectionHighlightPath(measurable: Connection) { + const conn = measurable.connectionModel; if ( conn.type === ConnectionType.NEXT_STATEMENT || conn.type === ConnectionType.PREVIOUS_STATEMENT || (conn.type === ConnectionType.OUTPUT_VALUE && !measurable.isDynamicShape) ) { - super.drawConnectionHighlightPath(conn); + super.drawConnectionHighlightPath(measurable); return; } diff --git a/core/renderers/zelos/renderer.ts b/core/renderers/zelos/renderer.ts index a3bd5f70e..729c54f5f 100644 --- a/core/renderers/zelos/renderer.ts +++ b/core/renderers/zelos/renderer.ts @@ -7,7 +7,6 @@ // Former goog.module ID: Blockly.zelos.Renderer import type {BlockSvg} from '../../block_svg.js'; -import type {Connection} from '../../connection.js'; import {ConnectionType} from '../../connection_type.js'; import {InsertionMarkerManager} from '../../insertion_marker_manager.js'; import type {Marker} from '../../keyboard_nav/marker.js'; @@ -109,13 +108,6 @@ export class Renderer extends BaseRenderer { return this.constants_; } - override shouldHighlightConnection(conn: Connection) { - return ( - conn.type !== ConnectionType.INPUT_VALUE && - conn.type !== ConnectionType.OUTPUT_VALUE - ); - } - override getConnectionPreviewMethod( closest: RenderedConnection, local: RenderedConnection,