feat: break input types into separate classes (#7019)

* chore: move input and input types into new directory

* feat: define and export new input types

* feat: modify blocks to construct individual inputs

* chore: transition code to use actual type checks

* chore: fixup input type type

* chore: format

* chore: fixup PR comments

* chore: fix build
This commit is contained in:
Beka Westberg
2023-05-04 08:50:45 -07:00
committed by GitHub
parent bf7910986e
commit 3a9a9bd24e
31 changed files with 243 additions and 169 deletions

View File

@@ -14,7 +14,7 @@ goog.declareModuleId('Blockly.libraryBlocks.lists');
import * as fieldRegistry from '../core/field_registry.js';
import * as xmlUtils from '../core/utils/xml.js';
import {Align} from '../core/input.js';
import {Align} from '../core/inputs/input.js';
import type {Block} from '../core/block.js';
import type {Connection} from '../core/connection.js';
import type {BlockSvg} from '../core/block_svg.js';
@@ -26,6 +26,7 @@ import {Mutator} from '../core/mutator.js';
import type {Workspace} from '../core/workspace.js';
import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js';
import '../core/field_dropdown.js';
import {ValueInput} from '../core/inputs/value_input.js';
/**
@@ -185,23 +186,24 @@ const LISTS_CREATE_WITH = {
* @param workspace Mutator's workspace.
* @return Root block in mutator.
*/
decompose: function(this: CreateWithBlock, workspace: Workspace): ContainerBlock {
const containerBlock =
workspace.newBlock('lists_create_with_container') as ContainerBlock;
(containerBlock as BlockSvg).initSvg();
let connection = containerBlock.getInput('STACK')!.connection;
for (let i = 0; i < this.itemCount_; i++) {
const itemBlock =
workspace.newBlock('lists_create_with_item') as ItemBlock;
(itemBlock as BlockSvg).initSvg();
if (!itemBlock.previousConnection) {
throw new Error('itemBlock has no previousConnection');
}
connection!.connect(itemBlock.previousConnection);
connection = itemBlock.nextConnection;
}
return containerBlock;
},
decompose: function(this: CreateWithBlock, workspace: Workspace):
ContainerBlock {
const containerBlock =
workspace.newBlock('lists_create_with_container') as ContainerBlock;
(containerBlock as BlockSvg).initSvg();
let connection = containerBlock.getInput('STACK')!.connection;
for (let i = 0; i < this.itemCount_; i++) {
const itemBlock =
workspace.newBlock('lists_create_with_item') as ItemBlock;
(itemBlock as BlockSvg).initSvg();
if (!itemBlock.previousConnection) {
throw new Error('itemBlock has no previousConnection');
}
connection!.connect(itemBlock.previousConnection);
connection = itemBlock.nextConnection;
}
return containerBlock;
},
/**
* Reconfigure this block based on the mutator dialog's components.
*
@@ -478,7 +480,7 @@ const LISTS_GETINDEX = {
const container = xmlUtils.createElement('mutation');
const isStatement = !this.outputConnection;
container.setAttribute('statement', String(isStatement));
const isAt = this.getInput('AT')!.type === ConnectionType.INPUT_VALUE;
const isAt = this.getInput('AT') instanceof ValueInput;
container.setAttribute('at', String(isAt));
return container;
},
@@ -689,7 +691,7 @@ const LISTS_SETINDEX = {
*/
mutationToDom: function(this: SetIndexBlock): Element {
const container = xmlUtils.createElement('mutation');
const isAt = this.getInput('AT')!.type === ConnectionType.INPUT_VALUE;
const isAt = this.getInput('AT') instanceof ValueInput;
container.setAttribute('at', String(isAt));
return container;
},
@@ -821,9 +823,9 @@ const LISTS_GETSUBLIST = {
*/
mutationToDom: function(this: GetSublistBlock): Element {
const container = xmlUtils.createElement('mutation');
const isAt1 = this.getInput('AT1')!.type === ConnectionType.INPUT_VALUE;
const isAt1 = this.getInput('AT1') instanceof ValueInput;
container.setAttribute('at1', String(isAt1));
const isAt2 = this.getInput('AT2')!.type === ConnectionType.INPUT_VALUE;
const isAt2 = this.getInput('AT2') instanceof ValueInput;
container.setAttribute('at2', String(isAt2));
return container;
},

View File

@@ -324,16 +324,17 @@ const CONTROL_FLOW_IN_LOOP_CHECK_MIXIN = {
*
* @returns The nearest surrounding loop, or null if none.
*/
getSurroundLoop: function(this: ControlFlowInLoopBlock): Block | null {
let block: Block|null = this;
do {
if (loopTypes.has(block.type)) {
return block;
}
block = block.getSurroundParent();
} while (block);
return null;
},
getSurroundLoop: function(this: ControlFlowInLoopBlock): Block |
null {
let block: Block|null = this;
do {
if (loopTypes.has(block.type)) {
return block;
}
block = block.getSurroundParent();
} while (block);
return null;
},
/**
* Called whenever anything on the workspace changes.

View File

@@ -433,7 +433,8 @@ Extensions.register(
/** Type of a block that has IS_DIVISBLEBY_MUTATOR_MIXIN */
type DivisiblebyBlock = Block&DivisiblebyMixin;
interface DivisiblebyMixin extends DivisiblebyMixinType {};
interface DivisiblebyMixin extends DivisiblebyMixinType {}
;
type DivisiblebyMixinType = typeof IS_DIVISIBLEBY_MUTATOR_MIXIN;
/**
@@ -517,7 +518,8 @@ Extensions.register(
/** Type of a block that has LIST_MODES_MUTATOR_MIXIN */
type ListModesBlock = Block&ListModesMixin;
interface ListModesMixin extends ListModesMixinType {};
interface ListModesMixin extends ListModesMixinType {}
;
type ListModesMixinType = typeof LIST_MODES_MUTATOR_MIXIN;
/**
@@ -571,11 +573,11 @@ const LIST_MODES_MUTATOR_MIXIN = {
* modes operation (outputs a list of numbers).
*/
const LIST_MODES_MUTATOR_EXTENSION = function(this: ListModesBlock) {
this.getField('OP')!.setValidator(
function(this: ListModesBlock, newOp: string) {
this.updateType_(newOp);
return undefined;
}.bind(this));
this.getField(
'OP')!.setValidator(function(this: ListModesBlock, newOp: string) {
this.updateType_(newOp);
return undefined;
}.bind(this));
};
Extensions.registerMutator(

View File

@@ -14,7 +14,7 @@ goog.declareModuleId('Blockly.libraryBlocks.texts');
import * as Extensions from '../core/extensions.js';
import * as fieldRegistry from '../core/field_registry.js';
import * as xmlUtils from '../core/utils/xml.js';
import {Align} from '../core/input.js';
import {Align} from '../core/inputs/input.js';
import type {Block} from '../core/block.js';
import type {BlockSvg} from '../core/block_svg.js';
import {Connection} from '../core/connection.js';
@@ -28,6 +28,7 @@ import type {Workspace} from '../core/workspace.js';
import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js';
import '../core/field_multilineinput.js';
import '../core/field_variable.js';
import { ValueInput } from '../core/inputs/value_input.js';
/**
@@ -284,9 +285,9 @@ const GET_SUBSTRING_BLOCK = {
*/
mutationToDom: function(this: GetSubstringBlock): Element {
const container = xmlUtils.createElement('mutation');
const isAt1 = this.getInput('AT1')!.type === ConnectionType.INPUT_VALUE;
const isAt1 = this.getInput('AT1') instanceof ValueInput;
container.setAttribute('at1', `${isAt1}`);
const isAt2 = this.getInput('AT2')!.type === ConnectionType.INPUT_VALUE;
const isAt2 = this.getInput('AT2') instanceof ValueInput;
container.setAttribute('at2', `${isAt2}`);
return container;
},

View File

@@ -31,8 +31,7 @@ import * as eventUtils from './events/utils.js';
import * as Extensions from './extensions.js';
import type {Field} from './field.js';
import * as fieldRegistry from './field_registry.js';
import {Align, Input} from './input.js';
import {inputTypes} from './input_types.js';
import {Align, Input} from './inputs/input.js';
import type {IASTNodeLocation} from './interfaces/i_ast_node_location.js';
import type {IDeletable} from './interfaces/i_deletable.js';
import type {Mutator} from './mutator.js';
@@ -45,6 +44,9 @@ import * as registry from './registry.js';
import {Size} from './utils/size.js';
import type {VariableModel} from './variable_model.js';
import type {Workspace} from './workspace.js';
import {DummyInput} from './inputs/dummy_input.js';
import {ValueInput} from './inputs/value_input.js';
import {StatementInput} from './inputs/statement_input.js';
/**
@@ -1298,15 +1300,15 @@ export class Block implements IASTNodeLocation, IDeletable {
}
// Not defined explicitly. Figure out what would look best.
for (let i = 1; i < this.inputList.length; i++) {
if (this.inputList[i - 1].type === inputTypes.DUMMY &&
this.inputList[i].type === inputTypes.DUMMY) {
if (this.inputList[i - 1] instanceof DummyInput &&
this.inputList[i] instanceof DummyInput) {
// Two dummy inputs in a row. Don't inline them.
return false;
}
}
for (let i = 1; i < this.inputList.length; i++) {
if (this.inputList[i - 1].type === inputTypes.VALUE &&
this.inputList[i].type === inputTypes.DUMMY) {
if (this.inputList[i - 1] instanceof ValueInput &&
this.inputList[i] instanceof DummyInput) {
// Dummy input after a value input. Inline them.
return true;
}
@@ -1490,7 +1492,8 @@ export class Block implements IASTNodeLocation, IDeletable {
* @returns The input object created.
*/
appendValueInput(name: string): Input {
return this.appendInput_(inputTypes.VALUE, name);
return this.appendInput(new ValueInput(
name, this, this.makeConnection_(ConnectionType.INPUT_VALUE)));
}
/**
@@ -1501,18 +1504,20 @@ export class Block implements IASTNodeLocation, IDeletable {
* @returns The input object created.
*/
appendStatementInput(name: string): Input {
return this.appendInput_(inputTypes.STATEMENT, name);
this.statementInputCount++;
return this.appendInput(new StatementInput(
name, this, this.makeConnection_(ConnectionType.NEXT_STATEMENT)));
}
/**
* Appends a dummy input row.
*
* @param opt_name Language-neutral identifier which may used to find this
* input again. Should be unique to this block.
* @param name Optional language-neutral identifier which may used to find
* this input again. Should be unique to this block.
* @returns The input object created.
*/
appendDummyInput(opt_name?: string): Input {
return this.appendInput_(inputTypes.DUMMY, opt_name || '');
appendDummyInput(name = ''): Input {
return this.appendInput(new DummyInput(name, this));
}
/**
@@ -1538,8 +1543,7 @@ export class Block implements IASTNodeLocation, IDeletable {
const inputConstructor =
registry.getClass(registry.Type.INPUT, type, false);
if (!inputConstructor) return null;
return this.appendInput(
new inputConstructor(inputTypes.CUSTOM, name, this, null));
return this.appendInput(new inputConstructor(name, this, null));
}
/**
@@ -1934,28 +1938,6 @@ export class Block implements IASTNodeLocation, IDeletable {
return null;
}
/**
* Add a value input, statement input or local variable to this block.
*
* @param type One of Blockly.inputTypes.
* @param name Language-neutral identifier which may used to find this input
* again. Should be unique to this block.
* @returns The input object created.
*/
protected appendInput_(type: number, name: string): Input {
let connection = null;
if (type === inputTypes.VALUE || type === inputTypes.STATEMENT) {
connection = this.makeConnection_(type);
}
if (type === inputTypes.STATEMENT) {
this.statementInputCount++;
}
const input = new Input(type, name, this, connection);
// Append input to list.
this.inputList.push(input);
return input;
}
/**
* Move a named input to a different location on this block.
*
@@ -2031,9 +2013,7 @@ export class Block implements IASTNodeLocation, IDeletable {
removeInput(name: string, opt_quiet?: boolean): boolean {
for (let i = 0, input; input = this.inputList[i]; i++) {
if (input.name === name) {
if (input.type === inputTypes.STATEMENT) {
this.statementInputCount--;
}
if (input instanceof StatementInput) this.statementInputCount--;
input.dispose();
this.inputList.splice(i, 1);
return true;

View File

@@ -31,7 +31,7 @@ import * as eventUtils from './events/utils.js';
import type {Field} from './field.js';
import {FieldLabel} from './field_label.js';
import type {Icon} from './icon.js';
import type {Input} from './input.js';
import type {Input} from './inputs/input.js';
import type {IASTNodeLocationSvg} from './interfaces/i_ast_node_location_svg.js';
import type {IBoundedElement} from './interfaces/i_bounded_element.js';
import type {CopyData, ICopyable} from './interfaces/i_copyable.js';
@@ -1342,16 +1342,9 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg,
}
}
/**
* Add a value input, statement input or local variable to this block.
*
* @param type One of Blockly.inputTypes.
* @param name Language-neutral identifier which may used to find this input
* again. Should be unique to this block.
* @returns The input object created.
*/
protected override appendInput_(type: number, name: string): Input {
const input = super.appendInput_(type, name);
/** @override */
override appendInput(input: Input): Input {
super.appendInput(input);
if (this.rendered) {
this.queueRender();

View File

@@ -71,8 +71,11 @@ import {Gesture} from './gesture.js';
import {Grid} from './grid.js';
import {Icon} from './icon.js';
import {inject} from './inject.js';
import {Align, Input} from './input.js';
import {inputTypes} from './input_types.js';
import {Align, Input} from './inputs/input.js';
import {inputTypes} from './inputs/input_types.js';
import {DummyInput} from './inputs/dummy_input.js';
import {StatementInput} from './inputs/statement_input.js';
import {ValueInput} from './inputs/value_input.js';
import {InsertionMarkerManager} from './insertion_marker_manager.js';
import {IASTNodeLocation} from './interfaces/i_ast_node_location.js';
import {IASTNodeLocationSvg} from './interfaces/i_ast_node_location_svg.js';
@@ -692,6 +695,13 @@ export {IKeyboardAccessible};
export {IMetricsManager};
export {IMovable};
export {Input};
export const inputs = {
Input,
inputTypes,
DummyInput,
StatementInput,
ValueInput,
};
export {InsertionMarkerManager};
export {IObservable, isObservable};
export {IPositionable};

View File

@@ -16,7 +16,7 @@ import type {Block} from './block.js';
import {ConnectionType} from './connection_type.js';
import type {BlockMove} from './events/events_block_move.js';
import * as eventUtils from './events/utils.js';
import type {Input} from './input.js';
import type {Input} from './inputs/input.js';
import type {IASTNodeLocationWithBlock} from './interfaces/i_ast_node_location_with_block.js';
import type {IConnectionChecker} from './interfaces/i_connection_checker.js';
import * as blocks from './serialization/blocks.js';

View File

@@ -13,8 +13,8 @@ import {ContextMenuRegistry, RegistryItem, Scope} from './contextmenu_registry.j
import * as dialog from './dialog.js';
import * as Events from './events/events.js';
import * as eventUtils from './events/utils.js';
import {inputTypes} from './input_types.js';
import {Msg} from './msg.js';
import {StatementInput} from './renderers/zelos/zelos.js';
import type {WorkspaceSvg} from './workspace_svg.js';
@@ -386,8 +386,8 @@ export function registerInline() {
for (let i = 1; i < block!.inputList.length; i++) {
// Only display this option if there are two value or dummy inputs
// next to each other.
if (block!.inputList[i - 1].type !== inputTypes.STATEMENT &&
block!.inputList[i].type !== inputTypes.STATEMENT) {
if (!(block!.inputList[i - 1] instanceof StatementInput) &&
!(block!.inputList[i] instanceof StatementInput)) {
return 'enabled';
}
}

View File

@@ -22,7 +22,7 @@ import type {BlockSvg} from './block_svg.js';
import * as browserEvents from './browser_events.js';
import * as dropDownDiv from './dropdowndiv.js';
import * as eventUtils from './events/utils.js';
import type {Input} from './input.js';
import type {Input} from './inputs/input.js';
import type {IASTNodeLocationSvg} from './interfaces/i_ast_node_location_svg.js';
import type {IASTNodeLocationWithBlock} from './interfaces/i_ast_node_location_with_block.js';
import type {IKeyboardAccessible} from './interfaces/i_keyboard_accessible.js';

View File

@@ -0,0 +1,24 @@
/**
* @license
* Copyright 2023 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import type {Block} from '../block.js';
import {Input} from './input.js';
import {inputTypes} from './input_types.js';
/** Represents an input on a block with no connection. */
export class DummyInput extends Input {
readonly type = inputTypes.DUMMY;
/**
* @param name Language-neutral identifier which may used to find this input
* again.
* @param block The block containing this input.
*/
constructor(public name: string, block: Block) {
super(name, block, null);
}
}

View File

@@ -9,26 +9,25 @@
*
* @class
*/
import * as goog from '../closure/goog/goog.js';
import * as goog from '../../closure/goog/goog.js';
goog.declareModuleId('Blockly.Input');
// Unused import preserved for side-effects. Remove if unneeded.
import './field_label.js';
import '../field_label.js';
import type {Block} from './block.js';
import type {BlockSvg} from './block_svg.js';
import type {Connection} from './connection.js';
import type {Field} from './field.js';
import * as fieldRegistry from './field_registry.js';
import type {Block} from '../block.js';
import type {BlockSvg} from '../block_svg.js';
import type {Connection} from '../connection.js';
import type {Field} from '../field.js';
import * as fieldRegistry from '../field_registry.js';
import type {RenderedConnection} from '../rendered_connection.js';
import {inputTypes} from './input_types.js';
import type {RenderedConnection} from './rendered_connection.js';
/**
* Class for an input with an optional field.
*/
export class Input {
private sourceBlock: Block;
fieldRow: Field[] = [];
/** Alignment of input's fields (left, right or centre). */
align = Align.LEFT;
@@ -36,24 +35,19 @@ export class Input {
/** Is the input visible? */
private visible = true;
public readonly type: inputTypes = inputTypes.CUSTOM;
/**
* @param type The type of the input.
* @param name Language-neutral identifier which may used to find this input
* again.
* @param block The block containing this input.
* @param sourceBlock The block containing this input.
* @param connection Optional connection for this input. If this is a custom
* input, `null` will always be passed, and then the subclass can
* optionally construct a connection.
*/
constructor(
public type: number, public name: string, block: Block,
public connection: Connection|null) {
if ((type === inputTypes.VALUE || type === inputTypes.STATEMENT) && !name) {
throw Error(
'Value inputs and statement inputs must have non-empty name.');
}
this.sourceBlock = block;
}
public name: string, private sourceBlock: Block,
public connection: Connection|null) {}
/**
* Get the source block for this input.

View File

@@ -4,10 +4,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
import * as goog from '../closure/goog/goog.js';
import * as goog from '../../closure/goog/goog.js';
goog.declareModuleId('Blockly.inputTypes');
import {ConnectionType} from './connection_type.js';
import {ConnectionType} from '../connection_type.js';
/**

View File

@@ -0,0 +1,30 @@
/**
* @license
* Copyright 2023 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import type {Block} from '../block.js';
import type {Connection} from '../connection.js';
import {Input} from './input.js';
import {inputTypes} from './input_types.js';
/** Represents an input on a block with a statement connection. */
export class StatementInput extends Input {
readonly type = inputTypes.STATEMENT;
/**
* @param name Language-neutral identifier which may used to find this input
* again.
* @param block The block containing this input.
* @param connection The statement connection for this input.
*/
constructor(
public name: string, block: Block, public connection: Connection) {
// Errors are maintained for people not using typescript.
if (!name) throw new Error('Statement inputs must have a non-empty name');
if (!connection) throw new Error('Value inputs must have a connection');
super(name, block, connection);
}
}

View File

@@ -0,0 +1,30 @@
/**
* @license
* Copyright 2023 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import type {Block} from '../block.js';
import type {Connection} from '../connection.js';
import {Input} from './input.js';
import {inputTypes} from './input_types.js';
/** Represents an input on a block with a value connection. */
export class ValueInput extends Input {
readonly type = inputTypes.VALUE;
/**
* @param name Language-neutral identifier which may used to find this input
* again.
* @param block The block containing this input.
* @param connection The value connection for this input.
*/
constructor(
public name: string, block: Block, public connection: Connection) {
// Errors are maintained for people not using typescript.
if (!name) throw new Error('Value inputs must have a non-empty name');
if (!connection) throw new Error('Value inputs must have a connection');
super(name, block, connection);
}
}

View File

@@ -17,7 +17,7 @@ import type {Block} from '../block.js';
import type {Connection} from '../connection.js';
import {ConnectionType} from '../connection_type.js';
import type {Field} from '../field.js';
import type {Input} from '../input.js';
import type {Input} from '../inputs/input.js';
import type {IASTNodeLocation} from '../interfaces/i_ast_node_location.js';
import type {IASTNodeLocationWithBlock} from '../interfaces/i_ast_node_location_with_block.js';
import {Coordinate} from '../utils/coordinate.js';

View File

@@ -13,7 +13,7 @@ import type {IBlockDragger} from './interfaces/i_block_dragger.js';
import type {IConnectionChecker} from './interfaces/i_connection_checker.js';
import type {IFlyout} from './interfaces/i_flyout.js';
import type {IMetricsManager} from './interfaces/i_metrics_manager.js';
import type {Input} from './input.js';
import type {Input} from './inputs/input.js';
import type {ISerializer} from './interfaces/i_serializer.js';
import type {IToolbox} from './interfaces/i_toolbox.js';
import type {Cursor} from './keyboard_nav/cursor.js';

View File

@@ -8,11 +8,11 @@ import * as goog from '../../../closure/goog/goog.js';
goog.declareModuleId('Blockly.blockRendering.RenderInfo');
import type {BlockSvg} from '../../block_svg.js';
import {Align, Input} from '../../input.js';
import {inputTypes} from '../../input_types.js';
import {Align, Input} from '../../inputs/input.js';
import type {RenderedConnection} from '../../rendered_connection.js';
import type {Measurable} from '../measurables/base.js';
import {BottomRow} from '../measurables/bottom_row.js';
import {DummyInput} from '../../inputs/dummy_input.js';
import {ExternalValueInput} from '../measurables/external_value_input.js';
import {Field} from '../measurables/field.js';
import {Hat} from '../measurables/hat.js';
@@ -28,9 +28,11 @@ import {RoundCorner} from '../measurables/round_corner.js';
import type {Row} from '../measurables/row.js';
import {SpacerRow} from '../measurables/spacer_row.js';
import {SquareCorner} from '../measurables/square_corner.js';
import {StatementInput} from '../measurables/statement_input.js';
import {StatementInput as StatementInputMeasurable} from '../measurables/statement_input.js';
import {StatementInput} from '../../inputs/statement_input.js';
import {TopRow} from '../measurables/top_row.js';
import {Types} from '../measurables/types.js';
import {ValueInput} from '../../inputs/value_input.js';
import type {ConstantProvider} from './constants.js';
import type {Renderer} from './renderer.js';
@@ -241,7 +243,7 @@ export class RenderInfo {
}
const precedesStatement = this.block_.inputList.length &&
this.block_.inputList[0].type === inputTypes.STATEMENT;
this.block_.inputList[0] instanceof StatementInput;
// This is the minimum height for the row. If one of its elements has a
// greater height it will be overwritten in the compute pass.
@@ -264,8 +266,8 @@ export class RenderInfo {
this.bottomRow.hasNextConnection = !!this.block_.nextConnection;
const followsStatement = this.block_.inputList.length &&
this.block_.inputList[this.block_.inputList.length - 1].type ===
inputTypes.STATEMENT;
this.block_.inputList[this.block_.inputList.length - 1] instanceof
StatementInput;
// This is the minimum height for the row. If one of its elements has a
// greater height it will be overwritten in the compute pass.
@@ -308,16 +310,17 @@ export class RenderInfo {
*/
protected addInput_(input: Input, activeRow: Row) {
// Non-dummy inputs have visual representations onscreen.
if (this.isInline && input.type === inputTypes.VALUE) {
if (this.isInline && input instanceof ValueInput) {
activeRow.elements.push(new InlineInput(this.constants_, input));
activeRow.hasInlineInput = true;
} else if (input.type === inputTypes.STATEMENT) {
activeRow.elements.push(new StatementInput(this.constants_, input));
} else if (input instanceof StatementInput) {
activeRow.elements.push(
new StatementInputMeasurable(this.constants_, input));
activeRow.hasStatement = true;
} else if (input.type === inputTypes.VALUE) {
} else if (input instanceof ValueInput) {
activeRow.elements.push(new ExternalValueInput(this.constants_, input));
activeRow.hasExternalInput = true;
} else if (input.type === inputTypes.DUMMY) {
} else if (input instanceof DummyInput) {
// Dummy inputs have no visual representation, but the information is
// still important.
activeRow.minHeight = Math.max(
@@ -346,12 +349,12 @@ export class RenderInfo {
return false;
}
// A statement input or an input following one always gets a new row.
if (input.type === inputTypes.STATEMENT ||
lastInput.type === inputTypes.STATEMENT) {
if (input instanceof StatementInput ||
lastInput instanceof StatementInput) {
return true;
}
// Value and dummy inputs get new row if inputs are not inlined.
if (input.type === inputTypes.VALUE || input.type === inputTypes.DUMMY) {
if (input instanceof ValueInput || input instanceof DummyInput) {
return !this.isInline;
}
return false;

View File

@@ -8,22 +8,24 @@ import * as goog from '../../../closure/goog/goog.js';
goog.declareModuleId('Blockly.geras.RenderInfo');
import type {BlockSvg} from '../../block_svg.js';
import type {Input} from '../../input.js';
import {inputTypes} from '../../input_types.js';
import type {Input} from '../../inputs/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 {DummyInput} from '../../inputs/dummy_input.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 {StatementInput} from '../../inputs/statement_input.js';
import type {TopRow} from '../measurables/top_row.js';
import {Types} from '../measurables/types.js';
import {ValueInput} from '../../inputs/value_input.js';
import type {ConstantProvider} from './constants.js';
import {InlineInput} from './measurables/inline_input.js';
import {StatementInput} from './measurables/statement_input.js';
import {StatementInput as StatementInputMeasurable} from './measurables/statement_input.js';
import type {Renderer} from './renderer.js';
@@ -63,8 +65,8 @@ export class RenderInfo extends BaseRenderInfo {
super.populateBottomRow_();
const followsStatement = this.block_.inputList.length &&
this.block_.inputList[this.block_.inputList.length - 1].type ===
inputTypes.STATEMENT;
this.block_.inputList[this.block_.inputList.length - 1] instanceof
StatementInput;
// The minimum height of the bottom row is smaller in Geras than in other
// renderers, because the dark path adds a pixel.
// If one of the row's elements has a greater height this will be
@@ -77,16 +79,17 @@ export class RenderInfo extends BaseRenderInfo {
override addInput_(input: Input, activeRow: Row) {
// Non-dummy inputs have visual representations onscreen.
if (this.isInline && input.type === inputTypes.VALUE) {
if (this.isInline && input instanceof ValueInput) {
activeRow.elements.push(new InlineInput(this.constants_, input));
activeRow.hasInlineInput = true;
} else if (input.type === inputTypes.STATEMENT) {
activeRow.elements.push(new StatementInput(this.constants_, input));
} else if (input instanceof StatementInput) {
activeRow.elements.push(
new StatementInputMeasurable(this.constants_, input));
activeRow.hasStatement = true;
} else if (input.type === inputTypes.VALUE) {
} else if (input instanceof ValueInput) {
activeRow.elements.push(new ExternalValueInput(this.constants_, input));
activeRow.hasExternalInput = true;
} else if (input.type === inputTypes.DUMMY) {
} else if (input instanceof DummyInput) {
// Dummy inputs have no visual representation, but the information is
// still important.
activeRow.minHeight =

View File

@@ -8,7 +8,7 @@ import * as goog from '../../../../closure/goog/goog.js';
goog.declareModuleId('Blockly.geras.InlineInput');
/* eslint-disable-next-line no-unused-vars */
import type {Input} from '../../../input.js';
import type {Input} from '../../../inputs/input.js';
import type {ConstantProvider as BaseConstantProvider} from '../../../renderers/common/constants.js';
import {InlineInput as BaseInlineInput} from '../../../renderers/measurables/inline_input.js';
import type {ConstantProvider as GerasConstantProvider} from '../constants.js';

View File

@@ -8,7 +8,7 @@ import * as goog from '../../../../closure/goog/goog.js';
goog.declareModuleId('Blockly.geras.StatementInput');
/* eslint-disable-next-line no-unused-vars */
import type {Input} from '../../../input.js';
import type {Input} from '../../../inputs/input.js';
import type {ConstantProvider as BaseConstantProvider} from '../../../renderers/common/constants.js';
import {StatementInput as BaseStatementInput} from '../../../renderers/measurables/statement_input.js';
import type {ConstantProvider as GerasConstantProvider} from '../constants.js';

View File

@@ -8,7 +8,7 @@ import * as goog from '../../../closure/goog/goog.js';
goog.declareModuleId('Blockly.blockRendering.ExternalValueInput');
/* eslint-disable-next-line no-unused-vars */
import type {Input} from '../../input.js';
import type {Input} from '../../inputs/input.js';
import type {ConstantProvider} from '../common/constants.js';
import {InputConnection} from './input_connection.js';

View File

@@ -9,7 +9,7 @@ goog.declareModuleId('Blockly.blockRendering.Field');
/* eslint-disable-next-line no-unused-vars */
import type {Field as BlocklyField} from '../../field.js';
import type {Input} from '../../input.js';
import type {Input} from '../../inputs/input.js';
import type {ConstantProvider} from '../common/constants.js';
import {Measurable} from './base.js';

View File

@@ -8,7 +8,7 @@ import * as goog from '../../../closure/goog/goog.js';
goog.declareModuleId('Blockly.blockRendering.InlineInput');
/* eslint-disable-next-line no-unused-vars */
import type {Input} from '../../input.js';
import type {Input} from '../../inputs/input.js';
import type {ConstantProvider} from '../common/constants.js';
import {InputConnection} from './input_connection.js';

View File

@@ -8,7 +8,7 @@ import * as goog from '../../../closure/goog/goog.js';
goog.declareModuleId('Blockly.blockRendering.InputConnection');
import type {BlockSvg} from '../../block_svg.js';
import type {Input} from '../../input.js';
import type {Input} from '../../inputs/input.js';
import type {RenderedConnection} from '../../rendered_connection.js';
import type {ConstantProvider} from '../common/constants.js';

View File

@@ -8,7 +8,7 @@ import * as goog from '../../../closure/goog/goog.js';
goog.declareModuleId('Blockly.blockRendering.StatementInput');
/* eslint-disable-next-line no-unused-vars */
import type {Input} from '../../input.js';
import type {Input} from '../../inputs/input.js';
import type {ConstantProvider} from '../common/constants.js';
import {InputConnection} from './input_connection.js';

View File

@@ -8,23 +8,25 @@ import * as goog from '../../../closure/goog/goog.js';
goog.declareModuleId('Blockly.zelos.RenderInfo');
import type {BlockSvg} from '../../block_svg.js';
import {DummyInput} from '../../inputs/dummy_input.js';
import {FieldImage} from '../../field_image.js';
import {FieldLabel} from '../../field_label.js';
import {FieldTextInput} from '../../field_textinput.js';
import {Align, Input} from '../../input.js';
import {inputTypes} from '../../input_types.js';
import {Align, Input} from '../../inputs/input.js';
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 {StatementInput} from '../../inputs/statement_input.js';
import type {Row} from '../measurables/row.js';
import type {SpacerRow} from '../measurables/spacer_row.js';
import {Types} from '../measurables/types.js';
import {ValueInput} from '../../inputs/value_input.js';
import type {ConstantProvider, InsideCorners} from './constants.js';
import {BottomRow} from './measurables/bottom_row.js';
import {StatementInput} from './measurables/inputs.js';
import {StatementInput as StatementInputMeasurable} from './measurables/inputs.js';
import {RightConnectionShape} from './measurables/row_elements.js';
import {TopRow} from './measurables/top_row.js';
import type {PathObject} from './path_object.js';
@@ -123,12 +125,12 @@ export class RenderInfo extends BaseRenderInfo {
return false;
}
// A statement input or an input following one always gets a new row.
if (input.type === inputTypes.STATEMENT ||
lastInput.type === inputTypes.STATEMENT) {
if (input instanceof StatementInput ||
lastInput instanceof StatementInput) {
return true;
}
// Value and dummy inputs get new row if inputs are not inlined.
if (input.type === inputTypes.VALUE || input.type === inputTypes.DUMMY) {
if (input instanceof ValueInput || input instanceof DummyInput) {
return !this.isInline || this.isMultiRow;
}
return false;
@@ -244,12 +246,13 @@ export class RenderInfo extends BaseRenderInfo {
// If we have two dummy inputs on the same row, one aligned left and the
// other right, keep track of the right aligned dummy input so we can add
// padding later.
if (input.type === inputTypes.DUMMY && activeRow.hasDummyInput &&
if (input instanceof DummyInput && activeRow.hasDummyInput &&
activeRow.align === Align.LEFT && input.align === Align.RIGHT) {
this.rightAlignedDummyInputs.set(activeRow, input);
} else if (input.type === inputTypes.STATEMENT) {
} else if (input instanceof StatementInput) {
// Handle statements without next connections correctly.
activeRow.elements.push(new StatementInput(this.constants_, input));
activeRow.elements.push(
new StatementInputMeasurable(this.constants_, input));
activeRow.hasStatement = true;
if (activeRow.align === null) {

View File

@@ -8,7 +8,7 @@ import * as goog from '../../../../closure/goog/goog.js';
goog.declareModuleId('Blockly.zelos.StatementInput');
/* eslint-disable-next-line no-unused-vars */
import type {Input} from '../../../input.js';
import type {Input} from '../../../inputs/input.js';
import type {ConstantProvider} from '../../../renderers/common/constants.js';
import {StatementInput as BaseStatementInput} from '../../../renderers/measurables/statement_input.js';

View File

@@ -11,7 +11,7 @@ import type {Block} from '../block.js';
import type {BlockSvg} from '../block_svg.js';
import type {Connection} from '../connection.js';
import * as eventUtils from '../events/utils.js';
import {inputTypes} from '../input_types.js';
import {inputTypes} from '../inputs/input_types.js';
import type {ISerializer} from '../interfaces/i_serializer.js';
import {Size} from '../utils/size.js';
import * as utilsXml from '../utils/xml.js';
@@ -251,9 +251,7 @@ function saveInputBlocks(
const inputs = Object.create(null);
for (let i = 0; i < block.inputList.length; i++) {
const input = block.inputList[i];
if (input.type === inputTypes.DUMMY) {
continue;
}
if (!input.connection) continue;
const connectionState =
saveConnection(input.connection as Connection, doFullSerialization);
if (connectionState) {

View File

@@ -13,7 +13,7 @@ import type {Connection} from './connection.js';
import * as deprecation from './utils/deprecation.js';
import * as eventUtils from './events/utils.js';
import type {Field} from './field.js';
import {inputTypes} from './input_types.js';
import {inputTypes} from './inputs/input_types.js';
import * as dom from './utils/dom.js';
import {Size} from './utils/size.js';
import * as utilsXml from './utils/xml.js';

View File

@@ -6,7 +6,7 @@
goog.declareModuleId('Blockly.test.blockJson');
import {Align} from '../../build/src/core/input.js';
import {Align} from '../../build/src/core/inputs/input.js';
import {sharedTestSetup, sharedTestTeardown} from './test_helpers/setup_teardown.js';
suite('Block JSON initialization', function() {