fix!: Normalize Zelos connection indicators (#9565)

* fix: Normalize Zelos connection indicators

* feat: Add `IPathObject.updateReplacing()`
This commit is contained in:
Aaron Dodson
2026-02-26 11:27:33 -08:00
committed by GitHub
parent 1cbf1b3703
commit 589e05d4d1
10 changed files with 51 additions and 161 deletions
+8 -18
View File
@@ -791,6 +791,13 @@ export class BlockSvg
}
}
/**
* Returns whether or not this block is currently being dragged.
*/
isDragging() {
return this.dragging;
}
/**
* Set whether this block is movable or not.
*
@@ -1738,24 +1745,7 @@ export class BlockSvg
* @internal
*/
fadeForReplacement(add: boolean) {
// TODO (7204): Remove these internal methods.
(this.pathObject as AnyDuringMigration).updateReplacementFade(add);
}
/**
* Visual effect to show that if the dragging block is dropped it will connect
* to this input.
*
* @param conn The connection on the input to highlight.
* @param add True if highlighting should be added.
* @internal
*/
highlightShapeForInput(conn: RenderedConnection, add: boolean) {
// TODO (7204): Remove these internal methods.
(this.pathObject as AnyDuringMigration).updateShapeForInputHighlight(
conn,
add,
);
this.pathObject.updateReplacing?.(add);
}
/**
@@ -92,6 +92,10 @@ export class InsertionMarkerPreviewer implements IConnectionPreviewer {
staticConn.highlight();
}
if (this.workspace.getRenderer().shouldHighlightConnection(draggedConn)) {
draggedConn.highlight();
}
this.draggedConn = draggedConn;
this.staticConn = staticConn;
} finally {
@@ -224,6 +228,10 @@ export class InsertionMarkerPreviewer implements IConnectionPreviewer {
this.staticConn.unhighlight();
this.staticConn = null;
}
if (this.draggedConn) {
this.draggedConn.unhighlight();
this.draggedConn = null;
}
if (this.fadedBlock) {
this.fadedBlock.fadeForReplacement(false);
this.fadedBlock = null;
@@ -331,6 +331,7 @@ export class RenderedConnection
const highlightSvg = this.findHighlightSvg();
if (highlightSvg) {
highlightSvg.style.display = '';
highlightSvg.parentElement?.appendChild(highlightSvg);
}
}
@@ -106,4 +106,13 @@ export interface IPathObject {
* @param blockStyle The block style to use.
*/
setStyle?(blockStyle: BlockStyle): void;
/**
* Add or remove styling indicating that a block will be bumped out and
* replaced by another block that is mid-move.
*
* @param replacing True if the block is at risk of being replaced, false
* otherwise.
*/
updateReplacing?(replacing: boolean): void;
}
@@ -7,7 +7,6 @@
// Former goog.module ID: Blockly.blockRendering.PathObject
import type {BlockSvg} from '../../block_svg.js';
import type {Connection} from '../../connection.js';
import {RenderedConnection} from '../../rendered_connection.js';
import type {BlockStyle} from '../../theme.js';
import {Coordinate} from '../../utils/coordinate.js';
@@ -193,25 +192,14 @@ export class PathObject implements IPathObject {
}
/**
* Add or remove styling that shows that if the dragging block is dropped,
* this block will be replaced. If a shadow block, it will disappear.
* Otherwise it will bump.
* Add or remove styling indicating that a block will be bumped out and
* replaced by another block that is mid-move.
*
* @param enable True if styling should be added.
* @param replacing True if the block is at risk of being replaced, false
* otherwise.
*/
updateReplacementFade(enable: boolean) {
this.setClass_('blocklyReplaceable', enable);
}
/**
* Add or remove styling that shows that if the dragging block is dropped,
* this block will be connected to the input.
*
* @param _conn The connection on the input to highlight.
* @param _enable True if styling should be added.
*/
updateShapeForInputHighlight(_conn: Connection, _enable: boolean) {
// NOOP
updateReplacing(replacing: boolean) {
this.setClass_('blocklyReplaceable', replacing);
}
/** Adds the given path as a connection highlight for the given connection. */
@@ -11,6 +11,7 @@ import type {BlockSvg} from '../../block_svg.js';
import {Connection} from '../../connection.js';
import {ConnectionType} from '../../connection_type.js';
import type {IRegistrable} from '../../interfaces/i_registrable.js';
import type {RenderedConnection} from '../../rendered_connection.js';
import type {BlockStyle, Theme} from '../../theme.js';
import {ConstantProvider} from './constants.js';
import {Drawer} from './drawer.js';
@@ -188,11 +189,11 @@ export class Renderer implements IRegistrable {
/**
* Determine whether or not to highlight a connection.
*
* @param _conn The connection to determine whether or not to highlight.
* @param connection The connection to determine whether or not to highlight.
* @returns True if we should highlight the connection.
*/
shouldHighlightConnection(_conn: Connection): boolean {
return true;
shouldHighlightConnection(connection: RenderedConnection): boolean {
return !connection.getSourceBlock().isDragging();
}
/**
@@ -105,12 +105,6 @@ export class ConstantProvider extends BaseConstantProvider {
/** The size of the selected glow. */
SELECTED_GLOW_SIZE = 0.5;
/** The replacement glow colour. */
REPLACEMENT_GLOW_COLOUR = '#fff200';
/** The size of the selected glow. */
REPLACEMENT_GLOW_SIZE = 2;
/**
* The ID of the selected glow filter, or the empty string if no filter is
* set.
@@ -122,17 +116,6 @@ export class ConstantProvider extends BaseConstantProvider {
*/
private selectedGlowFilter: SVGElement | null = null;
/**
* The ID of the replacement glow filter, or the empty string if no filter
* is set.
*/
replacementGlowFilterId = '';
/**
* The <filter> element to use for a replacement glow, or null if not set.
*/
private replacementGlowFilter: SVGElement | null = null;
/**
* The object containing information about the hexagon used for a boolean
* reporter block. Null before init is called.
@@ -269,16 +252,6 @@ export class ConstantProvider extends BaseConstantProvider {
selectedGlowSize && !isNaN(selectedGlowSize)
? selectedGlowSize
: this.SELECTED_GLOW_SIZE;
this.REPLACEMENT_GLOW_COLOUR =
theme.getComponentStyle('replacementGlowColour') ||
this.REPLACEMENT_GLOW_COLOUR;
const replacementGlowSize = Number(
theme.getComponentStyle('replacementGlowSize'),
);
this.REPLACEMENT_GLOW_SIZE =
replacementGlowSize && !isNaN(replacementGlowSize)
? replacementGlowSize
: this.REPLACEMENT_GLOW_SIZE;
}
override dispose() {
@@ -286,9 +259,6 @@ export class ConstantProvider extends BaseConstantProvider {
if (this.selectedGlowFilter) {
dom.removeNode(this.selectedGlowFilter);
}
if (this.replacementGlowFilter) {
dom.removeNode(this.replacementGlowFilter);
}
}
override makeStartHat() {
@@ -740,67 +710,6 @@ export class ConstantProvider extends BaseConstantProvider {
this.selectedGlowFilterId = selectedGlowFilter.id;
this.selectedGlowFilter = selectedGlowFilter;
// Using a dilate distorts the block shape.
// Instead use a gaussian blur, and then set all alpha to 1 with a transfer.
const replacementGlowFilter = dom.createSvgElement(
Svg.FILTER,
{
'id': 'blocklyReplacementGlowFilter' + this.randomIdentifier,
'height': '160%',
'width': '180%',
'y': '-30%',
'x': '-40%',
},
defs,
);
dom.createSvgElement(
Svg.FEGAUSSIANBLUR,
{'in': 'SourceGraphic', 'stdDeviation': this.REPLACEMENT_GLOW_SIZE},
replacementGlowFilter,
);
// Set all gaussian blur pixels to 1 opacity before applying flood
const replacementComponentTransfer = dom.createSvgElement(
Svg.FECOMPONENTTRANSFER,
{'result': 'outBlur'},
replacementGlowFilter,
);
dom.createSvgElement(
Svg.FEFUNCA,
{'type': 'table', 'tableValues': '0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1'},
replacementComponentTransfer,
);
// Color the highlight
dom.createSvgElement(
Svg.FEFLOOD,
{
'flood-color': this.REPLACEMENT_GLOW_COLOUR,
'flood-opacity': 1,
'result': 'outColor',
},
replacementGlowFilter,
);
dom.createSvgElement(
Svg.FECOMPOSITE,
{
'in': 'outColor',
'in2': 'outBlur',
'operator': 'in',
'result': 'outGlow',
},
replacementGlowFilter,
);
dom.createSvgElement(
Svg.FECOMPOSITE,
{
'in': 'SourceGraphic',
'in2': 'outGlow',
'operator': 'over',
},
replacementGlowFilter,
);
this.replacementGlowFilterId = replacementGlowFilter.id;
this.replacementGlowFilter = replacementGlowFilter;
if (injectionDivIfIsParent) {
// If this renderer is for the parent workspace, add CSS variables scoped
// to the injection div referencing the created patterns so that CSS can
@@ -809,10 +718,6 @@ export class ConstantProvider extends BaseConstantProvider {
'--blocklySelectedGlowFilter',
`url(#${this.selectedGlowFilterId})`,
);
injectionDivIfIsParent.style.setProperty(
'--blocklyReplacementGlowFilter',
`url(#${this.replacementGlowFilterId})`,
);
}
}
@@ -904,10 +809,6 @@ export class ConstantProvider extends BaseConstantProvider {
`fill: none;`,
`filter: var(--blocklySelectedGlowFilter);`,
`}`,
`${selector} .blocklyReplaceable>.blocklyPath {`,
`filter: var(--blocklyReplacementGlowFilter);`,
`}`,
];
}
}
@@ -7,7 +7,6 @@
// Former goog.module ID: Blockly.zelos.PathObject
import type {BlockSvg} from '../../block_svg.js';
import type {Connection} from '../../connection.js';
import {FocusManager} from '../../focus_manager.js';
import type {BlockStyle} from '../../theme.js';
import * as dom from '../../utils/dom.js';
@@ -113,26 +112,6 @@ export class PathObject extends BasePathObject {
}
}
override updateReplacementFade(enable: boolean) {
this.setClass_('blocklyReplaceable', enable);
}
override updateShapeForInputHighlight(conn: Connection, enable: boolean) {
const name = conn.getParentInput()!.name;
const outlinePath = this.getOutlinePath(name);
if (!outlinePath) {
return;
}
if (enable) {
outlinePath.setAttribute(
'filter',
'url(#' + this.constants.replacementGlowFilterId + ')',
);
} else {
outlinePath.removeAttribute('filter');
}
}
/**
* Method that's called when the drawer is about to draw the block.
*/
@@ -7,6 +7,8 @@
// Former goog.module ID: Blockly.zelos.Renderer
import type {BlockSvg} from '../../block_svg.js';
import {ConnectionType} from '../../connection_type.js';
import type {RenderedConnection} from '../../rendered_connection.js';
import type {BlockStyle} from '../../theme.js';
import * as blockRendering from '../common/block_rendering.js';
import type {RenderInfo as BaseRenderInfo} from '../common/info.js';
@@ -86,6 +88,19 @@ export class Renderer extends BaseRenderer {
override getConstants(): ConstantProvider {
return this.constants_;
}
/**
* Determine whether or not to highlight a connection.
*
* @param connection The connection to determine whether or not to highlight.
* @returns True if we should highlight the connection.
*/
override shouldHighlightConnection(connection: RenderedConnection): boolean {
return (
super.shouldHighlightConnection(connection) ||
connection.type === ConnectionType.INPUT_VALUE
);
}
}
blockRendering.register('zelos', Renderer);
-2
View File
@@ -215,8 +215,6 @@ export namespace Theme {
cursorColour?: string;
selectedGlowColour?: string;
selectedGlowOpacity?: number;
replacementGlowColour?: string;
replacementGlowOpacity?: number;
}
export interface FontStyle {