mirror of
https://github.com/google/blockly.git
synced 2026-01-06 08:30:13 +01:00
fix!: remove MarkerSvg and uses (#8991)
* fix: delete MarkerSvg (marker drawer) * fix: delete marker and cursor SVG elements * chore: format * chore: lint
This commit is contained in:
@@ -50,7 +50,6 @@ import type {IFocusableTree} from './interfaces/i_focusable_tree.js';
|
|||||||
import {IIcon} from './interfaces/i_icon.js';
|
import {IIcon} from './interfaces/i_icon.js';
|
||||||
import type {INavigable} from './interfaces/i_navigable.js';
|
import type {INavigable} from './interfaces/i_navigable.js';
|
||||||
import * as internalConstants from './internal_constants.js';
|
import * as internalConstants from './internal_constants.js';
|
||||||
import {MarkerManager} from './marker_manager.js';
|
|
||||||
import {Msg} from './msg.js';
|
import {Msg} from './msg.js';
|
||||||
import * as renderManagement from './render_management.js';
|
import * as renderManagement from './render_management.js';
|
||||||
import {RenderedConnection} from './rendered_connection.js';
|
import {RenderedConnection} from './rendered_connection.js';
|
||||||
@@ -1679,7 +1678,6 @@ export class BlockSvg
|
|||||||
this.tightenChildrenEfficiently();
|
this.tightenChildrenEfficiently();
|
||||||
|
|
||||||
dom.stopTextWidthCache();
|
dom.stopTextWidthCache();
|
||||||
this.updateMarkers_();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1699,44 +1697,6 @@ export class BlockSvg
|
|||||||
if (this.nextConnection) this.nextConnection.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) {
|
|
||||||
this.workspace.getCursor()!.draw();
|
|
||||||
}
|
|
||||||
if (this.workspace.keyboardAccessibilityMode && this.pathObject.markerSvg) {
|
|
||||||
// TODO(#4592): Update all markers on the block.
|
|
||||||
this.workspace.getMarker(MarkerManager.LOCAL_MARKER)!.draw();
|
|
||||||
}
|
|
||||||
for (const input of this.inputList) {
|
|
||||||
for (const field of input.fieldRow) {
|
|
||||||
field.updateMarkers_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the cursor SVG to this block's SVG group.
|
|
||||||
*
|
|
||||||
* @param cursorSvg The SVG root of the cursor to be added to the block SVG
|
|
||||||
* group.
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
setCursorSvg(cursorSvg: SVGElement) {
|
|
||||||
this.pathObject.setCursorSvg(cursorSvg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the marker SVG to this block's SVG group.
|
|
||||||
*
|
|
||||||
* @param markerSvg The SVG root of the marker to be added to the block SVG
|
|
||||||
* group.
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
setMarkerSvg(markerSvg: SVGElement) {
|
|
||||||
this.pathObject.setMarkerSvg(markerSvg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a bounding box describing the dimensions of this block
|
* Returns a bounding box describing the dimensions of this block
|
||||||
* and any blocks stacked below it.
|
* and any blocks stacked below it.
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import type {IKeyboardAccessible} from './interfaces/i_keyboard_accessible.js';
|
|||||||
import type {INavigable} from './interfaces/i_navigable.js';
|
import type {INavigable} from './interfaces/i_navigable.js';
|
||||||
import type {IRegistrable} from './interfaces/i_registrable.js';
|
import type {IRegistrable} from './interfaces/i_registrable.js';
|
||||||
import {ISerializable} from './interfaces/i_serializable.js';
|
import {ISerializable} from './interfaces/i_serializable.js';
|
||||||
import {MarkerManager} from './marker_manager.js';
|
|
||||||
import type {ConstantProvider} from './renderers/common/constants.js';
|
import type {ConstantProvider} from './renderers/common/constants.js';
|
||||||
import type {KeyboardShortcut} from './shortcut_registry.js';
|
import type {KeyboardShortcut} from './shortcut_registry.js';
|
||||||
import * as Tooltip from './tooltip.js';
|
import * as Tooltip from './tooltip.js';
|
||||||
@@ -113,18 +112,6 @@ export abstract class Field<T = any>
|
|||||||
private tooltip: Tooltip.TipInfo | null = null;
|
private tooltip: Tooltip.TipInfo | null = null;
|
||||||
protected size_: Size;
|
protected size_: Size;
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds the cursors svg element when the cursor is attached to the field.
|
|
||||||
* This is null if there is no cursor on the field.
|
|
||||||
*/
|
|
||||||
private cursorSvg: SVGElement | null = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds the markers svg element when the marker is attached to the field.
|
|
||||||
* This is null if there is no marker on the field.
|
|
||||||
*/
|
|
||||||
private markerSvg: SVGElement | null = null;
|
|
||||||
|
|
||||||
/** The rendered field's SVG group element. */
|
/** The rendered field's SVG group element. */
|
||||||
protected fieldGroup_: SVGGElement | null = null;
|
protected fieldGroup_: SVGGElement | null = null;
|
||||||
|
|
||||||
@@ -1358,64 +1345,6 @@ export abstract class Field<T = any>
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the cursor SVG to this fields SVG group.
|
|
||||||
*
|
|
||||||
* @param cursorSvg The SVG root of the cursor to be added to the field group.
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
setCursorSvg(cursorSvg: SVGElement) {
|
|
||||||
if (!cursorSvg) {
|
|
||||||
this.cursorSvg = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.fieldGroup_) {
|
|
||||||
throw new Error(`The field group is ${this.fieldGroup_}.`);
|
|
||||||
}
|
|
||||||
this.fieldGroup_.appendChild(cursorSvg);
|
|
||||||
this.cursorSvg = cursorSvg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the marker SVG to this fields SVG group.
|
|
||||||
*
|
|
||||||
* @param markerSvg The SVG root of the marker to be added to the field group.
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
setMarkerSvg(markerSvg: SVGElement) {
|
|
||||||
if (!markerSvg) {
|
|
||||||
this.markerSvg = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.fieldGroup_) {
|
|
||||||
throw new Error(`The field group is ${this.fieldGroup_}.`);
|
|
||||||
}
|
|
||||||
this.fieldGroup_.appendChild(markerSvg);
|
|
||||||
this.markerSvg = markerSvg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redraw any attached marker or cursor svgs if needed.
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
updateMarkers_() {
|
|
||||||
const block = this.getSourceBlock();
|
|
||||||
if (!block) {
|
|
||||||
throw new UnattachedFieldError();
|
|
||||||
}
|
|
||||||
const workspace = block.workspace as WorkspaceSvg;
|
|
||||||
if (workspace.keyboardAccessibilityMode && this.cursorSvg) {
|
|
||||||
workspace.getCursor()!.draw();
|
|
||||||
}
|
|
||||||
if (workspace.keyboardAccessibilityMode && this.markerSvg) {
|
|
||||||
// TODO(#4592): Update all markers on the field.
|
|
||||||
workspace.getMarker(MarkerManager.LOCAL_MARKER)!.draw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** See IFocusableNode.getFocusableElement. */
|
/** See IFocusableNode.getFocusableElement. */
|
||||||
getFocusableElement(): HTMLElement | SVGElement {
|
getFocusableElement(): HTMLElement | SVGElement {
|
||||||
if (!this.fieldGroup_) {
|
if (!this.fieldGroup_) {
|
||||||
|
|||||||
@@ -348,15 +348,6 @@ export class FlyoutButton
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Required by IASTNodeLocationSvg, but not used. A marker cannot be set on a
|
|
||||||
* button. If the 'mark' shortcut is used on a button, its associated callback
|
|
||||||
* function is triggered.
|
|
||||||
*/
|
|
||||||
setMarkerSvg() {
|
|
||||||
throw new Error('Attempted to set a marker on a button.');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do something when the button is clicked.
|
* Do something when the button is clicked.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -11,18 +11,4 @@ import type {IASTNodeLocation} from './i_ast_node_location.js';
|
|||||||
/**
|
/**
|
||||||
* An AST node location SVG interface.
|
* An AST node location SVG interface.
|
||||||
*/
|
*/
|
||||||
export interface IASTNodeLocationSvg extends IASTNodeLocation {
|
export interface IASTNodeLocationSvg extends IASTNodeLocation {}
|
||||||
/**
|
|
||||||
* Add the marker SVG to this node's SVG group.
|
|
||||||
*
|
|
||||||
* @param markerSvg The SVG root of the marker to be added to the SVG group.
|
|
||||||
*/
|
|
||||||
setMarkerSvg(markerSvg: SVGElement | null): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the cursor SVG to this node's SVG group.
|
|
||||||
*
|
|
||||||
* @param cursorSvg The SVG root of the cursor to be added to the SVG group.
|
|
||||||
*/
|
|
||||||
setCursorSvg(cursorSvg: SVGElement | null): void;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -30,12 +30,8 @@ import {isFocusableNode} from '../interfaces/i_focusable_node.js';
|
|||||||
import type {INavigable} from '../interfaces/i_navigable.js';
|
import type {INavigable} from '../interfaces/i_navigable.js';
|
||||||
import * as registry from '../registry.js';
|
import * as registry from '../registry.js';
|
||||||
import {RenderedConnection} from '../rendered_connection.js';
|
import {RenderedConnection} from '../rendered_connection.js';
|
||||||
import type {MarkerSvg} from '../renderers/common/marker_svg.js';
|
|
||||||
import type {PathObject} from '../renderers/zelos/path_object.js';
|
|
||||||
import {Renderer} from '../renderers/zelos/renderer.js';
|
import {Renderer} from '../renderers/zelos/renderer.js';
|
||||||
import * as dom from '../utils/dom.js';
|
|
||||||
import {WorkspaceSvg} from '../workspace_svg.js';
|
import {WorkspaceSvg} from '../workspace_svg.js';
|
||||||
import {ASTNode} from './ast_node.js';
|
|
||||||
import {BlockNavigationPolicy} from './block_navigation_policy.js';
|
import {BlockNavigationPolicy} from './block_navigation_policy.js';
|
||||||
import {ConnectionNavigationPolicy} from './connection_navigation_policy.js';
|
import {ConnectionNavigationPolicy} from './connection_navigation_policy.js';
|
||||||
import {FieldNavigationPolicy} from './field_navigation_policy.js';
|
import {FieldNavigationPolicy} from './field_navigation_policy.js';
|
||||||
@@ -574,40 +570,6 @@ export class LineCursor extends Marker {
|
|||||||
return super.getCurNode();
|
return super.getCurNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the object in charge of drawing the marker.
|
|
||||||
*
|
|
||||||
* We want to customize drawing, so rather than directly setting the given
|
|
||||||
* object, we instead set a wrapper proxy object that passes through all
|
|
||||||
* method calls and property accesses except for draw(), which it delegates
|
|
||||||
* to the drawMarker() method in this class.
|
|
||||||
*
|
|
||||||
* @param drawer The object ~in charge of drawing the marker.
|
|
||||||
*/
|
|
||||||
override setDrawer(drawer: MarkerSvg) {
|
|
||||||
const altDraw = function (
|
|
||||||
this: LineCursor,
|
|
||||||
oldNode: ASTNode | null,
|
|
||||||
curNode: ASTNode | null,
|
|
||||||
) {
|
|
||||||
// Pass the unproxied, raw drawer object so that drawMarker can call its
|
|
||||||
// `draw()` method without triggering infinite recursion.
|
|
||||||
this.drawMarker(oldNode, curNode, drawer);
|
|
||||||
}.bind(this);
|
|
||||||
|
|
||||||
super.setDrawer(
|
|
||||||
new Proxy(drawer, {
|
|
||||||
get(target: typeof drawer, prop: keyof typeof drawer) {
|
|
||||||
if (prop === 'draw') {
|
|
||||||
return altDraw;
|
|
||||||
}
|
|
||||||
|
|
||||||
return target[prop];
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the location of the cursor and draw it.
|
* Set the location of the cursor and draw it.
|
||||||
*
|
*
|
||||||
@@ -631,124 +593,6 @@ export class LineCursor extends Marker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Draw this cursor's marker.
|
|
||||||
*
|
|
||||||
* This is a wrapper around this.drawer.draw (usually implemented by
|
|
||||||
* MarkerSvg.prototype.draw) that will, if newNode is a BLOCK node,
|
|
||||||
* instead call `setSelected` to select it (if it's a regular block)
|
|
||||||
* or `addSelect` (if it's a shadow block, since shadow blocks can't
|
|
||||||
* be selected) instead of using the normal drawer logic.
|
|
||||||
*
|
|
||||||
* TODO(#142): The selection and fake-selection code was originally
|
|
||||||
* a hack added for testing on October 28 2024, because the default
|
|
||||||
* drawer (MarkerSvg) behaviour in Zelos was to draw a box around
|
|
||||||
* the block and all attached child blocks, which was confusing when
|
|
||||||
* navigating stacks.
|
|
||||||
*
|
|
||||||
* Since then we have decided that we probably _do_ in most cases
|
|
||||||
* want navigating to a block to select the block, but more
|
|
||||||
* particularly that we want navigation to move _focus_. Replace
|
|
||||||
* this selection hack with non-hacky changing of focus once that's
|
|
||||||
* possible.
|
|
||||||
*
|
|
||||||
* @param oldNode The previous node.
|
|
||||||
* @param curNode The current node.
|
|
||||||
* @param realDrawer The object ~in charge of drawing the marker.
|
|
||||||
*/
|
|
||||||
private drawMarker(
|
|
||||||
oldNode: ASTNode | null,
|
|
||||||
curNode: ASTNode | null,
|
|
||||||
realDrawer: MarkerSvg,
|
|
||||||
) {
|
|
||||||
// If old node was a block, unselect it or remove fake selection.
|
|
||||||
if (oldNode?.getType() === ASTNode.types.BLOCK) {
|
|
||||||
const block = oldNode.getLocation() as BlockSvg;
|
|
||||||
if (!block.isShadow()) {
|
|
||||||
// Selection should already be in sync.
|
|
||||||
} else {
|
|
||||||
block.removeSelect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isZelos && oldNode && this.isValueInputConnection(oldNode)) {
|
|
||||||
this.hideAtInput(oldNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
const curNodeType = curNode?.getType();
|
|
||||||
const isZelosInputConnection =
|
|
||||||
this.isZelos && curNode && this.isValueInputConnection(curNode);
|
|
||||||
|
|
||||||
// If drawing can't be handled locally, just use the drawer.
|
|
||||||
if (curNodeType !== ASTNode.types.BLOCK && !isZelosInputConnection) {
|
|
||||||
realDrawer.draw(oldNode, curNode);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide any visible marker SVG and instead do some manual rendering.
|
|
||||||
realDrawer.hide();
|
|
||||||
|
|
||||||
if (isZelosInputConnection) {
|
|
||||||
this.showAtInput(curNode);
|
|
||||||
} else if (curNode && curNodeType === ASTNode.types.BLOCK) {
|
|
||||||
const block = curNode.getLocation() as BlockSvg;
|
|
||||||
if (!block.isShadow()) {
|
|
||||||
// Selection should already be in sync.
|
|
||||||
} else {
|
|
||||||
block.addSelect();
|
|
||||||
block.getParent()?.removeSelect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call MarkerSvg.prototype.fireMarkerEvent like
|
|
||||||
// MarkerSvg.prototype.draw would (even though it's private).
|
|
||||||
(realDrawer as any)?.fireMarkerEvent?.(oldNode, curNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether the node represents a value input connection.
|
|
||||||
*
|
|
||||||
* @param node The node to check
|
|
||||||
* @returns True if the node represents a value input connection.
|
|
||||||
*/
|
|
||||||
private isValueInputConnection(node: ASTNode) {
|
|
||||||
if (node?.getType() !== ASTNode.types.INPUT) return false;
|
|
||||||
const connection = node.getLocation() as RenderedConnection;
|
|
||||||
return connection.type === ConnectionType.INPUT_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hide the cursor rendering at the given input node.
|
|
||||||
*
|
|
||||||
* @param node The input node to hide.
|
|
||||||
*/
|
|
||||||
private hideAtInput(node: ASTNode) {
|
|
||||||
const inputConnection = node.getLocation() as RenderedConnection;
|
|
||||||
const sourceBlock = inputConnection.getSourceBlock() as BlockSvg;
|
|
||||||
const input = inputConnection.getParentInput();
|
|
||||||
if (input) {
|
|
||||||
const pathObject = sourceBlock.pathObject as PathObject;
|
|
||||||
const outlinePath = pathObject.getOutlinePath(input.name);
|
|
||||||
dom.removeClass(outlinePath, 'inputActiveFocus');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the cursor rendering at the given input node.
|
|
||||||
*
|
|
||||||
* @param node The input node to show.
|
|
||||||
*/
|
|
||||||
private showAtInput(node: ASTNode) {
|
|
||||||
const inputConnection = node.getLocation() as RenderedConnection;
|
|
||||||
const sourceBlock = inputConnection.getSourceBlock() as BlockSvg;
|
|
||||||
const input = inputConnection.getParentInput();
|
|
||||||
if (input) {
|
|
||||||
const pathObject = sourceBlock.pathObject as PathObject;
|
|
||||||
const outlinePath = pathObject.getOutlinePath(input.name);
|
|
||||||
dom.addClass(outlinePath, 'inputActiveFocus');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the current node to match the selection.
|
* Updates the current node to match the selection.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import {Field} from '../field.js';
|
|||||||
import {FlyoutButton} from '../flyout_button.js';
|
import {FlyoutButton} from '../flyout_button.js';
|
||||||
import type {INavigable} from '../interfaces/i_navigable.js';
|
import type {INavigable} from '../interfaces/i_navigable.js';
|
||||||
import {RenderedConnection} from '../rendered_connection.js';
|
import {RenderedConnection} from '../rendered_connection.js';
|
||||||
import type {MarkerSvg} from '../renderers/common/marker_svg.js';
|
|
||||||
import {Coordinate} from '../utils/coordinate.js';
|
import {Coordinate} from '../utils/coordinate.js';
|
||||||
import {WorkspaceSvg} from '../workspace_svg.js';
|
import {WorkspaceSvg} from '../workspace_svg.js';
|
||||||
import {ASTNode} from './ast_node.js';
|
import {ASTNode} from './ast_node.js';
|
||||||
@@ -33,33 +32,9 @@ export class Marker {
|
|||||||
/** The current location of the marker. */
|
/** The current location of the marker. */
|
||||||
protected curNode: INavigable<any> | null = null;
|
protected curNode: INavigable<any> | null = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* The object in charge of drawing the visual representation of the current
|
|
||||||
* node.
|
|
||||||
*/
|
|
||||||
private drawer: MarkerSvg | null = null;
|
|
||||||
|
|
||||||
/** The type of the marker. */
|
/** The type of the marker. */
|
||||||
type = 'marker';
|
type = 'marker';
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the object in charge of drawing the marker.
|
|
||||||
*
|
|
||||||
* @param drawer The object in charge of drawing the marker.
|
|
||||||
*/
|
|
||||||
setDrawer(drawer: MarkerSvg) {
|
|
||||||
this.drawer = drawer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current drawer for the marker.
|
|
||||||
*
|
|
||||||
* @returns The object in charge of drawing the marker.
|
|
||||||
*/
|
|
||||||
getDrawer(): MarkerSvg | null {
|
|
||||||
return this.drawer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current location of the marker.
|
* Gets the current location of the marker.
|
||||||
*
|
*
|
||||||
@@ -75,30 +50,11 @@ export class Marker {
|
|||||||
* @param newNode The new location of the marker, or null to remove it.
|
* @param newNode The new location of the marker, or null to remove it.
|
||||||
*/
|
*/
|
||||||
setCurNode(newNode: INavigable<any> | null) {
|
setCurNode(newNode: INavigable<any> | null) {
|
||||||
const oldNode = this.curNode;
|
|
||||||
this.curNode = newNode;
|
this.curNode = newNode;
|
||||||
this.drawer?.draw(this.toASTNode(oldNode), this.toASTNode(this.curNode));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redraw the current marker.
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
draw() {
|
|
||||||
const node = this.toASTNode(this.curNode);
|
|
||||||
this.drawer?.draw(node, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Hide the marker SVG. */
|
|
||||||
hide() {
|
|
||||||
this.drawer?.hide();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Dispose of this marker. */
|
/** Dispose of this marker. */
|
||||||
dispose() {
|
dispose() {
|
||||||
this.drawer?.dispose();
|
|
||||||
this.drawer = null;
|
|
||||||
this.curNode = null;
|
this.curNode = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,15 +25,9 @@ export class MarkerManager {
|
|||||||
/** The cursor. */
|
/** The cursor. */
|
||||||
private cursor: LineCursor | null = null;
|
private cursor: LineCursor | null = null;
|
||||||
|
|
||||||
/** The cursor's SVG element. */
|
|
||||||
private cursorSvg: SVGElement | null = null;
|
|
||||||
|
|
||||||
/** The map of markers for the workspace. */
|
/** The map of markers for the workspace. */
|
||||||
private markers = new Map<string, Marker>();
|
private markers = new Map<string, Marker>();
|
||||||
|
|
||||||
/** The marker's SVG element. */
|
|
||||||
private markerSvg: SVGElement | null = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param workspace The workspace for the marker manager.
|
* @param workspace The workspace for the marker manager.
|
||||||
* @internal
|
* @internal
|
||||||
@@ -50,11 +44,6 @@ export class MarkerManager {
|
|||||||
if (this.markers.has(id)) {
|
if (this.markers.has(id)) {
|
||||||
this.unregisterMarker(id);
|
this.unregisterMarker(id);
|
||||||
}
|
}
|
||||||
const drawer = this.workspace
|
|
||||||
.getRenderer()
|
|
||||||
.makeMarkerDrawer(this.workspace, marker);
|
|
||||||
marker.setDrawer(drawer);
|
|
||||||
this.setMarkerSvg(drawer.createDom());
|
|
||||||
this.markers.set(id, marker);
|
this.markers.set(id, marker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,67 +94,7 @@ export class MarkerManager {
|
|||||||
* @param cursor The cursor used to move around this workspace.
|
* @param cursor The cursor used to move around this workspace.
|
||||||
*/
|
*/
|
||||||
setCursor(cursor: LineCursor) {
|
setCursor(cursor: LineCursor) {
|
||||||
this.cursor?.getDrawer()?.dispose();
|
|
||||||
this.cursor = cursor;
|
this.cursor = cursor;
|
||||||
if (this.cursor) {
|
|
||||||
const drawer = this.workspace
|
|
||||||
.getRenderer()
|
|
||||||
.makeMarkerDrawer(this.workspace, this.cursor);
|
|
||||||
this.cursor.setDrawer(drawer);
|
|
||||||
this.setCursorSvg(drawer.createDom());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the cursor SVG to this workspace SVG group.
|
|
||||||
*
|
|
||||||
* @param cursorSvg The SVG root of the cursor to be added to the workspace
|
|
||||||
* SVG group.
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
setCursorSvg(cursorSvg: SVGElement | null) {
|
|
||||||
if (!cursorSvg) {
|
|
||||||
this.cursorSvg = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.workspace.getBlockCanvas()!.appendChild(cursorSvg);
|
|
||||||
this.cursorSvg = cursorSvg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the marker SVG to this workspaces SVG group.
|
|
||||||
*
|
|
||||||
* @param markerSvg The SVG root of the marker to be added to the workspace
|
|
||||||
* SVG group.
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
setMarkerSvg(markerSvg: SVGElement | null) {
|
|
||||||
if (!markerSvg) {
|
|
||||||
this.markerSvg = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.workspace.getBlockCanvas()) {
|
|
||||||
if (this.cursorSvg) {
|
|
||||||
this.workspace
|
|
||||||
.getBlockCanvas()!
|
|
||||||
.insertBefore(markerSvg, this.cursorSvg);
|
|
||||||
} else {
|
|
||||||
this.workspace.getBlockCanvas()!.appendChild(markerSvg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redraw the attached cursor SVG if needed.
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
updateMarkers() {
|
|
||||||
if (this.workspace.keyboardAccessibilityMode && this.cursorSvg) {
|
|
||||||
this.workspace.getCursor()!.draw();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import {Types} from '../measurables/types.js';
|
|||||||
import {Drawer} from './drawer.js';
|
import {Drawer} from './drawer.js';
|
||||||
import type {IPathObject} from './i_path_object.js';
|
import type {IPathObject} from './i_path_object.js';
|
||||||
import {RenderInfo} from './info.js';
|
import {RenderInfo} from './info.js';
|
||||||
import {MarkerSvg} from './marker_svg.js';
|
|
||||||
import {PathObject} from './path_object.js';
|
import {PathObject} from './path_object.js';
|
||||||
import {Renderer} from './renderer.js';
|
import {Renderer} from './renderer.js';
|
||||||
|
|
||||||
@@ -94,7 +93,6 @@ export {
|
|||||||
InRowSpacer,
|
InRowSpacer,
|
||||||
IPathObject,
|
IPathObject,
|
||||||
JaggedEdge,
|
JaggedEdge,
|
||||||
MarkerSvg,
|
|
||||||
Measurable,
|
Measurable,
|
||||||
NextConnection,
|
NextConnection,
|
||||||
OutputConnection,
|
OutputConnection,
|
||||||
|
|||||||
@@ -30,18 +30,6 @@ export interface IPathObject {
|
|||||||
/** The primary path of the block. */
|
/** The primary path of the block. */
|
||||||
style: BlockStyle;
|
style: BlockStyle;
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds the cursors SVG element when the cursor is attached to the block.
|
|
||||||
* This is null if there is no cursor on the block.
|
|
||||||
*/
|
|
||||||
cursorSvg: SVGElement | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds the markers SVG element when the marker is attached to the block.
|
|
||||||
* This is null if there is no marker on the block.
|
|
||||||
*/
|
|
||||||
markerSvg: SVGElement | null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the path generated by the renderer onto the respective SVG element.
|
* Set the path generated by the renderer onto the respective SVG element.
|
||||||
*
|
*
|
||||||
@@ -54,22 +42,6 @@ export interface IPathObject {
|
|||||||
*/
|
*/
|
||||||
flipRTL(): void;
|
flipRTL(): void;
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the cursor SVG to this block's SVG group.
|
|
||||||
*
|
|
||||||
* @param cursorSvg The SVG root of the cursor to be added to the block SVG
|
|
||||||
* group.
|
|
||||||
*/
|
|
||||||
setCursorSvg(cursorSvg: SVGElement): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the marker SVG to this block's SVG group.
|
|
||||||
*
|
|
||||||
* @param markerSvg The SVG root of the marker to be added to the block SVG
|
|
||||||
* group.
|
|
||||||
*/
|
|
||||||
setMarkerSvg(markerSvg: SVGElement): void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set whether the block shows a highlight or not. Block highlighting is
|
* Set whether the block shows a highlight or not. Block highlighting is
|
||||||
* often used to visually mark blocks currently being executed.
|
* often used to visually mark blocks currently being executed.
|
||||||
|
|||||||
@@ -1,767 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright 2019 Google LLC
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Former goog.module ID: Blockly.blockRendering.MarkerSvg
|
|
||||||
|
|
||||||
// Unused import preserved for side-effects. Remove if unneeded.
|
|
||||||
import '../../events/events_marker_move.js';
|
|
||||||
|
|
||||||
import type {BlockSvg} from '../../block_svg.js';
|
|
||||||
import type {Connection} from '../../connection.js';
|
|
||||||
import {ConnectionType} from '../../connection_type.js';
|
|
||||||
import {EventType} from '../../events/type.js';
|
|
||||||
import * as eventUtils from '../../events/utils.js';
|
|
||||||
import type {Field} from '../../field.js';
|
|
||||||
import {FlyoutButton} from '../../flyout_button.js';
|
|
||||||
import type {IASTNodeLocationSvg} from '../../interfaces/i_ast_node_location_svg.js';
|
|
||||||
import {ASTNode} from '../../keyboard_nav/ast_node.js';
|
|
||||||
import type {Marker} from '../../keyboard_nav/marker.js';
|
|
||||||
import type {RenderedConnection} from '../../rendered_connection.js';
|
|
||||||
import * as dom from '../../utils/dom.js';
|
|
||||||
import {Svg} from '../../utils/svg.js';
|
|
||||||
import * as svgPaths from '../../utils/svg_paths.js';
|
|
||||||
import type {WorkspaceSvg} from '../../workspace_svg.js';
|
|
||||||
import type {ConstantProvider, Notch, PuzzleTab} from './constants.js';
|
|
||||||
|
|
||||||
/** The name of the CSS class for a cursor. */
|
|
||||||
const CURSOR_CLASS = 'blocklyCursor';
|
|
||||||
|
|
||||||
/** The name of the CSS class for a marker. */
|
|
||||||
const MARKER_CLASS = 'blocklyMarker';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* What we multiply the height by to get the height of the marker.
|
|
||||||
* Only used for the block and block connections.
|
|
||||||
*/
|
|
||||||
const HEIGHT_MULTIPLIER = 3 / 4;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class for a marker, containing methods for graphically rendering a marker as
|
|
||||||
* SVG.
|
|
||||||
*/
|
|
||||||
export class MarkerSvg {
|
|
||||||
/**
|
|
||||||
* The workspace, field, or block that the marker SVG element should be
|
|
||||||
* attached to.
|
|
||||||
*/
|
|
||||||
protected parent: IASTNodeLocationSvg | null = null;
|
|
||||||
|
|
||||||
/** The current SVG element for the marker. */
|
|
||||||
currentMarkerSvg: SVGElement | null = null;
|
|
||||||
colour_: string;
|
|
||||||
|
|
||||||
/** The root SVG group containing the marker. */
|
|
||||||
protected markerSvg_: SVGGElement | null = null;
|
|
||||||
protected svgGroup_: SVGGElement | null = null;
|
|
||||||
|
|
||||||
protected markerBlock_: SVGPathElement | null = null;
|
|
||||||
|
|
||||||
protected markerInput_: SVGPathElement | null = null;
|
|
||||||
protected markerSvgLine_: SVGRectElement | null = null;
|
|
||||||
|
|
||||||
protected markerSvgRect_: SVGRectElement | null = null;
|
|
||||||
|
|
||||||
/** The constants necessary to draw the marker. */
|
|
||||||
protected constants_: ConstantProvider;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param workspace The workspace the marker belongs to.
|
|
||||||
* @param constants The constants for the renderer.
|
|
||||||
* @param marker The marker to draw.
|
|
||||||
*/
|
|
||||||
constructor(
|
|
||||||
protected readonly workspace: WorkspaceSvg,
|
|
||||||
constants: ConstantProvider,
|
|
||||||
protected readonly marker: Marker,
|
|
||||||
) {
|
|
||||||
this.constants_ = constants;
|
|
||||||
|
|
||||||
const defaultColour = this.isCursor()
|
|
||||||
? this.constants_.CURSOR_COLOUR
|
|
||||||
: this.constants_.MARKER_COLOUR;
|
|
||||||
|
|
||||||
/** The colour of the marker. */
|
|
||||||
this.colour_ = marker.colour || defaultColour;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the root node of the SVG or null if none exists.
|
|
||||||
*
|
|
||||||
* @returns The root SVG node.
|
|
||||||
*/
|
|
||||||
getSvgRoot(): SVGElement | null {
|
|
||||||
return this.svgGroup_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the marker.
|
|
||||||
*
|
|
||||||
* @returns The marker to draw for.
|
|
||||||
*/
|
|
||||||
getMarker(): Marker {
|
|
||||||
return this.marker;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True if the marker should be drawn as a cursor, false otherwise.
|
|
||||||
* A cursor is drawn as a flashing line. A marker is drawn as a solid line.
|
|
||||||
*
|
|
||||||
* @returns True if the marker is a cursor, false otherwise.
|
|
||||||
*/
|
|
||||||
isCursor(): boolean {
|
|
||||||
return this.marker.type === 'cursor';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the DOM element for the marker.
|
|
||||||
*
|
|
||||||
* @returns The marker controls SVG group.
|
|
||||||
*/
|
|
||||||
createDom(): SVGElement {
|
|
||||||
const className = this.isCursor() ? CURSOR_CLASS : MARKER_CLASS;
|
|
||||||
|
|
||||||
this.svgGroup_ = dom.createSvgElement(Svg.G, {'class': className});
|
|
||||||
|
|
||||||
this.createDomInternal_();
|
|
||||||
return this.svgGroup_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attaches the SVG root of the marker to the SVG group of the parent.
|
|
||||||
*
|
|
||||||
* @param newParent The workspace, field, or block that the marker SVG element
|
|
||||||
* should be attached to.
|
|
||||||
*/
|
|
||||||
protected setParent_(newParent: IASTNodeLocationSvg) {
|
|
||||||
if (!this.isCursor()) {
|
|
||||||
if (this.parent) {
|
|
||||||
this.parent.setMarkerSvg(null);
|
|
||||||
}
|
|
||||||
newParent.setMarkerSvg(this.getSvgRoot());
|
|
||||||
} else {
|
|
||||||
if (this.parent) {
|
|
||||||
this.parent.setCursorSvg(null);
|
|
||||||
}
|
|
||||||
newParent.setCursorSvg(this.getSvgRoot());
|
|
||||||
}
|
|
||||||
this.parent = newParent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the marker.
|
|
||||||
*
|
|
||||||
* @param oldNode The previous node the marker was on or null.
|
|
||||||
* @param curNode The node that we want to draw the marker for.
|
|
||||||
*/
|
|
||||||
draw(oldNode: ASTNode | null, curNode: ASTNode | null) {
|
|
||||||
if (!curNode) {
|
|
||||||
this.hide();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.constants_ = this.workspace.getRenderer().getConstants();
|
|
||||||
|
|
||||||
const defaultColour = this.isCursor()
|
|
||||||
? this.constants_.CURSOR_COLOUR
|
|
||||||
: this.constants_.MARKER_COLOUR;
|
|
||||||
this.colour_ = this.marker.colour || defaultColour;
|
|
||||||
this.applyColour_(curNode);
|
|
||||||
|
|
||||||
this.showAtLocation_(curNode);
|
|
||||||
|
|
||||||
this.fireMarkerEvent(oldNode, curNode);
|
|
||||||
|
|
||||||
// Ensures the marker will be visible immediately after the move.
|
|
||||||
const animate = this.currentMarkerSvg!.childNodes[0];
|
|
||||||
if (
|
|
||||||
animate !== undefined &&
|
|
||||||
(animate as SVGAnimationElement).beginElement
|
|
||||||
) {
|
|
||||||
(animate as SVGAnimationElement).beginElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the marker's visible state based on the type of curNode..
|
|
||||||
*
|
|
||||||
* @param curNode The node that we want to draw the marker for.
|
|
||||||
*/
|
|
||||||
protected showAtLocation_(curNode: ASTNode) {
|
|
||||||
const curNodeAsConnection = curNode.getLocation() as Connection;
|
|
||||||
const connectionType = curNodeAsConnection.type;
|
|
||||||
if (curNode.getType() === ASTNode.types.BLOCK) {
|
|
||||||
this.showWithBlock_(curNode);
|
|
||||||
} else if (curNode.getType() === ASTNode.types.OUTPUT) {
|
|
||||||
this.showWithOutput_(curNode);
|
|
||||||
} else if (connectionType === ConnectionType.INPUT_VALUE) {
|
|
||||||
this.showWithInput_(curNode);
|
|
||||||
} else if (connectionType === ConnectionType.NEXT_STATEMENT) {
|
|
||||||
this.showWithNext_(curNode);
|
|
||||||
} else if (curNode.getType() === ASTNode.types.PREVIOUS) {
|
|
||||||
this.showWithPrevious_(curNode);
|
|
||||||
} else if (curNode.getType() === ASTNode.types.FIELD) {
|
|
||||||
this.showWithField_(curNode);
|
|
||||||
} else if (curNode.getType() === ASTNode.types.WORKSPACE) {
|
|
||||||
this.showWithCoordinates_(curNode);
|
|
||||||
} else if (curNode.getType() === ASTNode.types.STACK) {
|
|
||||||
this.showWithStack_(curNode);
|
|
||||||
} else if (curNode.getType() === ASTNode.types.BUTTON) {
|
|
||||||
this.showWithButton_(curNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************
|
|
||||||
* Display
|
|
||||||
**************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the marker as a combination of the previous connection and block,
|
|
||||||
* the output connection and block, or just the block.
|
|
||||||
*
|
|
||||||
* @param curNode The node to draw the marker for.
|
|
||||||
*/
|
|
||||||
protected showWithBlockPrevOutput(curNode: ASTNode) {
|
|
||||||
const block = curNode.getSourceBlock() as BlockSvg;
|
|
||||||
const width = block.width;
|
|
||||||
const height = block.height;
|
|
||||||
const markerHeight = height * HEIGHT_MULTIPLIER;
|
|
||||||
const markerOffset = this.constants_.CURSOR_BLOCK_PADDING;
|
|
||||||
|
|
||||||
if (block.previousConnection) {
|
|
||||||
const connectionShape = this.constants_.shapeFor(
|
|
||||||
block.previousConnection,
|
|
||||||
) as Notch;
|
|
||||||
this.positionPrevious_(
|
|
||||||
width,
|
|
||||||
markerOffset,
|
|
||||||
markerHeight,
|
|
||||||
connectionShape,
|
|
||||||
);
|
|
||||||
} else if (block.outputConnection) {
|
|
||||||
const connectionShape = this.constants_.shapeFor(
|
|
||||||
block.outputConnection,
|
|
||||||
) as PuzzleTab;
|
|
||||||
this.positionOutput_(width, height, connectionShape);
|
|
||||||
} else {
|
|
||||||
this.positionBlock_(width, markerOffset, markerHeight);
|
|
||||||
}
|
|
||||||
this.setParent_(block);
|
|
||||||
this.showCurrent_();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position and display the marker for a block.
|
|
||||||
*
|
|
||||||
* @param curNode The node to draw the marker for.
|
|
||||||
*/
|
|
||||||
protected showWithBlock_(curNode: ASTNode) {
|
|
||||||
this.showWithBlockPrevOutput(curNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position and display the marker for a previous connection.
|
|
||||||
*
|
|
||||||
* @param curNode The node to draw the marker for.
|
|
||||||
*/
|
|
||||||
protected showWithPrevious_(curNode: ASTNode) {
|
|
||||||
this.showWithBlockPrevOutput(curNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position and display the marker for an output connection.
|
|
||||||
*
|
|
||||||
* @param curNode The node to draw the marker for.
|
|
||||||
*/
|
|
||||||
protected showWithOutput_(curNode: ASTNode) {
|
|
||||||
this.showWithBlockPrevOutput(curNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position and display the marker for a workspace coordinate.
|
|
||||||
* This is a horizontal line.
|
|
||||||
*
|
|
||||||
* @param curNode The node to draw the marker for.
|
|
||||||
*/
|
|
||||||
protected showWithCoordinates_(curNode: ASTNode) {
|
|
||||||
const wsCoordinate = curNode.getWsCoordinate();
|
|
||||||
let x = wsCoordinate?.x ?? 0;
|
|
||||||
const y = wsCoordinate?.y ?? 0;
|
|
||||||
|
|
||||||
if (this.workspace.RTL) {
|
|
||||||
x -= this.constants_.CURSOR_WS_WIDTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.positionLine_(x, y, this.constants_.CURSOR_WS_WIDTH);
|
|
||||||
this.setParent_(this.workspace);
|
|
||||||
this.showCurrent_();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position and display the marker for a field.
|
|
||||||
* This is a box around the field.
|
|
||||||
*
|
|
||||||
* @param curNode The node to draw the marker for.
|
|
||||||
*/
|
|
||||||
protected showWithField_(curNode: ASTNode) {
|
|
||||||
const field = curNode.getLocation() as Field;
|
|
||||||
const width = field.getSize().width;
|
|
||||||
const height = field.getSize().height;
|
|
||||||
|
|
||||||
this.positionRect_(0, 0, width, height);
|
|
||||||
this.setParent_(field);
|
|
||||||
this.showCurrent_();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position and display the marker for an input.
|
|
||||||
* This is a puzzle piece.
|
|
||||||
*
|
|
||||||
* @param curNode The node to draw the marker for.
|
|
||||||
*/
|
|
||||||
protected showWithInput_(curNode: ASTNode) {
|
|
||||||
const connection = curNode.getLocation() as RenderedConnection;
|
|
||||||
const sourceBlock = connection.getSourceBlock();
|
|
||||||
|
|
||||||
this.positionInput_(connection);
|
|
||||||
this.setParent_(sourceBlock);
|
|
||||||
this.showCurrent_();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position and display the marker for a next connection.
|
|
||||||
* This is a horizontal line.
|
|
||||||
*
|
|
||||||
* @param curNode The node to draw the marker for.
|
|
||||||
*/
|
|
||||||
protected showWithNext_(curNode: ASTNode) {
|
|
||||||
const connection = curNode.getLocation() as RenderedConnection;
|
|
||||||
const targetBlock = connection.getSourceBlock();
|
|
||||||
let x = 0;
|
|
||||||
const y = connection.getOffsetInBlock().y;
|
|
||||||
const width = targetBlock.getHeightWidth().width;
|
|
||||||
if (this.workspace.RTL) {
|
|
||||||
x = -width;
|
|
||||||
}
|
|
||||||
this.positionLine_(x, y, width);
|
|
||||||
this.setParent_(targetBlock);
|
|
||||||
this.showCurrent_();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position and display the marker for a stack.
|
|
||||||
* This is a box with extra padding around the entire stack of blocks.
|
|
||||||
*
|
|
||||||
* @param curNode The node to draw the marker for.
|
|
||||||
*/
|
|
||||||
protected showWithStack_(curNode: ASTNode) {
|
|
||||||
const block = curNode.getLocation() as BlockSvg;
|
|
||||||
|
|
||||||
// Gets the height and width of entire stack.
|
|
||||||
const heightWidth = block.getHeightWidth();
|
|
||||||
|
|
||||||
// Add padding so that being on a stack looks different than being on a
|
|
||||||
// block.
|
|
||||||
const width = heightWidth.width + this.constants_.CURSOR_STACK_PADDING;
|
|
||||||
const height = heightWidth.height + this.constants_.CURSOR_STACK_PADDING;
|
|
||||||
|
|
||||||
// Shift the rectangle slightly to upper left so padding is equal on all
|
|
||||||
// sides.
|
|
||||||
const xPadding = -this.constants_.CURSOR_STACK_PADDING / 2;
|
|
||||||
const yPadding = -this.constants_.CURSOR_STACK_PADDING / 2;
|
|
||||||
|
|
||||||
let x = xPadding;
|
|
||||||
const y = yPadding;
|
|
||||||
|
|
||||||
if (this.workspace.RTL) {
|
|
||||||
x = -(width + xPadding);
|
|
||||||
}
|
|
||||||
this.positionRect_(x, y, width, height);
|
|
||||||
this.setParent_(block);
|
|
||||||
this.showCurrent_();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position and display the marker for a flyout button.
|
|
||||||
* This is a box with extra padding around the button.
|
|
||||||
*
|
|
||||||
* @param curNode The node to draw the marker for.
|
|
||||||
*/
|
|
||||||
protected showWithButton_(curNode: ASTNode) {
|
|
||||||
const button = curNode.getLocation() as FlyoutButton;
|
|
||||||
|
|
||||||
// Gets the height and width of entire stack.
|
|
||||||
const heightWidth = {height: button.height, width: button.width};
|
|
||||||
|
|
||||||
// Add padding so that being on a button looks similar to being on a stack.
|
|
||||||
const width = heightWidth.width + this.constants_.CURSOR_STACK_PADDING;
|
|
||||||
const height = heightWidth.height + this.constants_.CURSOR_STACK_PADDING;
|
|
||||||
|
|
||||||
// Shift the rectangle slightly to upper left so padding is equal on all
|
|
||||||
// sides.
|
|
||||||
const xPadding = -this.constants_.CURSOR_STACK_PADDING / 2;
|
|
||||||
const yPadding = -this.constants_.CURSOR_STACK_PADDING / 2;
|
|
||||||
|
|
||||||
let x = xPadding;
|
|
||||||
const y = yPadding;
|
|
||||||
|
|
||||||
if (this.workspace.RTL) {
|
|
||||||
x = -(width + xPadding);
|
|
||||||
}
|
|
||||||
this.positionRect_(x, y, width, height);
|
|
||||||
this.setParent_(button);
|
|
||||||
this.showCurrent_();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Show the current marker. */
|
|
||||||
protected showCurrent_() {
|
|
||||||
this.hide();
|
|
||||||
if (this.currentMarkerSvg) {
|
|
||||||
this.currentMarkerSvg.style.display = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************
|
|
||||||
* Position
|
|
||||||
**************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position the marker for a block.
|
|
||||||
* Displays an outline of the top half of a rectangle around a block.
|
|
||||||
*
|
|
||||||
* @param width The width of the block.
|
|
||||||
* @param markerOffset The extra padding for around the block.
|
|
||||||
* @param markerHeight The height of the marker.
|
|
||||||
*/
|
|
||||||
protected positionBlock_(
|
|
||||||
width: number,
|
|
||||||
markerOffset: number,
|
|
||||||
markerHeight: number,
|
|
||||||
) {
|
|
||||||
const markerPath =
|
|
||||||
svgPaths.moveBy(-markerOffset, markerHeight) +
|
|
||||||
svgPaths.lineOnAxis('V', -markerOffset) +
|
|
||||||
svgPaths.lineOnAxis('H', width + markerOffset * 2) +
|
|
||||||
svgPaths.lineOnAxis('V', markerHeight);
|
|
||||||
if (!this.markerBlock_) {
|
|
||||||
throw new Error(
|
|
||||||
'createDom should be called before positioning the marker',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.markerBlock_.setAttribute('d', markerPath);
|
|
||||||
if (this.workspace.RTL) {
|
|
||||||
this.flipRtl(this.markerBlock_);
|
|
||||||
}
|
|
||||||
this.currentMarkerSvg = this.markerBlock_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position the marker for an input connection.
|
|
||||||
* Displays a filled in puzzle piece.
|
|
||||||
*
|
|
||||||
* @param connection The connection to position marker around.
|
|
||||||
*/
|
|
||||||
protected positionInput_(connection: RenderedConnection) {
|
|
||||||
const x = connection.getOffsetInBlock().x;
|
|
||||||
const y = connection.getOffsetInBlock().y;
|
|
||||||
|
|
||||||
const path =
|
|
||||||
svgPaths.moveTo(0, 0) +
|
|
||||||
(this.constants_.shapeFor(connection) as PuzzleTab).pathDown;
|
|
||||||
|
|
||||||
this.markerInput_!.setAttribute('d', path);
|
|
||||||
this.markerInput_!.setAttribute(
|
|
||||||
'transform',
|
|
||||||
'translate(' +
|
|
||||||
x +
|
|
||||||
',' +
|
|
||||||
y +
|
|
||||||
')' +
|
|
||||||
(this.workspace.RTL ? ' scale(-1 1)' : ''),
|
|
||||||
);
|
|
||||||
this.currentMarkerSvg = this.markerInput_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Move and show the marker at the specified coordinate in workspace units.
|
|
||||||
* Displays a horizontal line.
|
|
||||||
*
|
|
||||||
* @param x The new x, in workspace units.
|
|
||||||
* @param y The new y, in workspace units.
|
|
||||||
* @param width The new width, in workspace units.
|
|
||||||
*/
|
|
||||||
protected positionLine_(x: number, y: number, width: number) {
|
|
||||||
if (!this.markerSvgLine_) {
|
|
||||||
throw new Error('createDom should be called before positioning the line');
|
|
||||||
}
|
|
||||||
this.markerSvgLine_.setAttribute('x', `${x}`);
|
|
||||||
this.markerSvgLine_.setAttribute('y', `${y}`);
|
|
||||||
this.markerSvgLine_.setAttribute('width', `${width}`);
|
|
||||||
this.currentMarkerSvg = this.markerSvgLine_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position the marker for an output connection.
|
|
||||||
* Displays a puzzle outline and the top and bottom path.
|
|
||||||
*
|
|
||||||
* @param width The width of the block.
|
|
||||||
* @param height The height of the block.
|
|
||||||
* @param connectionShape The shape object for the connection.
|
|
||||||
*/
|
|
||||||
protected positionOutput_(
|
|
||||||
width: number,
|
|
||||||
height: number,
|
|
||||||
connectionShape: PuzzleTab,
|
|
||||||
) {
|
|
||||||
if (!this.markerBlock_) {
|
|
||||||
throw new Error(
|
|
||||||
'createDom should be called before positioning the output',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const markerPath =
|
|
||||||
svgPaths.moveBy(width, 0) +
|
|
||||||
svgPaths.lineOnAxis('h', -(width - connectionShape.width)) +
|
|
||||||
svgPaths.lineOnAxis('v', this.constants_.TAB_OFFSET_FROM_TOP) +
|
|
||||||
connectionShape.pathDown +
|
|
||||||
svgPaths.lineOnAxis('V', height) +
|
|
||||||
svgPaths.lineOnAxis('H', width);
|
|
||||||
this.markerBlock_.setAttribute('d', markerPath);
|
|
||||||
if (this.workspace.RTL) {
|
|
||||||
this.flipRtl(this.markerBlock_);
|
|
||||||
}
|
|
||||||
this.currentMarkerSvg = this.markerBlock_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position the marker for a previous connection.
|
|
||||||
* Displays a half rectangle with a notch in the top to represent the previous
|
|
||||||
* connection.
|
|
||||||
*
|
|
||||||
* @param width The width of the block.
|
|
||||||
* @param markerOffset The offset of the marker from around the block.
|
|
||||||
* @param markerHeight The height of the marker.
|
|
||||||
* @param connectionShape The shape object for the connection.
|
|
||||||
*/
|
|
||||||
protected positionPrevious_(
|
|
||||||
width: number,
|
|
||||||
markerOffset: number,
|
|
||||||
markerHeight: number,
|
|
||||||
connectionShape: Notch,
|
|
||||||
) {
|
|
||||||
if (!this.markerBlock_) {
|
|
||||||
throw new Error(
|
|
||||||
'createDom should be called before positioning the previous connection marker',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const markerPath =
|
|
||||||
svgPaths.moveBy(-markerOffset, markerHeight) +
|
|
||||||
svgPaths.lineOnAxis('V', -markerOffset) +
|
|
||||||
svgPaths.lineOnAxis('H', this.constants_.NOTCH_OFFSET_LEFT) +
|
|
||||||
connectionShape.pathLeft +
|
|
||||||
svgPaths.lineOnAxis('H', width + markerOffset * 2) +
|
|
||||||
svgPaths.lineOnAxis('V', markerHeight);
|
|
||||||
this.markerBlock_.setAttribute('d', markerPath);
|
|
||||||
if (this.workspace.RTL) {
|
|
||||||
this.flipRtl(this.markerBlock_);
|
|
||||||
}
|
|
||||||
this.currentMarkerSvg = this.markerBlock_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Move and show the marker at the specified coordinate in workspace units.
|
|
||||||
* Displays a filled in rectangle.
|
|
||||||
*
|
|
||||||
* @param x The new x, in workspace units.
|
|
||||||
* @param y The new y, in workspace units.
|
|
||||||
* @param width The new width, in workspace units.
|
|
||||||
* @param height The new height, in workspace units.
|
|
||||||
*/
|
|
||||||
protected positionRect_(x: number, y: number, width: number, height: number) {
|
|
||||||
if (!this.markerSvgRect_) {
|
|
||||||
throw new Error('createDom should be called before positioning the rect');
|
|
||||||
}
|
|
||||||
this.markerSvgRect_.setAttribute('x', `${x}`);
|
|
||||||
this.markerSvgRect_.setAttribute('y', `${y}`);
|
|
||||||
this.markerSvgRect_.setAttribute('width', `${width}`);
|
|
||||||
this.markerSvgRect_.setAttribute('height', `${height}`);
|
|
||||||
this.currentMarkerSvg = this.markerSvgRect_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flip the SVG paths in RTL.
|
|
||||||
*
|
|
||||||
* @param markerSvg The marker that we want to flip.
|
|
||||||
*/
|
|
||||||
private flipRtl(markerSvg: SVGElement) {
|
|
||||||
markerSvg.setAttribute('transform', 'scale(-1 1)');
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Hide the marker. */
|
|
||||||
hide() {
|
|
||||||
if (
|
|
||||||
!this.markerSvgLine_ ||
|
|
||||||
!this.markerSvgRect_ ||
|
|
||||||
!this.markerInput_ ||
|
|
||||||
!this.markerBlock_
|
|
||||||
) {
|
|
||||||
throw new Error('createDom should be called before hiding the marker');
|
|
||||||
}
|
|
||||||
this.markerSvgLine_.style.display = 'none';
|
|
||||||
this.markerSvgRect_.style.display = 'none';
|
|
||||||
this.markerInput_.style.display = 'none';
|
|
||||||
this.markerBlock_.style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fire event for the marker or marker.
|
|
||||||
*
|
|
||||||
* @param oldNode The old node the marker used to be on.
|
|
||||||
* @param curNode The new node the marker is currently on.
|
|
||||||
*/
|
|
||||||
protected fireMarkerEvent(oldNode: ASTNode | null, curNode: ASTNode) {
|
|
||||||
const curBlock = curNode.getSourceBlock();
|
|
||||||
const event = new (eventUtils.get(EventType.MARKER_MOVE))(
|
|
||||||
curBlock,
|
|
||||||
this.isCursor(),
|
|
||||||
oldNode,
|
|
||||||
curNode,
|
|
||||||
);
|
|
||||||
eventUtils.fire(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the properties to make a marker blink.
|
|
||||||
*
|
|
||||||
* @returns The object holding attributes to make the marker blink.
|
|
||||||
*/
|
|
||||||
protected getBlinkProperties_(): {[key: string]: string} {
|
|
||||||
return {
|
|
||||||
'attributeType': 'XML',
|
|
||||||
'attributeName': 'fill',
|
|
||||||
'dur': '1s',
|
|
||||||
'values': this.colour_ + ';transparent;transparent;',
|
|
||||||
'repeatCount': 'indefinite',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the marker SVG.
|
|
||||||
*
|
|
||||||
* @returns The SVG node created.
|
|
||||||
*/
|
|
||||||
protected createDomInternal_(): Element {
|
|
||||||
/* This markup will be generated and added to the .svgGroup_:
|
|
||||||
<g>
|
|
||||||
<rect width="100" height="5">
|
|
||||||
<animate attributeType="XML" attributeName="fill" dur="1s"
|
|
||||||
values="transparent;transparent;#fff;transparent"
|
|
||||||
repeatCount="indefinite" />
|
|
||||||
</rect>
|
|
||||||
</g>
|
|
||||||
*/
|
|
||||||
|
|
||||||
this.markerSvg_ = dom.createSvgElement(
|
|
||||||
Svg.G,
|
|
||||||
{
|
|
||||||
'width': this.constants_.CURSOR_WS_WIDTH,
|
|
||||||
'height': this.constants_.WS_CURSOR_HEIGHT,
|
|
||||||
},
|
|
||||||
this.svgGroup_,
|
|
||||||
);
|
|
||||||
|
|
||||||
// A horizontal line used to represent a workspace coordinate or next
|
|
||||||
// connection.
|
|
||||||
this.markerSvgLine_ = dom.createSvgElement(
|
|
||||||
Svg.RECT,
|
|
||||||
{
|
|
||||||
'width': this.constants_.CURSOR_WS_WIDTH,
|
|
||||||
'height': this.constants_.WS_CURSOR_HEIGHT,
|
|
||||||
},
|
|
||||||
this.markerSvg_,
|
|
||||||
);
|
|
||||||
|
|
||||||
// A filled in rectangle used to represent a stack.
|
|
||||||
this.markerSvgRect_ = dom.createSvgElement(
|
|
||||||
Svg.RECT,
|
|
||||||
{
|
|
||||||
'class': 'blocklyVerticalMarker',
|
|
||||||
'rx': 10,
|
|
||||||
'ry': 10,
|
|
||||||
},
|
|
||||||
this.markerSvg_,
|
|
||||||
);
|
|
||||||
|
|
||||||
// A filled in puzzle piece used to represent an input value.
|
|
||||||
this.markerInput_ = dom.createSvgElement(
|
|
||||||
Svg.PATH,
|
|
||||||
{'transform': ''},
|
|
||||||
this.markerSvg_,
|
|
||||||
);
|
|
||||||
|
|
||||||
// A path used to represent a previous connection and a block, an output
|
|
||||||
// connection and a block, or a block.
|
|
||||||
this.markerBlock_ = dom.createSvgElement(
|
|
||||||
Svg.PATH,
|
|
||||||
{
|
|
||||||
'transform': '',
|
|
||||||
'fill': 'none',
|
|
||||||
'stroke-width': this.constants_.CURSOR_STROKE_WIDTH,
|
|
||||||
},
|
|
||||||
this.markerSvg_,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.hide();
|
|
||||||
|
|
||||||
// Markers and stack markers don't blink.
|
|
||||||
if (this.isCursor()) {
|
|
||||||
const blinkProperties = this.getBlinkProperties_();
|
|
||||||
dom.createSvgElement(Svg.ANIMATE, blinkProperties, this.markerSvgLine_);
|
|
||||||
dom.createSvgElement(Svg.ANIMATE, blinkProperties, this.markerInput_);
|
|
||||||
dom.createSvgElement(
|
|
||||||
Svg.ANIMATE,
|
|
||||||
{...blinkProperties, attributeName: 'stroke'},
|
|
||||||
this.markerBlock_,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.markerSvg_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply the marker's colour.
|
|
||||||
*
|
|
||||||
* @param _curNode The node that we want to draw the marker for.
|
|
||||||
*/
|
|
||||||
protected applyColour_(_curNode: ASTNode) {
|
|
||||||
if (
|
|
||||||
!this.markerSvgLine_ ||
|
|
||||||
!this.markerSvgRect_ ||
|
|
||||||
!this.markerInput_ ||
|
|
||||||
!this.markerBlock_
|
|
||||||
) {
|
|
||||||
throw new Error(
|
|
||||||
'createDom should be called before applying color to the markerj',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.markerSvgLine_.setAttribute('fill', this.colour_);
|
|
||||||
this.markerSvgRect_.setAttribute('stroke', this.colour_);
|
|
||||||
this.markerInput_.setAttribute('fill', this.colour_);
|
|
||||||
this.markerBlock_.setAttribute('stroke', this.colour_);
|
|
||||||
|
|
||||||
if (this.isCursor()) {
|
|
||||||
const values = this.colour_ + ';transparent;transparent;';
|
|
||||||
this.markerSvgLine_.firstElementChild!.setAttribute('values', values);
|
|
||||||
this.markerInput_.firstElementChild!.setAttribute('values', values);
|
|
||||||
this.markerBlock_.firstElementChild!.setAttribute('values', values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Dispose of this marker. */
|
|
||||||
dispose() {
|
|
||||||
if (this.svgGroup_) {
|
|
||||||
dom.removeNode(this.svgGroup_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,18 +24,6 @@ export class PathObject implements IPathObject {
|
|||||||
svgRoot: SVGElement;
|
svgRoot: SVGElement;
|
||||||
svgPath: SVGElement;
|
svgPath: SVGElement;
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds the cursors svg element when the cursor is attached to the block.
|
|
||||||
* This is null if there is no cursor on the block.
|
|
||||||
*/
|
|
||||||
cursorSvg: SVGElement | null = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds the markers svg element when the marker is attached to the block.
|
|
||||||
* This is null if there is no marker on the block.
|
|
||||||
*/
|
|
||||||
markerSvg: SVGElement | null = null;
|
|
||||||
|
|
||||||
constants: ConstantProvider;
|
constants: ConstantProvider;
|
||||||
style: BlockStyle;
|
style: BlockStyle;
|
||||||
|
|
||||||
@@ -86,42 +74,6 @@ export class PathObject implements IPathObject {
|
|||||||
this.svgPath.setAttribute('transform', 'scale(-1 1)');
|
this.svgPath.setAttribute('transform', 'scale(-1 1)');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the cursor SVG to this block's SVG group.
|
|
||||||
*
|
|
||||||
* @param cursorSvg The SVG root of the cursor to be added to the block SVG
|
|
||||||
* group.
|
|
||||||
*/
|
|
||||||
setCursorSvg(cursorSvg: SVGElement) {
|
|
||||||
if (!cursorSvg) {
|
|
||||||
this.cursorSvg = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.svgRoot.appendChild(cursorSvg);
|
|
||||||
this.cursorSvg = cursorSvg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the marker SVG to this block's SVG group.
|
|
||||||
*
|
|
||||||
* @param markerSvg The SVG root of the marker to be added to the block SVG
|
|
||||||
* group.
|
|
||||||
*/
|
|
||||||
setMarkerSvg(markerSvg: SVGElement) {
|
|
||||||
if (!markerSvg) {
|
|
||||||
this.markerSvg = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.cursorSvg) {
|
|
||||||
this.svgRoot.insertBefore(markerSvg, this.cursorSvg);
|
|
||||||
} else {
|
|
||||||
this.svgRoot.appendChild(markerSvg);
|
|
||||||
}
|
|
||||||
this.markerSvg = markerSvg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply the stored colours to the block's path, taking into account whether
|
* Apply the stored colours to the block's path, taking into account whether
|
||||||
* the paths belong to a shadow block.
|
* the paths belong to a shadow block.
|
||||||
|
|||||||
@@ -11,14 +11,11 @@ import type {BlockSvg} from '../../block_svg.js';
|
|||||||
import {Connection} from '../../connection.js';
|
import {Connection} from '../../connection.js';
|
||||||
import {ConnectionType} from '../../connection_type.js';
|
import {ConnectionType} from '../../connection_type.js';
|
||||||
import type {IRegistrable} from '../../interfaces/i_registrable.js';
|
import type {IRegistrable} from '../../interfaces/i_registrable.js';
|
||||||
import type {Marker} from '../../keyboard_nav/marker.js';
|
|
||||||
import type {BlockStyle, Theme} from '../../theme.js';
|
import type {BlockStyle, Theme} from '../../theme.js';
|
||||||
import type {WorkspaceSvg} from '../../workspace_svg.js';
|
|
||||||
import {ConstantProvider} from './constants.js';
|
import {ConstantProvider} from './constants.js';
|
||||||
import {Drawer} from './drawer.js';
|
import {Drawer} from './drawer.js';
|
||||||
import type {IPathObject} from './i_path_object.js';
|
import type {IPathObject} from './i_path_object.js';
|
||||||
import {RenderInfo} from './info.js';
|
import {RenderInfo} from './info.js';
|
||||||
import {MarkerSvg} from './marker_svg.js';
|
|
||||||
import {PathObject} from './path_object.js';
|
import {PathObject} from './path_object.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -167,17 +164,6 @@ export class Renderer implements IRegistrable {
|
|||||||
return new Drawer(block, info);
|
return new Drawer(block, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new instance of the renderer's marker drawer.
|
|
||||||
*
|
|
||||||
* @param workspace The workspace the marker belongs to.
|
|
||||||
* @param marker The marker.
|
|
||||||
* @returns The object in charge of drawing the marker.
|
|
||||||
*/
|
|
||||||
makeMarkerDrawer(workspace: WorkspaceSvg, marker: Marker): MarkerSvg {
|
|
||||||
return new MarkerSvg(workspace, this.getConstants(), marker);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance of a renderer path object.
|
* Create a new instance of a renderer path object.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,144 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright 2019 Google LLC
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Former goog.module ID: Blockly.zelos.MarkerSvg
|
|
||||||
|
|
||||||
import type {BlockSvg} from '../../block_svg.js';
|
|
||||||
import type {ASTNode} from '../../keyboard_nav/ast_node.js';
|
|
||||||
import type {Marker} from '../../keyboard_nav/marker.js';
|
|
||||||
import type {RenderedConnection} from '../../rendered_connection.js';
|
|
||||||
import * as dom from '../../utils/dom.js';
|
|
||||||
import {Svg} from '../../utils/svg.js';
|
|
||||||
import type {WorkspaceSvg} from '../../workspace_svg.js';
|
|
||||||
import type {ConstantProvider as BaseConstantProvider} from '../common/constants.js';
|
|
||||||
import {MarkerSvg as BaseMarkerSvg} from '../common/marker_svg.js';
|
|
||||||
import type {ConstantProvider as ZelosConstantProvider} from './constants.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class to draw a marker.
|
|
||||||
*/
|
|
||||||
export class MarkerSvg extends BaseMarkerSvg {
|
|
||||||
// TODO(b/109816955): remove '!', see go/strict-prop-init-fix.
|
|
||||||
constants_!: ZelosConstantProvider;
|
|
||||||
|
|
||||||
private markerCircle: SVGCircleElement | null = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param workspace The workspace the marker belongs to.
|
|
||||||
* @param constants The constants for the renderer.
|
|
||||||
* @param marker The marker to draw.
|
|
||||||
*/
|
|
||||||
constructor(
|
|
||||||
workspace: WorkspaceSvg,
|
|
||||||
constants: BaseConstantProvider,
|
|
||||||
marker: Marker,
|
|
||||||
) {
|
|
||||||
super(workspace, constants, marker);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position and display the marker for an input or an output connection.
|
|
||||||
*
|
|
||||||
* @param curNode The node to draw the marker for.
|
|
||||||
*/
|
|
||||||
private showWithInputOutput(curNode: ASTNode) {
|
|
||||||
const block = curNode.getSourceBlock() as BlockSvg;
|
|
||||||
const connection = curNode.getLocation() as RenderedConnection;
|
|
||||||
const offsetInBlock = connection.getOffsetInBlock();
|
|
||||||
|
|
||||||
this.positionCircle(offsetInBlock.x, offsetInBlock.y);
|
|
||||||
this.setParent_(block);
|
|
||||||
this.showCurrent_();
|
|
||||||
}
|
|
||||||
|
|
||||||
override showWithOutput_(curNode: ASTNode) {
|
|
||||||
this.showWithInputOutput(curNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
override showWithInput_(curNode: ASTNode) {
|
|
||||||
this.showWithInputOutput(curNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draw a rectangle around the block.
|
|
||||||
*
|
|
||||||
* @param curNode The current node of the marker.
|
|
||||||
*/
|
|
||||||
override showWithBlock_(curNode: ASTNode) {
|
|
||||||
const block = curNode.getLocation() as BlockSvg;
|
|
||||||
|
|
||||||
// Gets the height and width of entire stack.
|
|
||||||
const heightWidth = block.getHeightWidth();
|
|
||||||
// Add padding so that being on a stack looks different than being on a
|
|
||||||
// block.
|
|
||||||
this.positionRect_(0, 0, heightWidth.width, heightWidth.height);
|
|
||||||
this.setParent_(block);
|
|
||||||
this.showCurrent_();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position the circle we use for input and output connections.
|
|
||||||
*
|
|
||||||
* @param x The x position of the circle.
|
|
||||||
* @param y The y position of the circle.
|
|
||||||
*/
|
|
||||||
private positionCircle(x: number, y: number) {
|
|
||||||
this.markerCircle?.setAttribute('cx', `${x}`);
|
|
||||||
this.markerCircle?.setAttribute('cy', `${y}`);
|
|
||||||
this.currentMarkerSvg = this.markerCircle;
|
|
||||||
}
|
|
||||||
|
|
||||||
override hide() {
|
|
||||||
super.hide();
|
|
||||||
if (this.markerCircle) {
|
|
||||||
this.markerCircle.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override createDomInternal_() {
|
|
||||||
/* clang-format off */
|
|
||||||
/* This markup will be generated and added to the .svgGroup_:
|
|
||||||
<g>
|
|
||||||
<rect width="100" height="5">
|
|
||||||
<animate attributeType="XML" attributeName="fill" dur="1s"
|
|
||||||
values="transparent;transparent;#fff;transparent" repeatCount="indefinite" />
|
|
||||||
</rect>
|
|
||||||
</g>
|
|
||||||
*/
|
|
||||||
/* clang-format on */
|
|
||||||
super.createDomInternal_();
|
|
||||||
|
|
||||||
this.markerCircle = dom.createSvgElement(
|
|
||||||
Svg.CIRCLE,
|
|
||||||
{
|
|
||||||
'r': this.constants_.CURSOR_RADIUS,
|
|
||||||
'stroke-width': this.constants_.CURSOR_STROKE_WIDTH,
|
|
||||||
},
|
|
||||||
this.markerSvg_,
|
|
||||||
);
|
|
||||||
this.hide();
|
|
||||||
|
|
||||||
// Markers and stack cursors don't blink.
|
|
||||||
if (this.isCursor()) {
|
|
||||||
const blinkProperties = this.getBlinkProperties_();
|
|
||||||
dom.createSvgElement(Svg.ANIMATE, blinkProperties, this.markerCircle!);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.markerSvg_!;
|
|
||||||
}
|
|
||||||
|
|
||||||
override applyColour_(curNode: ASTNode) {
|
|
||||||
super.applyColour_(curNode);
|
|
||||||
|
|
||||||
this.markerCircle?.setAttribute('fill', this.colour_);
|
|
||||||
this.markerCircle?.setAttribute('stroke', this.colour_);
|
|
||||||
|
|
||||||
if (this.isCursor()) {
|
|
||||||
const values = this.colour_ + ';transparent;transparent;';
|
|
||||||
this.markerCircle?.firstElementChild!.setAttribute('values', values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,16 +7,13 @@
|
|||||||
// Former goog.module ID: Blockly.zelos.Renderer
|
// Former goog.module ID: Blockly.zelos.Renderer
|
||||||
|
|
||||||
import type {BlockSvg} from '../../block_svg.js';
|
import type {BlockSvg} from '../../block_svg.js';
|
||||||
import type {Marker} from '../../keyboard_nav/marker.js';
|
|
||||||
import type {BlockStyle} from '../../theme.js';
|
import type {BlockStyle} from '../../theme.js';
|
||||||
import type {WorkspaceSvg} from '../../workspace_svg.js';
|
|
||||||
import * as blockRendering from '../common/block_rendering.js';
|
import * as blockRendering from '../common/block_rendering.js';
|
||||||
import type {RenderInfo as BaseRenderInfo} from '../common/info.js';
|
import type {RenderInfo as BaseRenderInfo} from '../common/info.js';
|
||||||
import {Renderer as BaseRenderer} from '../common/renderer.js';
|
import {Renderer as BaseRenderer} from '../common/renderer.js';
|
||||||
import {ConstantProvider} from './constants.js';
|
import {ConstantProvider} from './constants.js';
|
||||||
import {Drawer} from './drawer.js';
|
import {Drawer} from './drawer.js';
|
||||||
import {RenderInfo} from './info.js';
|
import {RenderInfo} from './info.js';
|
||||||
import {MarkerSvg} from './marker_svg.js';
|
|
||||||
import {PathObject} from './path_object.js';
|
import {PathObject} from './path_object.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,20 +66,6 @@ export class Renderer extends BaseRenderer {
|
|||||||
return new Drawer(block, info as RenderInfo);
|
return new Drawer(block, info as RenderInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new instance of the renderer's cursor drawer.
|
|
||||||
*
|
|
||||||
* @param workspace The workspace the cursor belongs to.
|
|
||||||
* @param marker The marker.
|
|
||||||
* @returns The object in charge of drawing the marker.
|
|
||||||
*/
|
|
||||||
override makeMarkerDrawer(
|
|
||||||
workspace: WorkspaceSvg,
|
|
||||||
marker: Marker,
|
|
||||||
): MarkerSvg {
|
|
||||||
return new MarkerSvg(workspace, this.getConstants(), marker);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance of a renderer path object.
|
* Create a new instance of a renderer path object.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
import {ConstantProvider} from './constants.js';
|
import {ConstantProvider} from './constants.js';
|
||||||
import {Drawer} from './drawer.js';
|
import {Drawer} from './drawer.js';
|
||||||
import {RenderInfo} from './info.js';
|
import {RenderInfo} from './info.js';
|
||||||
import {MarkerSvg} from './marker_svg.js';
|
|
||||||
import {BottomRow} from './measurables/bottom_row.js';
|
import {BottomRow} from './measurables/bottom_row.js';
|
||||||
import {StatementInput} from './measurables/inputs.js';
|
import {StatementInput} from './measurables/inputs.js';
|
||||||
import {RightConnectionShape} from './measurables/row_elements.js';
|
import {RightConnectionShape} from './measurables/row_elements.js';
|
||||||
@@ -23,7 +22,6 @@ export {
|
|||||||
BottomRow,
|
BottomRow,
|
||||||
ConstantProvider,
|
ConstantProvider,
|
||||||
Drawer,
|
Drawer,
|
||||||
MarkerSvg,
|
|
||||||
PathObject,
|
PathObject,
|
||||||
Renderer,
|
Renderer,
|
||||||
RenderInfo,
|
RenderInfo,
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ import {Navigator} from './navigator.js';
|
|||||||
import {Options} from './options.js';
|
import {Options} from './options.js';
|
||||||
import * as Procedures from './procedures.js';
|
import * as Procedures from './procedures.js';
|
||||||
import * as registry from './registry.js';
|
import * as registry from './registry.js';
|
||||||
import * as renderManagement from './render_management.js';
|
|
||||||
import * as blockRendering from './renderers/common/block_rendering.js';
|
import * as blockRendering from './renderers/common/block_rendering.js';
|
||||||
import type {Renderer} from './renderers/common/renderer.js';
|
import type {Renderer} from './renderers/common/renderer.js';
|
||||||
import type {ScrollbarPair} from './scrollbar_pair.js';
|
import type {ScrollbarPair} from './scrollbar_pair.js';
|
||||||
@@ -474,28 +473,6 @@ export class WorkspaceSvg
|
|||||||
return this.componentManager;
|
return this.componentManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the cursor SVG to this workspaces SVG group.
|
|
||||||
*
|
|
||||||
* @param cursorSvg The SVG root of the cursor to be added to the workspace
|
|
||||||
* SVG group.
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
setCursorSvg(cursorSvg: SVGElement) {
|
|
||||||
this.markerManager.setCursorSvg(cursorSvg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the marker SVG to this workspaces SVG group.
|
|
||||||
*
|
|
||||||
* @param markerSvg The SVG root of the marker to be added to the workspace
|
|
||||||
* SVG group.
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
setMarkerSvg(markerSvg: SVGElement) {
|
|
||||||
this.markerManager.setMarkerSvg(markerSvg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the marker with the given ID.
|
* Get the marker with the given ID.
|
||||||
*
|
*
|
||||||
@@ -1340,10 +1317,6 @@ export class WorkspaceSvg
|
|||||||
.flatMap((block) => block.getDescendants(false))
|
.flatMap((block) => block.getDescendants(false))
|
||||||
.filter((block) => block.isInsertionMarker())
|
.filter((block) => block.isInsertionMarker())
|
||||||
.forEach((block) => block.queueRender());
|
.forEach((block) => block.queueRender());
|
||||||
|
|
||||||
renderManagement
|
|
||||||
.finishQueuedRenders()
|
|
||||||
.then(() => void this.markerManager.updateMarkers());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user