fix: highlighting connections in zelos, also highlight connections moving (#7795)

* fix: remove zelos highlight override

* feat: add isHighlighted to rendered connection

* feat: add refreshing connection highlighting

* chore: remove highlight and unhighlight connection APIs

* chore: PR comments
This commit is contained in:
Beka Westberg
2024-01-25 15:58:00 -08:00
parent 0b344e7fab
commit 702eed42db
8 changed files with 69 additions and 53 deletions

View File

@@ -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;
}
/**

View File

@@ -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 ||

View File

@@ -40,12 +40,11 @@ export class PathObject implements IPathObject {
constants: ConstantProvider;
style: BlockStyle;
/**
* Highlight paths associated with connections.
*
* @protected
*/
connectionHighlights = new WeakMap<RenderedConnection, SVGElement>();
/** Highlight paths associated with connections. */
private connectionHighlights = new WeakMap<RenderedConnection, SVGElement>();
/** Locations of connection highlights. */
private highlightOffsets = new WeakMap<RenderedConnection, Coordinate>();
/**
* @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.
*/

View File

@@ -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.
*

View File

@@ -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_);

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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,