refactor: Convert renderer typecheck methods to typeguards. (#8656)

* refactor: Convert renderer typecheck methods to typeguards.

* chore: Revert unintended change.

* chore: Format types.ts.
This commit is contained in:
Aaron Dodson
2025-01-07 14:04:21 -08:00
committed by GitHub
parent 956f272da0
commit 151d21e50e
18 changed files with 189 additions and 128 deletions

View File

@@ -15,7 +15,6 @@ import type {ExternalValueInput} from '../measurables/external_value_input.js';
import type {Field} from '../measurables/field.js';
import type {Icon} from '../measurables/icon.js';
import type {InlineInput} from '../measurables/inline_input.js';
import type {PreviousConnection} from '../measurables/previous_connection.js';
import type {Row} from '../measurables/row.js';
import {Types} from '../measurables/types.js';
import type {ConstantProvider, Notch, PuzzleTab} from './constants.js';
@@ -116,13 +115,8 @@ export class Drawer {
this.outlinePath_ += this.constants_.OUTSIDE_CORNERS.topLeft;
} else if (Types.isRightRoundedCorner(elem)) {
this.outlinePath_ += this.constants_.OUTSIDE_CORNERS.topRight;
} else if (
Types.isPreviousConnection(elem) &&
elem instanceof Connection
) {
this.outlinePath_ += (
(elem as PreviousConnection).shape as Notch
).pathLeft;
} else if (Types.isPreviousConnection(elem)) {
this.outlinePath_ += (elem.shape as Notch).pathLeft;
} else if (Types.isHat(elem)) {
this.outlinePath_ += this.constants_.START_HAT.path;
} else if (Types.isSpacer(elem)) {
@@ -217,7 +211,7 @@ export class Drawer {
let rightCornerYOffset = 0;
let outlinePath = '';
for (let i = elems.length - 1, elem; (elem = elems[i]); i--) {
if (Types.isNextConnection(elem) && elem instanceof Connection) {
if (Types.isNextConnection(elem)) {
outlinePath += (elem.shape as Notch).pathRight;
} else if (Types.isLeftSquareCorner(elem)) {
outlinePath += svgPaths.lineOnAxis('H', bottomRow.xPos);
@@ -269,9 +263,9 @@ export class Drawer {
for (let i = 0, row; (row = this.info_.rows[i]); i++) {
for (let j = 0, elem; (elem = row.elements[j]); j++) {
if (Types.isInlineInput(elem)) {
this.drawInlineInput_(elem as InlineInput);
this.drawInlineInput_(elem);
} else if (Types.isIcon(elem) || Types.isField(elem)) {
this.layoutField_(elem as Field | Icon);
this.layoutField_(elem);
}
}
}
@@ -295,13 +289,13 @@ export class Drawer {
}
if (Types.isIcon(fieldInfo)) {
const icon = (fieldInfo as Icon).icon;
const icon = fieldInfo.icon;
icon.setOffsetInBlock(new Coordinate(xPos, yPos));
if (this.info_.isInsertionMarker) {
icon.hideForInsertionMarker();
}
} else {
const svgGroup = (fieldInfo as Field).field.getSvgRoot()!;
const svgGroup = fieldInfo.field.getSvgRoot()!;
svgGroup.setAttribute(
'transform',
'translate(' + xPos + ',' + yPos + ')' + scale,

View File

@@ -671,20 +671,17 @@ export class RenderInfo {
return row.yPos + elem.height / 2;
}
if (Types.isBottomRow(row)) {
const bottomRow = row as BottomRow;
const baseline =
bottomRow.yPos + bottomRow.height - bottomRow.descenderHeight;
const baseline = row.yPos + row.height - row.descenderHeight;
if (Types.isNextConnection(elem)) {
return baseline + elem.height / 2;
}
return baseline - elem.height / 2;
}
if (Types.isTopRow(row)) {
const topRow = row as TopRow;
if (Types.isHat(elem)) {
return topRow.capline - elem.height / 2;
return row.capline - elem.height / 2;
}
return topRow.capline + elem.height / 2;
return row.capline + elem.height / 2;
}
return row.yPos + row.height / 2;
}

View File

@@ -100,7 +100,7 @@ export class Drawer extends BaseDrawer {
}
override drawInlineInput_(input: InlineInput) {
this.highlighter_.drawInlineInput(input as InlineInput);
this.highlighter_.drawInlineInput(input);
super.drawInlineInput_(input);
}

View File

@@ -14,13 +14,9 @@ import {StatementInput} from '../../inputs/statement_input.js';
import {ValueInput} from '../../inputs/value_input.js';
import {RenderInfo as BaseRenderInfo} from '../common/info.js';
import type {Measurable} from '../measurables/base.js';
import type {BottomRow} from '../measurables/bottom_row.js';
import {ExternalValueInput} from '../measurables/external_value_input.js';
import type {Field} from '../measurables/field.js';
import {InRowSpacer} from '../measurables/in_row_spacer.js';
import type {InputRow} from '../measurables/input_row.js';
import type {Row} from '../measurables/row.js';
import type {TopRow} from '../measurables/top_row.js';
import {Types} from '../measurables/types.js';
import type {ConstantProvider} from './constants.js';
import {InlineInput} from './measurables/inline_input.js';
@@ -150,7 +146,7 @@ export class RenderInfo extends BaseRenderInfo {
override getInRowSpacing_(prev: Measurable | null, next: Measurable | null) {
if (!prev) {
// Between an editable field and the beginning of the row.
if (next && Types.isField(next) && (next as Field).isEditable) {
if (next && Types.isField(next) && next.isEditable) {
return this.constants_.MEDIUM_PADDING;
}
// Inline input at the beginning of the row.
@@ -167,7 +163,7 @@ export class RenderInfo extends BaseRenderInfo {
// Spacing between a non-input and the end of the row or a statement input.
if (!Types.isInput(prev) && (!next || Types.isStatementInput(next))) {
// Between an editable field and the end of the row.
if (Types.isField(prev) && (prev as Field).isEditable) {
if (Types.isField(prev) && prev.isEditable) {
return this.constants_.MEDIUM_PADDING;
}
// Padding at the end of an icon-only row to make the block shape clearer.
@@ -208,7 +204,7 @@ export class RenderInfo extends BaseRenderInfo {
// Spacing between a non-input and an input.
if (!Types.isInput(prev) && next && Types.isInput(next)) {
// Between an editable field and an input.
if (Types.isField(prev) && (prev as Field).isEditable) {
if (Types.isField(prev) && prev.isEditable) {
if (Types.isInlineInput(next)) {
return this.constants_.SMALL_PADDING;
} else if (Types.isExternalInput(next)) {
@@ -233,7 +229,7 @@ export class RenderInfo extends BaseRenderInfo {
// Spacing between an inline input and a field.
if (Types.isInlineInput(prev) && next && Types.isField(next)) {
// Editable field after inline input.
if ((next as Field).isEditable) {
if (next.isEditable) {
return this.constants_.MEDIUM_PADDING;
} else {
// Noneditable field after inline input.
@@ -278,7 +274,7 @@ export class RenderInfo extends BaseRenderInfo {
Types.isField(prev) &&
next &&
Types.isField(next) &&
(prev as Field).isEditable === (next as Field).isEditable
prev.isEditable === next.isEditable
) {
return this.constants_.LARGE_PADDING;
}
@@ -323,20 +319,17 @@ export class RenderInfo extends BaseRenderInfo {
return row.yPos + elem.height / 2;
}
if (Types.isBottomRow(row)) {
const bottomRow = row as BottomRow;
const baseline =
bottomRow.yPos + bottomRow.height - bottomRow.descenderHeight;
const baseline = row.yPos + row.height - row.descenderHeight;
if (Types.isNextConnection(elem)) {
return baseline + elem.height / 2;
}
return baseline - elem.height / 2;
}
if (Types.isTopRow(row)) {
const topRow = row as TopRow;
if (Types.isHat(elem)) {
return topRow.capline - elem.height / 2;
return row.capline - elem.height / 2;
}
return topRow.capline + elem.height / 2;
return row.capline + elem.height / 2;
}
let result = row.yPos;
@@ -370,7 +363,7 @@ export class RenderInfo extends BaseRenderInfo {
rowNextRightEdges.set(row, nextRightEdge);
if (Types.isInputRow(row)) {
if (row.hasStatement) {
this.alignStatementRow_(row as InputRow);
this.alignStatementRow_(row);
}
if (
prevInput &&

View File

@@ -15,6 +15,14 @@ import {Types} from './types.js';
* row.
*/
export class InRowSpacer extends Measurable {
// This field exists solely to structurally distinguish this type from other
// Measurable subclasses. Because this class otherwise has the same fields as
// Measurable, and Typescript doesn't support nominal typing, Typescript will
// consider it and other subclasses in the same situation as being of the same
// type, even if typeguards are used, which could result in Typescript typing
// objects of this class as `never`.
private inRowSpacer: undefined;
/**
* @param constants The rendering constants provider.
* @param width The width of the spacer.

View File

@@ -7,10 +7,7 @@
// Former goog.module ID: Blockly.blockRendering.InputRow
import type {ConstantProvider} from '../common/constants.js';
import {ExternalValueInput} from './external_value_input.js';
import {InputConnection} from './input_connection.js';
import {Row} from './row.js';
import {StatementInput} from './statement_input.js';
import {Types} from './types.js';
/**
@@ -40,12 +37,11 @@ export class InputRow extends Row {
for (let i = 0; i < this.elements.length; i++) {
const elem = this.elements[i];
this.width += elem.width;
if (Types.isInput(elem) && elem instanceof InputConnection) {
if (Types.isStatementInput(elem) && elem instanceof StatementInput) {
if (Types.isInput(elem)) {
if (Types.isStatementInput(elem)) {
connectedBlockWidths += elem.connectedBlockWidth;
} else if (
Types.isExternalInput(elem) &&
elem instanceof ExternalValueInput &&
elem.connectedBlockWidth !== 0
) {
connectedBlockWidths +=

View File

@@ -15,6 +15,14 @@ import {Types} from './types.js';
* collapsed block takes up during rendering.
*/
export class JaggedEdge extends Measurable {
// This field exists solely to structurally distinguish this type from other
// Measurable subclasses. Because this class otherwise has the same fields as
// Measurable, and Typescript doesn't support nominal typing, Typescript will
// consider it and other subclasses in the same situation as being of the same
// type, even if typeguards are used, which could result in Typescript typing
// objects of this class as `never`.
private jaggedEdge: undefined;
/**
* @param constants The rendering constants provider.
*/

View File

@@ -16,6 +16,14 @@ import {Types} from './types.js';
* up during rendering.
*/
export class NextConnection extends Connection {
// This field exists solely to structurally distinguish this type from other
// Measurable subclasses. Because this class otherwise has the same fields as
// Measurable, and Typescript doesn't support nominal typing, Typescript will
// consider it and other subclasses in the same situation as being of the same
// type, even if typeguards are used, which could result in Typescript typing
// objects of this class as `never`.
private nextConnection: undefined;
/**
* @param constants The rendering constants provider.
* @param connectionModel The connection object on the block that this

View File

@@ -16,6 +16,14 @@ import {Types} from './types.js';
* up during rendering.
*/
export class PreviousConnection extends Connection {
// This field exists solely to structurally distinguish this type from other
// Measurable subclasses. Because this class otherwise has the same fields as
// Measurable, and Typescript doesn't support nominal typing, Typescript will
// consider it and other subclasses in the same situation as being of the same
// type, even if typeguards are used, which could result in Typescript typing
// objects of this class as `never`.
private previousConnection: undefined;
/**
* @param constants The rendering constants provider.
* @param connectionModel The connection object on the block that this

View File

@@ -15,6 +15,14 @@ import {Types} from './types.js';
* during rendering.
*/
export class RoundCorner extends Measurable {
// This field exists solely to structurally distinguish this type from other
// Measurable subclasses. Because this class otherwise has the same fields as
// Measurable, and Typescript doesn't support nominal typing, Typescript will
// consider it and other subclasses in the same situation as being of the same
// type, even if typeguards are used, which could result in Typescript typing
// objects of this class as `never`.
private roundCorner: undefined;
/**
* @param constants The rendering constants provider.
* @param opt_position The position of this corner.

View File

@@ -127,7 +127,7 @@ export class Row {
for (let i = this.elements.length - 1; i >= 0; i--) {
const elem = this.elements[i];
if (Types.isInput(elem)) {
return elem as InputConnection;
return elem;
}
}
return null;
@@ -166,8 +166,8 @@ export class Row {
getFirstSpacer(): InRowSpacer | null {
for (let i = 0; i < this.elements.length; i++) {
const elem = this.elements[i];
if (Types.isSpacer(elem)) {
return elem as InRowSpacer;
if (Types.isInRowSpacer(elem)) {
return elem;
}
}
return null;
@@ -181,8 +181,8 @@ export class Row {
getLastSpacer(): InRowSpacer | null {
for (let i = this.elements.length - 1; i >= 0; i--) {
const elem = this.elements[i];
if (Types.isSpacer(elem)) {
return elem as InRowSpacer;
if (Types.isInRowSpacer(elem)) {
return elem;
}
}
return null;

View File

@@ -15,6 +15,14 @@ import {Types} from './types.js';
* during rendering.
*/
export class SquareCorner extends Measurable {
// This field exists solely to structurally distinguish this type from other
// Measurable subclasses. Because this class otherwise has the same fields as
// Measurable, and Typescript doesn't support nominal typing, Typescript will
// consider it and other subclasses in the same situation as being of the same
// type, even if typeguards are used, which could result in Typescript typing
// objects of this class as `never`.
private squareCorner: undefined;
/**
* @param constants The rendering constants provider.
* @param opt_position The position of this corner.

View File

@@ -16,6 +16,14 @@ import {Types} from './types.js';
* during rendering
*/
export class StatementInput extends InputConnection {
// This field exists solely to structurally distinguish this type from other
// Measurable subclasses. Because this class otherwise has the same fields as
// Measurable, and Typescript doesn't support nominal typing, Typescript will
// consider it and other subclasses in the same situation as being of the same
// type, even if typeguards are used, which could result in Typescript typing
// objects of this class as `never`.
private statementInput: undefined;
/**
* @param constants The rendering constants provider.
* @param input The statement input to measure and store information for.

View File

@@ -8,7 +8,6 @@
import type {BlockSvg} from '../../block_svg.js';
import type {ConstantProvider} from '../common/constants.js';
import {Hat} from './hat.js';
import type {PreviousConnection} from './previous_connection.js';
import {Row} from './row.js';
import {Types} from './types.js';
@@ -85,7 +84,7 @@ export class TopRow extends Row {
const elem = this.elements[i];
width += elem.width;
if (!Types.isSpacer(elem)) {
if (Types.isHat(elem) && elem instanceof Hat) {
if (Types.isHat(elem)) {
ascenderHeight = Math.max(ascenderHeight, elem.ascenderHeight);
} else {
height = Math.max(height, elem.height);

View File

@@ -7,7 +7,24 @@
// Former goog.module ID: Blockly.blockRendering.Types
import type {Measurable} from './base.js';
import type {BottomRow} from './bottom_row.js';
import type {ExternalValueInput} from './external_value_input.js';
import type {Field} from './field.js';
import type {Hat} from './hat.js';
import type {Icon} from './icon.js';
import type {InRowSpacer} from './in_row_spacer.js';
import type {InlineInput} from './inline_input.js';
import type {InputConnection} from './input_connection.js';
import type {InputRow} from './input_row.js';
import type {JaggedEdge} from './jagged_edge.js';
import type {NextConnection} from './next_connection.js';
import type {PreviousConnection} from './previous_connection.js';
import type {RoundCorner} from './round_corner.js';
import type {Row} from './row.js';
import type {SpacerRow} from './spacer_row.js';
import type {SquareCorner} from './square_corner.js';
import type {StatementInput} from './statement_input.js';
import type {TopRow} from './top_row.js';
/**
* Types of rendering elements.
@@ -82,8 +99,8 @@ class TypesContainer {
* @param elem The element to check.
* @returns 1 if the object stores information about a field.
*/
isField(elem: Measurable): number {
return elem.type & this.FIELD;
isField(elem: Measurable): elem is Field {
return (elem.type & this.FIELD) >= 1;
}
/**
@@ -92,8 +109,8 @@ class TypesContainer {
* @param elem The element to check.
* @returns 1 if the object stores information about a hat.
*/
isHat(elem: Measurable): number {
return elem.type & this.HAT;
isHat(elem: Measurable): elem is Hat {
return (elem.type & this.HAT) >= 1;
}
/**
@@ -102,8 +119,8 @@ class TypesContainer {
* @param elem The element to check.
* @returns 1 if the object stores information about an icon.
*/
isIcon(elem: Measurable): number {
return elem.type & this.ICON;
isIcon(elem: Measurable): elem is Icon {
return (elem.type & this.ICON) >= 1;
}
/**
@@ -112,8 +129,8 @@ class TypesContainer {
* @param elem The element to check.
* @returns 1 if the object stores information about a spacer.
*/
isSpacer(elem: Measurable | Row): number {
return elem.type & this.SPACER;
isSpacer(elem: Measurable | Row): elem is SpacerRow | InRowSpacer {
return (elem.type & this.SPACER) >= 1;
}
/**
@@ -122,8 +139,18 @@ class TypesContainer {
* @param elem The element to check.
* @returns 1 if the object stores information about an in-row spacer.
*/
isInRowSpacer(elem: Measurable): number {
return elem.type & this.IN_ROW_SPACER;
isInRowSpacer(elem: Measurable): elem is InRowSpacer {
return (elem.type & this.IN_ROW_SPACER) >= 1;
}
/**
* Whether a row is a spacer row.
*
* @param row The row to check.
* @returns True if the row is a spacer row.
*/
isSpacerRow(row: Row): row is SpacerRow {
return (row.type & this.BETWEEN_ROW_SPACER) >= 1;
}
/**
@@ -132,8 +159,8 @@ class TypesContainer {
* @param elem The element to check.
* @returns 1 if the object stores information about an input.
*/
isInput(elem: Measurable): number {
return elem.type & this.INPUT;
isInput(elem: Measurable): elem is InputConnection {
return (elem.type & this.INPUT) >= 1;
}
/**
@@ -142,8 +169,8 @@ class TypesContainer {
* @param elem The element to check.
* @returns 1 if the object stores information about an external input.
*/
isExternalInput(elem: Measurable): number {
return elem.type & this.EXTERNAL_VALUE_INPUT;
isExternalInput(elem: Measurable): elem is ExternalValueInput {
return (elem.type & this.EXTERNAL_VALUE_INPUT) >= 1;
}
/**
@@ -152,8 +179,8 @@ class TypesContainer {
* @param elem The element to check.
* @returns 1 if the object stores information about an inline input.
*/
isInlineInput(elem: Measurable): number {
return elem.type & this.INLINE_INPUT;
isInlineInput(elem: Measurable): elem is InlineInput {
return (elem.type & this.INLINE_INPUT) >= 1;
}
/**
@@ -162,8 +189,8 @@ class TypesContainer {
* @param elem The element to check.
* @returns 1 if the object stores information about a statement input.
*/
isStatementInput(elem: Measurable): number {
return elem.type & this.STATEMENT_INPUT;
isStatementInput(elem: Measurable): elem is StatementInput {
return (elem.type & this.STATEMENT_INPUT) >= 1;
}
/**
@@ -172,8 +199,8 @@ class TypesContainer {
* @param elem The element to check.
* @returns 1 if the object stores information about a previous connection.
*/
isPreviousConnection(elem: Measurable): number {
return elem.type & this.PREVIOUS_CONNECTION;
isPreviousConnection(elem: Measurable): elem is PreviousConnection {
return (elem.type & this.PREVIOUS_CONNECTION) >= 1;
}
/**
@@ -182,8 +209,8 @@ class TypesContainer {
* @param elem The element to check.
* @returns 1 if the object stores information about a next connection.
*/
isNextConnection(elem: Measurable): number {
return elem.type & this.NEXT_CONNECTION;
isNextConnection(elem: Measurable): elem is NextConnection {
return (elem.type & this.NEXT_CONNECTION) >= 1;
}
/**
@@ -194,8 +221,17 @@ class TypesContainer {
* @returns 1 if the object stores information about a previous or next
* connection.
*/
isPreviousOrNextConnection(elem: Measurable): number {
return elem.type & (this.PREVIOUS_CONNECTION | this.NEXT_CONNECTION);
isPreviousOrNextConnection(
elem: Measurable,
): elem is PreviousConnection | NextConnection {
return this.isPreviousConnection(elem) || this.isNextConnection(elem);
}
isRoundCorner(elem: Measurable): elem is RoundCorner {
return (
(elem.type & this.LEFT_ROUND_CORNER) >= 1 ||
(elem.type & this.RIGHT_ROUND_CORNER) >= 1
);
}
/**
@@ -204,8 +240,10 @@ class TypesContainer {
* @param elem The element to check.
* @returns 1 if the object stores information about a left round corner.
*/
isLeftRoundedCorner(elem: Measurable): number {
return elem.type & this.LEFT_ROUND_CORNER;
isLeftRoundedCorner(elem: Measurable): boolean {
return (
this.isRoundCorner(elem) && (elem.type & this.LEFT_ROUND_CORNER) >= 1
);
}
/**
@@ -214,8 +252,10 @@ class TypesContainer {
* @param elem The element to check.
* @returns 1 if the object stores information about a right round corner.
*/
isRightRoundedCorner(elem: Measurable): number {
return elem.type & this.RIGHT_ROUND_CORNER;
isRightRoundedCorner(elem: Measurable): boolean {
return (
this.isRoundCorner(elem) && (elem.type & this.RIGHT_ROUND_CORNER) >= 1
);
}
/**
@@ -224,8 +264,8 @@ class TypesContainer {
* @param elem The element to check.
* @returns 1 if the object stores information about a left square corner.
*/
isLeftSquareCorner(elem: Measurable): number {
return elem.type & this.LEFT_SQUARE_CORNER;
isLeftSquareCorner(elem: Measurable): boolean {
return (elem.type & this.LEFT_SQUARE_CORNER) >= 1;
}
/**
@@ -234,8 +274,8 @@ class TypesContainer {
* @param elem The element to check.
* @returns 1 if the object stores information about a right square corner.
*/
isRightSquareCorner(elem: Measurable): number {
return elem.type & this.RIGHT_SQUARE_CORNER;
isRightSquareCorner(elem: Measurable): boolean {
return (elem.type & this.RIGHT_SQUARE_CORNER) >= 1;
}
/**
@@ -244,8 +284,8 @@ class TypesContainer {
* @param elem The element to check.
* @returns 1 if the object stores information about a corner.
*/
isCorner(elem: Measurable): number {
return elem.type & this.CORNER;
isCorner(elem: Measurable): elem is SquareCorner | RoundCorner {
return (elem.type & this.CORNER) >= 1;
}
/**
@@ -254,8 +294,8 @@ class TypesContainer {
* @param elem The element to check.
* @returns 1 if the object stores information about a jagged edge.
*/
isJaggedEdge(elem: Measurable): number {
return elem.type & this.JAGGED_EDGE;
isJaggedEdge(elem: Measurable): elem is JaggedEdge {
return (elem.type & this.JAGGED_EDGE) >= 1;
}
/**
@@ -264,8 +304,8 @@ class TypesContainer {
* @param row The row to check.
* @returns 1 if the object stores information about a row.
*/
isRow(row: Row): number {
return row.type & this.ROW;
isRow(row: Row): row is Row {
return (row.type & this.ROW) >= 1;
}
/**
@@ -274,8 +314,8 @@ class TypesContainer {
* @param row The row to check.
* @returns 1 if the object stores information about a between-row spacer.
*/
isBetweenRowSpacer(row: Row): number {
return row.type & this.BETWEEN_ROW_SPACER;
isBetweenRowSpacer(row: Row): row is SpacerRow {
return (row.type & this.BETWEEN_ROW_SPACER) >= 1;
}
/**
@@ -284,8 +324,8 @@ class TypesContainer {
* @param row The row to check.
* @returns 1 if the object stores information about a top row.
*/
isTopRow(row: Row): number {
return row.type & this.TOP_ROW;
isTopRow(row: Row): row is TopRow {
return (row.type & this.TOP_ROW) >= 1;
}
/**
@@ -294,8 +334,8 @@ class TypesContainer {
* @param row The row to check.
* @returns 1 if the object stores information about a bottom row.
*/
isBottomRow(row: Row): number {
return row.type & this.BOTTOM_ROW;
isBottomRow(row: Row): row is BottomRow {
return (row.type & this.BOTTOM_ROW) >= 1;
}
/**
@@ -304,8 +344,8 @@ class TypesContainer {
* @param row The row to check.
* @returns 1 if the object stores information about a top or bottom row.
*/
isTopOrBottomRow(row: Row): number {
return row.type & (this.TOP_ROW | this.BOTTOM_ROW);
isTopOrBottomRow(row: Row): row is TopRow | BottomRow {
return this.isTopRow(row) || this.isBottomRow(row);
}
/**
@@ -314,8 +354,8 @@ class TypesContainer {
* @param row The row to check.
* @returns 1 if the object stores information about an input row.
*/
isInputRow(row: Row): number {
return row.type & this.INPUT_ROW;
isInputRow(row: Row): row is InputRow {
return (row.type & this.INPUT_ROW) >= 1;
}
}

View File

@@ -9,11 +9,8 @@
import type {BlockSvg} from '../../block_svg.js';
import {RenderInfo as BaseRenderInfo} from '../common/info.js';
import type {Measurable} from '../measurables/base.js';
import type {BottomRow} from '../measurables/bottom_row.js';
import type {Field} from '../measurables/field.js';
import {InRowSpacer} from '../measurables/in_row_spacer.js';
import type {Row} from '../measurables/row.js';
import type {TopRow} from '../measurables/top_row.js';
import {Types} from '../measurables/types.js';
import type {Renderer} from './renderer.js';
@@ -94,7 +91,7 @@ export class RenderInfo extends BaseRenderInfo {
override getInRowSpacing_(prev: Measurable | null, next: Measurable | null) {
if (!prev) {
// Between an editable field and the beginning of the row.
if (next && Types.isField(next) && (next as Field).isEditable) {
if (next && Types.isField(next) && next.isEditable) {
return this.constants_.MEDIUM_PADDING;
}
// Inline input at the beginning of the row.
@@ -111,7 +108,7 @@ export class RenderInfo extends BaseRenderInfo {
// Spacing between a non-input and the end of the row.
if (!Types.isInput(prev) && !next) {
// Between an editable field and the end of the row.
if (Types.isField(prev) && (prev as Field).isEditable) {
if (Types.isField(prev) && prev.isEditable) {
return this.constants_.MEDIUM_PADDING;
}
// Padding at the end of an icon-only row to make the block shape clearer.
@@ -151,7 +148,7 @@ export class RenderInfo extends BaseRenderInfo {
// Spacing between a non-input and an input.
if (!Types.isInput(prev) && next && Types.isInput(next)) {
// Between an editable field and an input.
if (Types.isField(prev) && (prev as Field).isEditable) {
if (Types.isField(prev) && prev.isEditable) {
if (Types.isInlineInput(next)) {
return this.constants_.SMALL_PADDING;
} else if (Types.isExternalInput(next)) {
@@ -177,7 +174,7 @@ export class RenderInfo extends BaseRenderInfo {
// Spacing between an inline input and a field.
if (Types.isInlineInput(prev) && next && Types.isField(next)) {
// Editable field after inline input.
if ((next as Field).isEditable) {
if (next.isEditable) {
return this.constants_.MEDIUM_PADDING;
} else {
// Noneditable field after inline input.
@@ -205,7 +202,7 @@ export class RenderInfo extends BaseRenderInfo {
Types.isField(prev) &&
next &&
Types.isField(next) &&
(prev as Field).isEditable === (next as Field).isEditable
prev.isEditable === next.isEditable
) {
return this.constants_.LARGE_PADDING;
}
@@ -247,20 +244,17 @@ export class RenderInfo extends BaseRenderInfo {
return row.yPos + elem.height / 2;
}
if (Types.isBottomRow(row)) {
const bottomRow = row as BottomRow;
const baseline =
bottomRow.yPos + bottomRow.height - bottomRow.descenderHeight;
const baseline = row.yPos + row.height - row.descenderHeight;
if (Types.isNextConnection(elem)) {
return baseline + elem.height / 2;
}
return baseline - elem.height / 2;
}
if (Types.isTopRow(row)) {
const topRow = row as TopRow;
if (Types.isHat(elem)) {
return topRow.capline - elem.height / 2;
return row.capline - elem.height / 2;
}
return topRow.capline + elem.height / 2;
return row.capline + elem.height / 2;
}
let result = row.yPos;

View File

@@ -15,7 +15,6 @@ 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';
import type {SpacerRow} from '../measurables/spacer_row.js';
import {Types} from '../measurables/types.js';
import type {InsideCorners} from './constants.js';
import type {RenderInfo} from './info.js';
@@ -96,20 +95,19 @@ export class Drawer extends BaseDrawer {
return;
}
if (Types.isSpacer(row)) {
const spacerRow = row as SpacerRow;
const precedesStatement = spacerRow.precedesStatement;
const followsStatement = spacerRow.followsStatement;
const precedesStatement = row.precedesStatement;
const followsStatement = row.followsStatement;
if (precedesStatement || followsStatement) {
const insideCorners = this.constants_.INSIDE_CORNERS as InsideCorners;
const cornerHeight = insideCorners.rightHeight;
const remainingHeight =
spacerRow.height - (precedesStatement ? cornerHeight : 0);
row.height - (precedesStatement ? cornerHeight : 0);
const bottomRightPath = followsStatement
? insideCorners.pathBottomRight
: '';
const verticalPath =
remainingHeight > 0
? svgPaths.lineOnAxis('V', spacerRow.yPos + remainingHeight)
? svgPaths.lineOnAxis('V', row.yPos + remainingHeight)
: '';
const topRightPath = precedesStatement
? insideCorners.pathTopRight

View File

@@ -20,7 +20,6 @@ import {RenderInfo as BaseRenderInfo} from '../common/info.js';
import type {Measurable} from '../measurables/base.js';
import {Field} from '../measurables/field.js';
import {InRowSpacer} from '../measurables/in_row_spacer.js';
import {InputConnection} from '../measurables/input_connection.js';
import type {Row} from '../measurables/row.js';
import type {SpacerRow} from '../measurables/spacer_row.js';
import {Types} from '../measurables/types.js';
@@ -207,9 +206,8 @@ export class RenderInfo extends BaseRenderInfo {
}
// Top and bottom rows act as a spacer so we don't need any extra padding.
if (Types.isTopRow(prev)) {
const topRow = prev as TopRow;
if (
!topRow.hasPreviousConnection &&
!prev.hasPreviousConnection &&
(!this.outputConnection || this.hasStatementInput)
) {
return Math.abs(
@@ -219,7 +217,6 @@ export class RenderInfo extends BaseRenderInfo {
return this.constants_.NO_PADDING;
}
if (Types.isBottomRow(next)) {
const bottomRow = next as BottomRow;
if (!this.outputConnection) {
const topHeight =
Math.max(
@@ -230,7 +227,7 @@ export class RenderInfo extends BaseRenderInfo {
),
) - this.constants_.CORNER_RADIUS;
return topHeight;
} else if (!bottomRow.hasNextConnection && this.hasStatementInput) {
} else if (!next.hasNextConnection && this.hasStatementInput) {
return Math.abs(
this.constants_.NOTCH_HEIGHT - this.constants_.CORNER_RADIUS,
);
@@ -259,7 +256,7 @@ export class RenderInfo extends BaseRenderInfo {
) {
return row.yPos + this.constants_.EMPTY_STATEMENT_INPUT_HEIGHT / 2;
}
if (Types.isInlineInput(elem) && elem instanceof InputConnection) {
if (Types.isInlineInput(elem)) {
const connectedBlock = elem.connectedBlock;
if (
connectedBlock &&
@@ -308,7 +305,6 @@ export class RenderInfo extends BaseRenderInfo {
}
if (
Types.isField(elem) &&
elem instanceof Field &&
elem.parentInput === this.rightAlignedDummyInputs.get(row)
) {
break;
@@ -371,7 +367,6 @@ export class RenderInfo extends BaseRenderInfo {
xCursor < minXPos &&
!(
Types.isField(elem) &&
elem instanceof Field &&
(elem.field instanceof FieldLabel ||
elem.field instanceof FieldImage)
)
@@ -525,7 +520,7 @@ export class RenderInfo extends BaseRenderInfo {
return 0;
}
}
if (Types.isInlineInput(elem) && elem instanceof InputConnection) {
if (Types.isInlineInput(elem)) {
const connectedBlock = elem.connectedBlock;
const innerShape = connectedBlock
? (connectedBlock.pathObject as PathObject).outputShapeType
@@ -552,7 +547,7 @@ export class RenderInfo extends BaseRenderInfo {
connectionWidth -
this.constants_.SHAPE_IN_SHAPE_PADDING[outerShape][innerShape]
);
} else if (Types.isField(elem) && elem instanceof Field) {
} else if (Types.isField(elem)) {
// Special case for text inputs.
if (
outerShape === constants.SHAPES.ROUND &&
@@ -616,7 +611,6 @@ export class RenderInfo extends BaseRenderInfo {
for (let j = 0; j < row.elements.length; j++) {
const elem = row.elements[j];
if (
elem instanceof InputConnection &&
Types.isInlineInput(elem) &&
elem.connectedBlock &&
!elem.connectedBlock.isShadow() &&