From 4130a929d7d8f6375c458748fcb36d65e4fa20f7 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 12 Jan 2022 17:05:00 -0800 Subject: [PATCH 1/7] refactor: convert zelos renderer classes to es6 classes --- core/renderers/common/marker_svg.js | 7 + core/renderers/zelos/constants.js | 1772 ++++++++++++++------------- core/renderers/zelos/drawer.js | 336 ++--- core/renderers/zelos/info.js | 959 ++++++++------- core/renderers/zelos/marker_svg.js | 229 ++-- core/renderers/zelos/path_object.js | 361 +++--- core/renderers/zelos/renderer.js | 187 +-- 7 files changed, 1944 insertions(+), 1907 deletions(-) diff --git a/core/renderers/common/marker_svg.js b/core/renderers/common/marker_svg.js index 8d19b919164..6878c9e546a 100644 --- a/core/renderers/common/marker_svg.js +++ b/core/renderers/common/marker_svg.js @@ -114,6 +114,13 @@ const MarkerSvg = function(workspace, constants, marker) { * @type {string} */ this.colour_ = marker.colour || defaultColour; + + /** + * The root SVG group containing the marker. + * @type {SVGGElement} + * @protected + */ + this.markerSvg_ = null; }; /** diff --git a/core/renderers/zelos/constants.js b/core/renderers/zelos/constants.js index 582f0a4faf1..8caec78d802 100644 --- a/core/renderers/zelos/constants.js +++ b/core/renderers/zelos/constants.js @@ -18,7 +18,6 @@ goog.module('Blockly.zelos.ConstantProvider'); const dom = goog.require('Blockly.utils.dom'); -const object = goog.require('Blockly.utils.object'); const svgPaths = goog.require('Blockly.utils.svgPaths'); const utilsColour = goog.require('Blockly.utils.colour'); const {ConnectionType} = goog.require('Blockly.ConnectionType'); @@ -28,953 +27,976 @@ const {Svg} = goog.require('Blockly.utils.Svg'); /** * An object that provides constants for rendering blocks in Zelos mode. - * @constructor - * @package * @extends {BaseConstantProvider} - * @alias Blockly.zelos.ConstantProvider */ -const ConstantProvider = function() { - ConstantProvider.superClass_.constructor.call(this); - - this.GRID_UNIT = 4; - - /** - * @override - */ - this.SMALL_PADDING = this.GRID_UNIT; - - /** - * @override - */ - this.MEDIUM_PADDING = 2 * this.GRID_UNIT; - - /** - * @override - */ - this.MEDIUM_LARGE_PADDING = 3 * this.GRID_UNIT; - - /** - * @override - */ - this.LARGE_PADDING = 4 * this.GRID_UNIT; - - /** - * @override - */ - this.CORNER_RADIUS = 1 * this.GRID_UNIT; - - /** - * @override - */ - this.NOTCH_WIDTH = 9 * this.GRID_UNIT; - - /** - * @override - */ - this.NOTCH_HEIGHT = 2 * this.GRID_UNIT; - - /** - * @override - */ - this.NOTCH_OFFSET_LEFT = 3 * this.GRID_UNIT; - - /** - * @override - */ - this.STATEMENT_INPUT_NOTCH_OFFSET = this.NOTCH_OFFSET_LEFT; - +class ConstantProvider extends BaseConstantProvider { /** - * @override - */ - this.MIN_BLOCK_WIDTH = 2 * this.GRID_UNIT; - - /** - * @override - */ - this.MIN_BLOCK_HEIGHT = 12 * this.GRID_UNIT; - - /** - * @override - */ - this.EMPTY_STATEMENT_INPUT_HEIGHT = 6 * this.GRID_UNIT; - - /** - * @override - */ - this.TAB_OFFSET_FROM_TOP = 0; - - /** - * @override - */ - this.TOP_ROW_MIN_HEIGHT = this.CORNER_RADIUS; - - /** - * @override - */ - this.TOP_ROW_PRECEDES_STATEMENT_MIN_HEIGHT = this.LARGE_PADDING; - - /** - * @override - */ - this.BOTTOM_ROW_MIN_HEIGHT = this.CORNER_RADIUS; - - /** - * @override - */ - this.BOTTOM_ROW_AFTER_STATEMENT_MIN_HEIGHT = 6 * this.GRID_UNIT; + * @package + * @alias Blockly.zelos.ConstantProvider + */ + constructor() { + super(); + + this.GRID_UNIT = 4; + + /** + * @override + */ + this.SMALL_PADDING = this.GRID_UNIT; + + /** + * @override + */ + this.MEDIUM_PADDING = 2 * this.GRID_UNIT; + + /** + * @override + */ + this.MEDIUM_LARGE_PADDING = 3 * this.GRID_UNIT; + + /** + * @override + */ + this.LARGE_PADDING = 4 * this.GRID_UNIT; + + /** + * @override + */ + this.CORNER_RADIUS = 1 * this.GRID_UNIT; + + /** + * @override + */ + this.NOTCH_WIDTH = 9 * this.GRID_UNIT; + + /** + * @override + */ + this.NOTCH_HEIGHT = 2 * this.GRID_UNIT; + + /** + * @override + */ + this.NOTCH_OFFSET_LEFT = 3 * this.GRID_UNIT; + + /** + * @override + */ + this.STATEMENT_INPUT_NOTCH_OFFSET = this.NOTCH_OFFSET_LEFT; + + /** + * @override + */ + this.MIN_BLOCK_WIDTH = 2 * this.GRID_UNIT; + + /** + * @override + */ + this.MIN_BLOCK_HEIGHT = 12 * this.GRID_UNIT; + + /** + * @override + */ + this.EMPTY_STATEMENT_INPUT_HEIGHT = 6 * this.GRID_UNIT; + + /** + * @override + */ + this.TAB_OFFSET_FROM_TOP = 0; + + /** + * @override + */ + this.TOP_ROW_MIN_HEIGHT = this.CORNER_RADIUS; + + /** + * @override + */ + this.TOP_ROW_PRECEDES_STATEMENT_MIN_HEIGHT = this.LARGE_PADDING; + + /** + * @override + */ + this.BOTTOM_ROW_MIN_HEIGHT = this.CORNER_RADIUS; + + /** + * @override + */ + this.BOTTOM_ROW_AFTER_STATEMENT_MIN_HEIGHT = 6 * this.GRID_UNIT; + + /** + * @override + */ + this.STATEMENT_BOTTOM_SPACER = -this.NOTCH_HEIGHT; + + /** + * Minimum statement input spacer width. + * @type {number} + */ + this.STATEMENT_INPUT_SPACER_MIN_WIDTH = 40 * this.GRID_UNIT; + + /** + * @override + */ + this.STATEMENT_INPUT_PADDING_LEFT = 4 * this.GRID_UNIT; + + /** + * @override + */ + this.EMPTY_INLINE_INPUT_PADDING = 4 * this.GRID_UNIT; + + /** + * @override + */ + this.EMPTY_INLINE_INPUT_HEIGHT = 8 * this.GRID_UNIT; + + /** + * @override + */ + this.DUMMY_INPUT_MIN_HEIGHT = 8 * this.GRID_UNIT; + + /** + * @override + */ + this.DUMMY_INPUT_SHADOW_MIN_HEIGHT = 6 * this.GRID_UNIT; + + /** + * @override + */ + this.CURSOR_WS_WIDTH = 20 * this.GRID_UNIT; + + /** + * @override + */ + this.CURSOR_COLOUR = '#ffa200'; + + /** + * Radius of the cursor for input and output connections. + * @type {number} + * @package + */ + this.CURSOR_RADIUS = 5; + + /** + * @override + */ + this.JAGGED_TEETH_HEIGHT = 0; + + /** + * @override + */ + this.JAGGED_TEETH_WIDTH = 0; + + /** + * @override + */ + this.START_HAT_HEIGHT = 22; + + /** + * @override + */ + this.START_HAT_WIDTH = 96; + + /** + * @enum {number} + * @override + */ + this.SHAPES = {HEXAGONAL: 1, ROUND: 2, SQUARE: 3, PUZZLE: 4, NOTCH: 5}; + + /** + * Map of output/input shapes and the amount they should cause a block to be + * padded. Outer key is the outer shape, inner key is the inner shape. + * When a block with the outer shape contains an input block with the inner + * shape on its left or right edge, the block elements are aligned such that + * the padding specified is reached. + * @package + */ + this.SHAPE_IN_SHAPE_PADDING = { + 1: { + // Outer shape: hexagon. + 0: 5 * this.GRID_UNIT, // Field in hexagon. + 1: 2 * this.GRID_UNIT, // Hexagon in hexagon. + 2: 5 * this.GRID_UNIT, // Round in hexagon. + 3: 5 * this.GRID_UNIT, // Square in hexagon. + }, + 2: { + // Outer shape: round. + 0: 3 * this.GRID_UNIT, // Field in round. + 1: 3 * this.GRID_UNIT, // Hexagon in round. + 2: 1 * this.GRID_UNIT, // Round in round. + 3: 2 * this.GRID_UNIT, // Square in round. + }, + 3: { + // Outer shape: square. + 0: 2 * this.GRID_UNIT, // Field in square. + 1: 2 * this.GRID_UNIT, // Hexagon in square. + 2: 2 * this.GRID_UNIT, // Round in square. + 3: 2 * this.GRID_UNIT, // Square in square. + }, + }; + + /** + * @override + */ + this.FULL_BLOCK_FIELDS = true; + + /** + * @override + */ + this.FIELD_TEXT_FONTSIZE = 3 * this.GRID_UNIT; + + /** + * @override + */ + this.FIELD_TEXT_FONTWEIGHT = 'bold'; + + /** + * @override + */ + this.FIELD_TEXT_FONTFAMILY = + '"Helvetica Neue", "Segoe UI", Helvetica, sans-serif'; + + /** + * @override + */ + this.FIELD_BORDER_RECT_RADIUS = this.CORNER_RADIUS; + + /** + * @override + */ + this.FIELD_BORDER_RECT_X_PADDING = 2 * this.GRID_UNIT; + + /** + * @override + */ + this.FIELD_BORDER_RECT_Y_PADDING = 1.625 * this.GRID_UNIT; + + /** + * @override + */ + this.FIELD_BORDER_RECT_HEIGHT = 8 * this.GRID_UNIT; + + /** + * @override + */ + this.FIELD_DROPDOWN_BORDER_RECT_HEIGHT = 8 * this.GRID_UNIT; + + /** + * @override + */ + this.FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW = true; + + /** + * @override + */ + this.FIELD_DROPDOWN_COLOURED_DIV = true; + + /** + * @override + */ + this.FIELD_DROPDOWN_SVG_ARROW = true; + + /** + * @override + */ + this.FIELD_DROPDOWN_SVG_ARROW_PADDING = this.FIELD_BORDER_RECT_X_PADDING; + + /** + * @override + */ + this.FIELD_TEXTINPUT_BOX_SHADOW = true; + + /** + * @override + */ + this.FIELD_COLOUR_FULL_BLOCK = true; + + /** + * @override + */ + this.FIELD_COLOUR_DEFAULT_WIDTH = 2 * this.GRID_UNIT; + + /** + * @override + */ + this.FIELD_COLOUR_DEFAULT_HEIGHT = 4 * this.GRID_UNIT; + + /** + * @override + */ + this.FIELD_CHECKBOX_X_OFFSET = 1 * this.GRID_UNIT; + + /** + * The maximum width of a dynamic connection shape. + * @type {number} + */ + this.MAX_DYNAMIC_CONNECTION_SHAPE_WIDTH = 12 * this.GRID_UNIT; + + /** + * The selected glow colour. + * @type {string} + */ + this.SELECTED_GLOW_COLOUR = '#fff200'; + + /** + * The size of the selected glow. + * @type {number} + */ + this.SELECTED_GLOW_SIZE = 0.5; + + /** + * The replacement glow colour. + * @type {string} + */ + this.REPLACEMENT_GLOW_COLOUR = '#fff200'; + + /** + * The size of the selected glow. + * @type {number} + */ + this.REPLACEMENT_GLOW_SIZE = 2; + + /** + * The ID of the selected glow filter, or the empty string if no filter is + * set. + * @type {string} + * @package + */ + this.selectedGlowFilterId = ''; + + /** + * The element to use for a selected glow, or null if not set. + * @type {SVGElement} + * @private + */ + this.selectedGlowFilter_ = null; + + /** + * The ID of the replacement glow filter, or the empty string if no filter is + * set. + * @type {string} + * @package + */ + this.replacementGlowFilterId = ''; + + /** + * The element to use for a replacement glow, or null if not set. + * @type {SVGElement} + * @private + */ + this.replacementGlowFilter_ = null; + + /** + * The object containing information about the hexagon used for a boolean + * reporter block. Null before init is called. + * @type {Object} + */ + this.HEXAGONAL = null; + + /** + * The object containing information about the hexagon used for a number or + * string reporter block. Null before init is called. + * @type {Object} + */ + this.ROUNDED = null; + + /** + * The object containing information about the hexagon used for a + * rectangular reporter block. Null before init is called. + * @type {Object} + */ + this.SQUARED = null; + } /** * @override */ - this.STATEMENT_BOTTOM_SPACER = -this.NOTCH_HEIGHT; + setFontConstants_(theme) { + super.setFontConstants_(theme); - /** - * Minimum statement input spacer width. - * @type {number} - */ - this.STATEMENT_INPUT_SPACER_MIN_WIDTH = 40 * this.GRID_UNIT; + this.FIELD_BORDER_RECT_HEIGHT = + this.FIELD_TEXT_HEIGHT + this.FIELD_BORDER_RECT_Y_PADDING * 2; + this.FIELD_DROPDOWN_BORDER_RECT_HEIGHT = this.FIELD_BORDER_RECT_HEIGHT; + } /** * @override */ - this.STATEMENT_INPUT_PADDING_LEFT = 4 * this.GRID_UNIT; + init() { + super.init(); + this.HEXAGONAL = this.makeHexagonal(); + this.ROUNDED = this.makeRounded(); + this.SQUARED = this.makeSquared(); - /** - * @override - */ - this.EMPTY_INLINE_INPUT_PADDING = 4 * this.GRID_UNIT; + this.STATEMENT_INPUT_NOTCH_OFFSET = + this.NOTCH_OFFSET_LEFT + this.INSIDE_CORNERS.rightWidth; + } /** * @override */ - this.EMPTY_INLINE_INPUT_HEIGHT = 8 * this.GRID_UNIT; + setDynamicProperties_(theme) { + super.setDynamicProperties_(theme); - /** - * @override - */ - this.DUMMY_INPUT_MIN_HEIGHT = 8 * this.GRID_UNIT; + this.SELECTED_GLOW_COLOUR = theme.getComponentStyle('selectedGlowColour') || + this.SELECTED_GLOW_COLOUR; + const selectedGlowSize = Number(theme.getComponentStyle('selectedGlowSize')); + this.SELECTED_GLOW_SIZE = selectedGlowSize && !isNaN(selectedGlowSize) ? + selectedGlowSize : + this.SELECTED_GLOW_SIZE; + this.REPLACEMENT_GLOW_COLOUR = + theme.getComponentStyle('replacementGlowColour') || + this.REPLACEMENT_GLOW_COLOUR; + const replacementGlowSize = + Number(theme.getComponentStyle('replacementGlowSize')); + this.REPLACEMENT_GLOW_SIZE = + replacementGlowSize && !isNaN(replacementGlowSize) ? + replacementGlowSize : + this.REPLACEMENT_GLOW_SIZE; + } /** * @override */ - this.DUMMY_INPUT_SHADOW_MIN_HEIGHT = 6 * this.GRID_UNIT; + dispose() { + super.dispose(); + if (this.selectedGlowFilter_) { + dom.removeNode(this.selectedGlowFilter_); + } + if (this.replacementGlowFilter_) { + dom.removeNode(this.replacementGlowFilter_); + } + } /** * @override */ - this.CURSOR_WS_WIDTH = 20 * this.GRID_UNIT; + makeStartHat() { + const height = this.START_HAT_HEIGHT; + const width = this.START_HAT_WIDTH; - /** - * @override - */ - this.CURSOR_COLOUR = '#ffa200'; + const mainPath = svgPaths.curve('c', [ + svgPaths.point(25, -height), + svgPaths.point(71, -height), + svgPaths.point(width, 0), + ]); + return {height: height, width: width, path: mainPath}; + } /** - * Radius of the cursor for input and output connections. - * @type {number} + * Create sizing and path information about a hexagonal shape. + * @return {!Object} An object containing sizing and path information about + * a hexagonal shape for connections. * @package */ - this.CURSOR_RADIUS = 5; - - /** - * @override - */ - this.JAGGED_TEETH_HEIGHT = 0; - - /** - * @override - */ - this.JAGGED_TEETH_WIDTH = 0; - - /** - * @override - */ - this.START_HAT_HEIGHT = 22; - - /** - * @override - */ - this.START_HAT_WIDTH = 96; - - /** - * @enum {number} - * @override - */ - this.SHAPES = {HEXAGONAL: 1, ROUND: 2, SQUARE: 3, PUZZLE: 4, NOTCH: 5}; + makeHexagonal() { + const maxWidth = this.MAX_DYNAMIC_CONNECTION_SHAPE_WIDTH; + + /** + * Make the main path for the hexagonal connection shape out of two lines. + * The lines are defined with relative positions and require the block height. + * The 'up' and 'down' versions of the paths are the same, but the Y sign + * flips. The 'left' and 'right' versions of the path are also the same, but + * the X sign flips. + * @param {number} height The height of the block the connection is on. + * @param {boolean} up True if the path should be drawn from bottom to top, + * false otherwise. + * @param {boolean} right True if the path is for the right side of the + * block. + * @return {string} A path fragment describing a rounded connection. + */ + function makeMainPath(height, up, right) { + const halfHeight = height / 2; + const width = halfHeight > maxWidth ? maxWidth : halfHeight; + const forward = up ? -1 : 1; + const direction = right ? -1 : 1; + const dy = forward * height / 2; + return svgPaths.lineTo(-direction * width, dy) + + svgPaths.lineTo(direction * width, dy); + } + + return { + type: this.SHAPES.HEXAGONAL, + isDynamic: true, + width: function(height) { + const halfHeight = height / 2; + return halfHeight > maxWidth ? maxWidth : halfHeight; + }, + height: function(height) { + return height; + }, + connectionOffsetY: function(connectionHeight) { + return connectionHeight / 2; + }, + connectionOffsetX: function(connectionWidth) { + return -connectionWidth; + }, + pathDown: function(height) { + return makeMainPath(height, false, false); + }, + pathUp: function(height) { + return makeMainPath(height, true, false); + }, + pathRightDown: function(height) { + return makeMainPath(height, false, true); + }, + pathRightUp: function(height) { + return makeMainPath(height, false, true); + }, + }; + } /** - * Map of output/input shapes and the amount they should cause a block to be - * padded. Outer key is the outer shape, inner key is the inner shape. - * When a block with the outer shape contains an input block with the inner - * shape on its left or right edge, the block elements are aligned such that - * the padding specified is reached. + * Create sizing and path information about a rounded shape. + * @return {!Object} An object containing sizing and path information about + * a rounded shape for connections. * @package */ - this.SHAPE_IN_SHAPE_PADDING = { - 1: { - // Outer shape: hexagon. - 0: 5 * this.GRID_UNIT, // Field in hexagon. - 1: 2 * this.GRID_UNIT, // Hexagon in hexagon. - 2: 5 * this.GRID_UNIT, // Round in hexagon. - 3: 5 * this.GRID_UNIT, // Square in hexagon. - }, - 2: { - // Outer shape: round. - 0: 3 * this.GRID_UNIT, // Field in round. - 1: 3 * this.GRID_UNIT, // Hexagon in round. - 2: 1 * this.GRID_UNIT, // Round in round. - 3: 2 * this.GRID_UNIT, // Square in round. - }, - 3: { - // Outer shape: square. - 0: 2 * this.GRID_UNIT, // Field in square. - 1: 2 * this.GRID_UNIT, // Hexagon in square. - 2: 2 * this.GRID_UNIT, // Round in square. - 3: 2 * this.GRID_UNIT, // Square in square. - }, - }; - - /** - * @override - */ - this.FULL_BLOCK_FIELDS = true; - - /** - * @override - */ - this.FIELD_TEXT_FONTSIZE = 3 * this.GRID_UNIT; - - /** - * @override - */ - this.FIELD_TEXT_FONTWEIGHT = 'bold'; - - /** - * @override - */ - this.FIELD_TEXT_FONTFAMILY = - '"Helvetica Neue", "Segoe UI", Helvetica, sans-serif'; - - /** - * @override - */ - this.FIELD_BORDER_RECT_RADIUS = this.CORNER_RADIUS; - - /** - * @override - */ - this.FIELD_BORDER_RECT_X_PADDING = 2 * this.GRID_UNIT; - - /** - * @override - */ - this.FIELD_BORDER_RECT_Y_PADDING = 1.625 * this.GRID_UNIT; - - /** - * @override - */ - this.FIELD_BORDER_RECT_HEIGHT = 8 * this.GRID_UNIT; - - /** - * @override - */ - this.FIELD_DROPDOWN_BORDER_RECT_HEIGHT = 8 * this.GRID_UNIT; - - /** - * @override - */ - this.FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW = true; - - /** - * @override - */ - this.FIELD_DROPDOWN_COLOURED_DIV = true; - - /** - * @override - */ - this.FIELD_DROPDOWN_SVG_ARROW = true; + makeRounded() { + const maxWidth = this.MAX_DYNAMIC_CONNECTION_SHAPE_WIDTH; + const maxHeight = maxWidth * 2; + + /** + * Make the main path for the rounded connection shape out of two arcs and + * a line that joins them. The arcs are defined with relative positions. + * Usually, the height of the block is split between the two arcs. In the case + * where the height of the block exceeds the maximum height, a line is drawn + * in between the two arcs. + * The 'up' and 'down' versions of the paths are the same, but the Y sign + * flips. The 'up' and 'right' versions of the path flip the sweep-flag + * which moves the arc at negative angles. + * @param {number} blockHeight The height of the block the connection is on. + * @param {boolean} up True if the path should be drawn from bottom to top, + * false otherwise. + * @param {boolean} right True if the path is for the right side of the + * block. + * @return {string} A path fragment describing a rounded connection. + */ + function makeMainPath(blockHeight, up, right) { + const remainingHeight = + blockHeight > maxHeight ? blockHeight - maxHeight : 0; + const height = blockHeight > maxHeight ? maxHeight : blockHeight; + const radius = height / 2; + return svgPaths.arc( + 'a', '0 0,1', radius, + svgPaths.point((up ? -1 : 1) * radius, (up ? -1 : 1) * radius)) + + svgPaths.lineOnAxis('v', (right ? 1 : -1) * remainingHeight) + + svgPaths.arc( + 'a', '0 0,1', radius, + svgPaths.point((up ? 1 : -1) * radius, (up ? -1 : 1) * radius)); + } + + return { + type: this.SHAPES.ROUND, + isDynamic: true, + width: function(height) { + const halfHeight = height / 2; + return halfHeight > maxWidth ? maxWidth : halfHeight; + }, + height: function(height) { + return height; + }, + connectionOffsetY: function(connectionHeight) { + return connectionHeight / 2; + }, + connectionOffsetX: function(connectionWidth) { + return -connectionWidth; + }, + pathDown: function(height) { + return makeMainPath(height, false, false); + }, + pathUp: function(height) { + return makeMainPath(height, true, false); + }, + pathRightDown: function(height) { + return makeMainPath(height, false, true); + }, + pathRightUp: function(height) { + return makeMainPath(height, false, true); + }, + }; + } /** - * @override + * Create sizing and path information about a squared shape. + * @return {!Object} An object containing sizing and path information about + * a squared shape for connections. + * @package */ - this.FIELD_DROPDOWN_SVG_ARROW_PADDING = this.FIELD_BORDER_RECT_X_PADDING; + makeSquared() { + const radius = this.CORNER_RADIUS; + + /** + * Make the main path for the squared connection shape out of two corners + * and a single line in-between (a and v). These are defined in relative + * positions and require the height of the block. + * The 'left' and 'right' versions of the paths are the same, but the Y sign + * flips. The 'up' and 'down' versions of the path determine where the corner + * point is placed and in turn the direction of the corners. + * @param {number} height The height of the block the connection is on. + * @param {boolean} up True if the path should be drawn from bottom to top, + * false otherwise. + * @param {boolean} right True if the path is for the right side of the + * block. + * @return {string} A path fragment describing a squared connection. + */ + function makeMainPath(height, up, right) { + const innerHeight = height - radius * 2; + return svgPaths.arc( + 'a', '0 0,1', radius, + svgPaths.point((up ? -1 : 1) * radius, (up ? -1 : 1) * radius)) + + svgPaths.lineOnAxis('v', (right ? 1 : -1) * innerHeight) + + svgPaths.arc( + 'a', '0 0,1', radius, + svgPaths.point((up ? 1 : -1) * radius, (up ? -1 : 1) * radius)); + } + + return { + type: this.SHAPES.SQUARE, + isDynamic: true, + width: function(_height) { + return radius; + }, + height: function(height) { + return height; + }, + connectionOffsetY: function(connectionHeight) { + return connectionHeight / 2; + }, + connectionOffsetX: function(connectionWidth) { + return -connectionWidth; + }, + pathDown: function(height) { + return makeMainPath(height, false, false); + }, + pathUp: function(height) { + return makeMainPath(height, true, false); + }, + pathRightDown: function(height) { + return makeMainPath(height, false, true); + }, + pathRightUp: function(height) { + return makeMainPath(height, false, true); + }, + }; + } /** * @override */ - this.FIELD_TEXTINPUT_BOX_SHADOW = true; + shapeFor(connection) { + let checks = connection.getCheck(); + if (!checks && connection.targetConnection) { + checks = connection.targetConnection.getCheck(); + } + let outputShape; + switch (connection.type) { + case ConnectionType.INPUT_VALUE: + case ConnectionType.OUTPUT_VALUE: + outputShape = connection.getSourceBlock().getOutputShape(); + // If the block has an output shape set, use that instead. + if (outputShape !== null) { + switch (outputShape) { + case this.SHAPES.HEXAGONAL: + return /** @type {!Object} */ (this.HEXAGONAL); + case this.SHAPES.ROUND: + return /** @type {!Object} */ (this.ROUNDED); + case this.SHAPES.SQUARE: + return /** @type {!Object} */ (this.SQUARED); + } + } + // Includes doesn't work in IE. + if (checks && checks.indexOf('Boolean') !== -1) { + return /** @type {!Object} */ (this.HEXAGONAL); + } + if (checks && checks.indexOf('Number') !== -1) { + return /** @type {!Object} */ (this.ROUNDED); + } + if (checks && checks.indexOf('String') !== -1) { + return /** @type {!Object} */ (this.ROUNDED); + } + return /** @type {!Object} */ (this.ROUNDED); + case ConnectionType.PREVIOUS_STATEMENT: + case ConnectionType.NEXT_STATEMENT: + return this.NOTCH; + default: + throw Error('Unknown type'); + } + } /** * @override */ - this.FIELD_COLOUR_FULL_BLOCK = true; + makeNotch() { + const width = this.NOTCH_WIDTH; + const height = this.NOTCH_HEIGHT; - /** - * @override - */ - this.FIELD_COLOUR_DEFAULT_WIDTH = 2 * this.GRID_UNIT; + const innerWidth = width / 3; + const curveWidth = innerWidth / 3; - /** - * @override - */ - this.FIELD_COLOUR_DEFAULT_HEIGHT = 4 * this.GRID_UNIT; + const halfHeight = height / 2; + const quarterHeight = halfHeight / 2; + + /** + * Make the main path for the notch. + * @param {number} dir Direction multiplier to apply to horizontal offsets + * along the path. Either 1 or -1. + * @return {string} A path fragment describing a notch. + */ + function makeMainPath(dir) { + return ( + svgPaths.curve( + 'c', + [ + svgPaths.point(dir * curveWidth / 2, 0), + svgPaths.point(dir * curveWidth * 3 / 4, quarterHeight / 2), + svgPaths.point(dir * curveWidth, quarterHeight), + ]) + + svgPaths.line([svgPaths.point(dir * curveWidth, halfHeight)]) + + svgPaths.curve( + 'c', + [ + svgPaths.point(dir * curveWidth / 4, quarterHeight / 2), + svgPaths.point(dir * curveWidth / 2, quarterHeight), + svgPaths.point(dir * curveWidth, quarterHeight), + ]) + + svgPaths.lineOnAxis('h', dir * innerWidth) + + svgPaths.curve( + 'c', + [ + svgPaths.point(dir * curveWidth / 2, 0), + svgPaths.point(dir * curveWidth * 3 / 4, -(quarterHeight / 2)), + svgPaths.point(dir * curveWidth, -quarterHeight), + ]) + + svgPaths.line([svgPaths.point(dir * curveWidth, -halfHeight)]) + + svgPaths.curve('c', [ + svgPaths.point(dir * curveWidth / 4, -(quarterHeight / 2)), + svgPaths.point(dir * curveWidth / 2, -quarterHeight), + svgPaths.point(dir * curveWidth, -quarterHeight), + ])); + } + + const pathLeft = makeMainPath(1); + const pathRight = makeMainPath(-1); + + return { + type: this.SHAPES.NOTCH, + width: width, + height: height, + pathLeft: pathLeft, + pathRight: pathRight, + }; + } /** * @override */ - this.FIELD_CHECKBOX_X_OFFSET = 1 * this.GRID_UNIT; - - /** - * The maximum width of a dynamic connection shape. - * @type {number} - */ - this.MAX_DYNAMIC_CONNECTION_SHAPE_WIDTH = 12 * this.GRID_UNIT; - - /** - * The selected glow colour. - * @type {string} - */ - this.SELECTED_GLOW_COLOUR = '#fff200'; - - /** - * The size of the selected glow. - * @type {number} - */ - this.SELECTED_GLOW_SIZE = 0.5; - - /** - * The replacement glow colour. - * @type {string} - */ - this.REPLACEMENT_GLOW_COLOUR = '#fff200'; - - /** - * The size of the selected glow. - * @type {number} - */ - this.REPLACEMENT_GLOW_SIZE = 2; - - /** - * The ID of the selected glow filter, or the empty string if no filter is - * set. - * @type {string} - * @package - */ - this.selectedGlowFilterId = ''; - - /** - * The element to use for a selected glow, or null if not set. - * @type {SVGElement} - * @private - */ - this.selectedGlowFilter_ = null; - - /** - * The ID of the replacement glow filter, or the empty string if no filter is - * set. - * @type {string} - * @package - */ - this.replacementGlowFilterId = ''; + makeInsideCorners() { + const radius = this.CORNER_RADIUS; - /** - * The element to use for a replacement glow, or null if not set. - * @type {SVGElement} - * @private - */ - this.replacementGlowFilter_ = null; -}; -object.inherits(ConstantProvider, BaseConstantProvider); - -/** - * @override - */ -ConstantProvider.prototype.setFontConstants_ = function(theme) { - ConstantProvider.superClass_.setFontConstants_.call(this, theme); + const innerTopLeftCorner = + svgPaths.arc('a', '0 0,0', radius, svgPaths.point(-radius, radius)); - this.FIELD_BORDER_RECT_HEIGHT = - this.FIELD_TEXT_HEIGHT + this.FIELD_BORDER_RECT_Y_PADDING * 2; - this.FIELD_DROPDOWN_BORDER_RECT_HEIGHT = this.FIELD_BORDER_RECT_HEIGHT; -}; + const innerTopRightCorner = + svgPaths.arc('a', '0 0,1', radius, svgPaths.point(-radius, radius)); -/** - * @override - */ -ConstantProvider.prototype.init = function() { - ConstantProvider.superClass_.init.call(this); - this.HEXAGONAL = this.makeHexagonal(); - this.ROUNDED = this.makeRounded(); - this.SQUARED = this.makeSquared(); - - this.STATEMENT_INPUT_NOTCH_OFFSET = - this.NOTCH_OFFSET_LEFT + this.INSIDE_CORNERS.rightWidth; -}; + const innerBottomLeftCorner = + svgPaths.arc('a', '0 0,0', radius, svgPaths.point(radius, radius)); -/** - * @override - */ -ConstantProvider.prototype.setDynamicProperties_ = function(theme) { - ConstantProvider.superClass_.setDynamicProperties_.call(this, theme); - - this.SELECTED_GLOW_COLOUR = theme.getComponentStyle('selectedGlowColour') || - this.SELECTED_GLOW_COLOUR; - const selectedGlowSize = Number(theme.getComponentStyle('selectedGlowSize')); - this.SELECTED_GLOW_SIZE = selectedGlowSize && !isNaN(selectedGlowSize) ? - selectedGlowSize : - this.SELECTED_GLOW_SIZE; - this.REPLACEMENT_GLOW_COLOUR = - theme.getComponentStyle('replacementGlowColour') || - this.REPLACEMENT_GLOW_COLOUR; - const replacementGlowSize = - Number(theme.getComponentStyle('replacementGlowSize')); - this.REPLACEMENT_GLOW_SIZE = - replacementGlowSize && !isNaN(replacementGlowSize) ? - replacementGlowSize : - this.REPLACEMENT_GLOW_SIZE; -}; + const innerBottomRightCorner = + svgPaths.arc('a', '0 0,1', radius, svgPaths.point(radius, radius)); -/** - * @override - */ -ConstantProvider.prototype.dispose = function() { - ConstantProvider.superClass_.dispose.call(this); - if (this.selectedGlowFilter_) { - dom.removeNode(this.selectedGlowFilter_); - } - if (this.replacementGlowFilter_) { - dom.removeNode(this.replacementGlowFilter_); + return { + width: radius, + height: radius, + pathTop: innerTopLeftCorner, + pathBottom: innerBottomLeftCorner, + rightWidth: radius, + rightHeight: radius, + pathTopRight: innerTopRightCorner, + pathBottomRight: innerBottomRightCorner, + }; } -}; - -/** - * @override - */ -ConstantProvider.prototype.makeStartHat = function() { - const height = this.START_HAT_HEIGHT; - const width = this.START_HAT_WIDTH; - - const mainPath = svgPaths.curve('c', [ - svgPaths.point(25, -height), - svgPaths.point(71, -height), - svgPaths.point(width, 0), - ]); - return {height: height, width: width, path: mainPath}; -}; - -/** - * Create sizing and path information about a hexagonal shape. - * @return {!Object} An object containing sizing and path information about - * a hexagonal shape for connections. - * @package - */ -ConstantProvider.prototype.makeHexagonal = function() { - const maxWidth = this.MAX_DYNAMIC_CONNECTION_SHAPE_WIDTH; /** - * Make the main path for the hexagonal connection shape out of two lines. - * The lines are defined with relative positions and require the block height. - * The 'up' and 'down' versions of the paths are the same, but the Y sign - * flips. The 'left' and 'right' versions of the path are also the same, but - * the X sign flips. - * @param {number} height The height of the block the connection is on. - * @param {boolean} up True if the path should be drawn from bottom to top, - * false otherwise. - * @param {boolean} right True if the path is for the right side of the - * block. - * @return {string} A path fragment describing a rounded connection. + * @override */ - function makeMainPath(height, up, right) { - const halfHeight = height / 2; - const width = halfHeight > maxWidth ? maxWidth : halfHeight; - const forward = up ? -1 : 1; - const direction = right ? -1 : 1; - const dy = forward * height / 2; - return svgPaths.lineTo(-direction * width, dy) + - svgPaths.lineTo(direction * width, dy); + generateSecondaryColour_(colour) { + return utilsColour.blend('#000', colour, 0.15) || colour; } - return { - type: this.SHAPES.HEXAGONAL, - isDynamic: true, - width: function(height) { - const halfHeight = height / 2; - return halfHeight > maxWidth ? maxWidth : halfHeight; - }, - height: function(height) { - return height; - }, - connectionOffsetY: function(connectionHeight) { - return connectionHeight / 2; - }, - connectionOffsetX: function(connectionWidth) { - return -connectionWidth; - }, - pathDown: function(height) { - return makeMainPath(height, false, false); - }, - pathUp: function(height) { - return makeMainPath(height, true, false); - }, - pathRightDown: function(height) { - return makeMainPath(height, false, true); - }, - pathRightUp: function(height) { - return makeMainPath(height, false, true); - }, - }; -}; - -/** - * Create sizing and path information about a rounded shape. - * @return {!Object} An object containing sizing and path information about - * a rounded shape for connections. - * @package - */ -ConstantProvider.prototype.makeRounded = function() { - const maxWidth = this.MAX_DYNAMIC_CONNECTION_SHAPE_WIDTH; - const maxHeight = maxWidth * 2; - /** - * Make the main path for the rounded connection shape out of two arcs and - * a line that joins them. The arcs are defined with relative positions. - * Usually, the height of the block is split between the two arcs. In the case - * where the height of the block exceeds the maximum height, a line is drawn - * in between the two arcs. - * The 'up' and 'down' versions of the paths are the same, but the Y sign - * flips. The 'up' and 'right' versions of the path flip the sweep-flag - * which moves the arc at negative angles. - * @param {number} blockHeight The height of the block the connection is on. - * @param {boolean} up True if the path should be drawn from bottom to top, - * false otherwise. - * @param {boolean} right True if the path is for the right side of the - * block. - * @return {string} A path fragment describing a rounded connection. + * @override */ - function makeMainPath(blockHeight, up, right) { - const remainingHeight = - blockHeight > maxHeight ? blockHeight - maxHeight : 0; - const height = blockHeight > maxHeight ? maxHeight : blockHeight; - const radius = height / 2; - return svgPaths.arc( - 'a', '0 0,1', radius, - svgPaths.point((up ? -1 : 1) * radius, (up ? -1 : 1) * radius)) + - svgPaths.lineOnAxis('v', (right ? 1 : -1) * remainingHeight) + - svgPaths.arc( - 'a', '0 0,1', radius, - svgPaths.point((up ? 1 : -1) * radius, (up ? -1 : 1) * radius)); + generateTertiaryColour_(colour) { + return utilsColour.blend('#000', colour, 0.25) || colour; } - return { - type: this.SHAPES.ROUND, - isDynamic: true, - width: function(height) { - const halfHeight = height / 2; - return halfHeight > maxWidth ? maxWidth : halfHeight; - }, - height: function(height) { - return height; - }, - connectionOffsetY: function(connectionHeight) { - return connectionHeight / 2; - }, - connectionOffsetX: function(connectionWidth) { - return -connectionWidth; - }, - pathDown: function(height) { - return makeMainPath(height, false, false); - }, - pathUp: function(height) { - return makeMainPath(height, true, false); - }, - pathRightDown: function(height) { - return makeMainPath(height, false, true); - }, - pathRightUp: function(height) { - return makeMainPath(height, false, true); - }, - }; -}; - -/** - * Create sizing and path information about a squared shape. - * @return {!Object} An object containing sizing and path information about - * a squared shape for connections. - * @package - */ -ConstantProvider.prototype.makeSquared = function() { - const radius = this.CORNER_RADIUS; - /** - * Make the main path for the squared connection shape out of two corners - * and a single line in-between (a and v). These are defined in relative - * positions and require the height of the block. - * The 'left' and 'right' versions of the paths are the same, but the Y sign - * flips. The 'up' and 'down' versions of the path determine where the corner - * point is placed and in turn the direction of the corners. - * @param {number} height The height of the block the connection is on. - * @param {boolean} up True if the path should be drawn from bottom to top, - * false otherwise. - * @param {boolean} right True if the path is for the right side of the - * block. - * @return {string} A path fragment describing a squared connection. + * @override */ - function makeMainPath(height, up, right) { - const innerHeight = height - radius * 2; - return svgPaths.arc( - 'a', '0 0,1', radius, - svgPaths.point((up ? -1 : 1) * radius, (up ? -1 : 1) * radius)) + - svgPaths.lineOnAxis('v', (right ? 1 : -1) * innerHeight) + - svgPaths.arc( - 'a', '0 0,1', radius, - svgPaths.point((up ? 1 : -1) * radius, (up ? -1 : 1) * radius)); + createDom(svg, tagName, selector) { + super.createDom(svg, tagName, selector); + /* + + ... filters go here ... + + */ + const defs = dom.createSvgElement(Svg.DEFS, {}, svg); + // Using a dilate distorts the block shape. + // Instead use a gaussian blur, and then set all alpha to 1 with a transfer. + const selectedGlowFilter = dom.createSvgElement( + Svg.FILTER, { + 'id': 'blocklySelectedGlowFilter' + this.randomIdentifier, + 'height': '160%', + 'width': '180%', + 'y': '-30%', + 'x': '-40%', + }, + defs); + dom.createSvgElement( + Svg.FEGAUSSIANBLUR, + {'in': 'SourceGraphic', 'stdDeviation': this.SELECTED_GLOW_SIZE}, + selectedGlowFilter); + // Set all gaussian blur pixels to 1 opacity before applying flood + const selectedComponentTransfer = dom.createSvgElement( + Svg.FECOMPONENTTRANSFER, {'result': 'outBlur'}, selectedGlowFilter); + dom.createSvgElement( + Svg.FEFUNCA, + {'type': 'table', 'tableValues': '0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1'}, + selectedComponentTransfer); + // Color the highlight + dom.createSvgElement( + Svg.FEFLOOD, { + 'flood-color': this.SELECTED_GLOW_COLOUR, + 'flood-opacity': 1, + 'result': 'outColor', + }, + selectedGlowFilter); + dom.createSvgElement( + Svg.FECOMPOSITE, { + 'in': 'outColor', + 'in2': 'outBlur', + 'operator': 'in', + 'result': 'outGlow', + }, + selectedGlowFilter); + this.selectedGlowFilterId = selectedGlowFilter.id; + this.selectedGlowFilter_ = selectedGlowFilter; + + // Using a dilate distorts the block shape. + // Instead use a gaussian blur, and then set all alpha to 1 with a transfer. + const replacementGlowFilter = dom.createSvgElement( + Svg.FILTER, { + 'id': 'blocklyReplacementGlowFilter' + this.randomIdentifier, + 'height': '160%', + 'width': '180%', + 'y': '-30%', + 'x': '-40%', + }, + defs); + dom.createSvgElement( + Svg.FEGAUSSIANBLUR, + {'in': 'SourceGraphic', 'stdDeviation': this.REPLACEMENT_GLOW_SIZE}, + replacementGlowFilter); + // Set all gaussian blur pixels to 1 opacity before applying flood + const replacementComponentTransfer = dom.createSvgElement( + Svg.FECOMPONENTTRANSFER, {'result': 'outBlur'}, replacementGlowFilter); + dom.createSvgElement( + Svg.FEFUNCA, + {'type': 'table', 'tableValues': '0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1'}, + replacementComponentTransfer); + // Color the highlight + dom.createSvgElement( + Svg.FEFLOOD, { + 'flood-color': this.REPLACEMENT_GLOW_COLOUR, + 'flood-opacity': 1, + 'result': 'outColor', + }, + replacementGlowFilter); + dom.createSvgElement( + Svg.FECOMPOSITE, { + 'in': 'outColor', + 'in2': 'outBlur', + 'operator': 'in', + 'result': 'outGlow', + }, + replacementGlowFilter); + dom.createSvgElement( + Svg.FECOMPOSITE, { + 'in': 'SourceGraphic', + 'in2': 'outGlow', + 'operator': 'over', + }, + replacementGlowFilter); + this.replacementGlowFilterId = replacementGlowFilter.id; + this.replacementGlowFilter_ = replacementGlowFilter; } - return { - type: this.SHAPES.SQUARE, - isDynamic: true, - width: function(_height) { - return radius; - }, - height: function(height) { - return height; - }, - connectionOffsetY: function(connectionHeight) { - return connectionHeight / 2; - }, - connectionOffsetX: function(connectionWidth) { - return -connectionWidth; - }, - pathDown: function(height) { - return makeMainPath(height, false, false); - }, - pathUp: function(height) { - return makeMainPath(height, true, false); - }, - pathRightDown: function(height) { - return makeMainPath(height, false, true); - }, - pathRightUp: function(height) { - return makeMainPath(height, false, true); - }, - }; -}; - -/** - * @override - */ -ConstantProvider.prototype.shapeFor = function(connection) { - let checks = connection.getCheck(); - if (!checks && connection.targetConnection) { - checks = connection.targetConnection.getCheck(); - } - let outputShape; - switch (connection.type) { - case ConnectionType.INPUT_VALUE: - case ConnectionType.OUTPUT_VALUE: - outputShape = connection.getSourceBlock().getOutputShape(); - // If the block has an output shape set, use that instead. - if (outputShape !== null) { - switch (outputShape) { - case this.SHAPES.HEXAGONAL: - return this.HEXAGONAL; - case this.SHAPES.ROUND: - return this.ROUNDED; - case this.SHAPES.SQUARE: - return this.SQUARED; - } - } - // Includes doesn't work in IE. - if (checks && checks.indexOf('Boolean') !== -1) { - return this.HEXAGONAL; - } - if (checks && checks.indexOf('Number') !== -1) { - return this.ROUNDED; - } - if (checks && checks.indexOf('String') !== -1) { - return this.ROUNDED; - } - return this.ROUNDED; - case ConnectionType.PREVIOUS_STATEMENT: - case ConnectionType.NEXT_STATEMENT: - return this.NOTCH; - default: - throw Error('Unknown type'); - } -}; - -/** - * @override - */ -ConstantProvider.prototype.makeNotch = function() { - const width = this.NOTCH_WIDTH; - const height = this.NOTCH_HEIGHT; - - const innerWidth = width / 3; - const curveWidth = innerWidth / 3; - - const halfHeight = height / 2; - const quarterHeight = halfHeight / 2; - /** - * Make the main path for the notch. - * @param {number} dir Direction multiplier to apply to horizontal offsets - * along the path. Either 1 or -1. - * @return {string} A path fragment describing a notch. + * @override */ - function makeMainPath(dir) { - return ( - svgPaths.curve( - 'c', - [ - svgPaths.point(dir * curveWidth / 2, 0), - svgPaths.point(dir * curveWidth * 3 / 4, quarterHeight / 2), - svgPaths.point(dir * curveWidth, quarterHeight), - ]) + - svgPaths.line([svgPaths.point(dir * curveWidth, halfHeight)]) + - svgPaths.curve( - 'c', - [ - svgPaths.point(dir * curveWidth / 4, quarterHeight / 2), - svgPaths.point(dir * curveWidth / 2, quarterHeight), - svgPaths.point(dir * curveWidth, quarterHeight), - ]) + - svgPaths.lineOnAxis('h', dir * innerWidth) + - svgPaths.curve( - 'c', - [ - svgPaths.point(dir * curveWidth / 2, 0), - svgPaths.point(dir * curveWidth * 3 / 4, -(quarterHeight / 2)), - svgPaths.point(dir * curveWidth, -quarterHeight), - ]) + - svgPaths.line([svgPaths.point(dir * curveWidth, -halfHeight)]) + - svgPaths.curve('c', [ - svgPaths.point(dir * curveWidth / 4, -(quarterHeight / 2)), - svgPaths.point(dir * curveWidth / 2, -quarterHeight), - svgPaths.point(dir * curveWidth, -quarterHeight), - ])); + getCSS_(selector) { + return [ + /* eslint-disable indent */ + // Text. + selector + ' .blocklyText,', selector + ' .blocklyFlyoutLabelText {', + 'font: ' + this.FIELD_TEXT_FONTWEIGHT + ' ' + this.FIELD_TEXT_FONTSIZE + + 'pt ' + this.FIELD_TEXT_FONTFAMILY + ';', + '}', + + // Fields. + selector + ' .blocklyText {', 'fill: #fff;', '}', + selector + ' .blocklyNonEditableText>rect:not(.blocklyDropdownRect),', + selector + ' .blocklyEditableText>rect:not(.blocklyDropdownRect) {', + 'fill: ' + this.FIELD_BORDER_RECT_COLOUR + ';', '}', + selector + ' .blocklyNonEditableText>text,', + selector + ' .blocklyEditableText>text,', + selector + ' .blocklyNonEditableText>g>text,', + selector + ' .blocklyEditableText>g>text {', 'fill: #575E75;', '}', + + // Flyout labels. + selector + ' .blocklyFlyoutLabelText {', 'fill: #575E75;', '}', + + // Bubbles. + selector + ' .blocklyText.blocklyBubbleText {', 'fill: #575E75;', '}', + + // Editable field hover. + selector + ' .blocklyDraggable:not(.blocklyDisabled)', + ' .blocklyEditableText:not(.editing):hover>rect,', + selector + ' .blocklyDraggable:not(.blocklyDisabled)', + ' .blocklyEditableText:not(.editing):hover>.blocklyPath {', 'stroke: #fff;', + 'stroke-width: 2;', '}', + + // Text field input. + selector + ' .blocklyHtmlInput {', + 'font-family: ' + this.FIELD_TEXT_FONTFAMILY + ';', + 'font-weight: ' + this.FIELD_TEXT_FONTWEIGHT + ';', 'color: #575E75;', '}', + + // Dropdown field. + selector + ' .blocklyDropdownText {', 'fill: #fff !important;', '}', + // Widget and Dropdown Div + selector + '.blocklyWidgetDiv .goog-menuitem,', + selector + '.blocklyDropDownDiv .goog-menuitem {', + 'font-family: ' + this.FIELD_TEXT_FONTFAMILY + ';', '}', + selector + '.blocklyDropDownDiv .goog-menuitem-content {', 'color: #fff;', + '}', + + // Connection highlight. + selector + ' .blocklyHighlightedConnectionPath {', + 'stroke: ' + this.SELECTED_GLOW_COLOUR + ';', '}', + + // Disabled outline paths. + selector + ' .blocklyDisabled > .blocklyOutlinePath {', + 'fill: url(#blocklyDisabledPattern' + this.randomIdentifier + ')', '}', + + // Insertion marker. + selector + ' .blocklyInsertionMarker>.blocklyPath {', + 'fill-opacity: ' + this.INSERTION_MARKER_OPACITY + ';', 'stroke: none;', + '}', + /* eslint-enable indent */ + ]; } - - const pathLeft = makeMainPath(1); - const pathRight = makeMainPath(-1); - - return { - type: this.SHAPES.NOTCH, - width: width, - height: height, - pathLeft: pathLeft, - pathRight: pathRight, - }; -}; - -/** - * @override - */ -ConstantProvider.prototype.makeInsideCorners = function() { - const radius = this.CORNER_RADIUS; - - const innerTopLeftCorner = - svgPaths.arc('a', '0 0,0', radius, svgPaths.point(-radius, radius)); - - const innerTopRightCorner = - svgPaths.arc('a', '0 0,1', radius, svgPaths.point(-radius, radius)); - - const innerBottomLeftCorner = - svgPaths.arc('a', '0 0,0', radius, svgPaths.point(radius, radius)); - - const innerBottomRightCorner = - svgPaths.arc('a', '0 0,1', radius, svgPaths.point(radius, radius)); - - return { - width: radius, - height: radius, - pathTop: innerTopLeftCorner, - pathBottom: innerBottomLeftCorner, - rightWidth: radius, - rightHeight: radius, - pathTopRight: innerTopRightCorner, - pathBottomRight: innerBottomRightCorner, - }; -}; - -/** - * @override - */ -ConstantProvider.prototype.generateSecondaryColour_ = function(colour) { - return utilsColour.blend('#000', colour, 0.15) || colour; -}; - -/** - * @override - */ -ConstantProvider.prototype.generateTertiaryColour_ = function(colour) { - return utilsColour.blend('#000', colour, 0.25) || colour; -}; - -/** - * @override - */ -ConstantProvider.prototype.createDom = function(svg, tagName, selector) { - ConstantProvider.superClass_.createDom.call(this, svg, tagName, selector); - /* - - ... filters go here ... - - */ - const defs = dom.createSvgElement(Svg.DEFS, {}, svg); - // Using a dilate distorts the block shape. - // Instead use a gaussian blur, and then set all alpha to 1 with a transfer. - const selectedGlowFilter = dom.createSvgElement( - Svg.FILTER, { - 'id': 'blocklySelectedGlowFilter' + this.randomIdentifier, - 'height': '160%', - 'width': '180%', - 'y': '-30%', - 'x': '-40%', - }, - defs); - dom.createSvgElement( - Svg.FEGAUSSIANBLUR, - {'in': 'SourceGraphic', 'stdDeviation': this.SELECTED_GLOW_SIZE}, - selectedGlowFilter); - // Set all gaussian blur pixels to 1 opacity before applying flood - const selectedComponentTransfer = dom.createSvgElement( - Svg.FECOMPONENTTRANSFER, {'result': 'outBlur'}, selectedGlowFilter); - dom.createSvgElement( - Svg.FEFUNCA, - {'type': 'table', 'tableValues': '0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1'}, - selectedComponentTransfer); - // Color the highlight - dom.createSvgElement( - Svg.FEFLOOD, { - 'flood-color': this.SELECTED_GLOW_COLOUR, - 'flood-opacity': 1, - 'result': 'outColor', - }, - selectedGlowFilter); - dom.createSvgElement( - Svg.FECOMPOSITE, { - 'in': 'outColor', - 'in2': 'outBlur', - 'operator': 'in', - 'result': 'outGlow', - }, - selectedGlowFilter); - this.selectedGlowFilterId = selectedGlowFilter.id; - this.selectedGlowFilter_ = selectedGlowFilter; - - // Using a dilate distorts the block shape. - // Instead use a gaussian blur, and then set all alpha to 1 with a transfer. - const replacementGlowFilter = dom.createSvgElement( - Svg.FILTER, { - 'id': 'blocklyReplacementGlowFilter' + this.randomIdentifier, - 'height': '160%', - 'width': '180%', - 'y': '-30%', - 'x': '-40%', - }, - defs); - dom.createSvgElement( - Svg.FEGAUSSIANBLUR, - {'in': 'SourceGraphic', 'stdDeviation': this.REPLACEMENT_GLOW_SIZE}, - replacementGlowFilter); - // Set all gaussian blur pixels to 1 opacity before applying flood - const replacementComponentTransfer = dom.createSvgElement( - Svg.FECOMPONENTTRANSFER, {'result': 'outBlur'}, replacementGlowFilter); - dom.createSvgElement( - Svg.FEFUNCA, - {'type': 'table', 'tableValues': '0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1'}, - replacementComponentTransfer); - // Color the highlight - dom.createSvgElement( - Svg.FEFLOOD, { - 'flood-color': this.REPLACEMENT_GLOW_COLOUR, - 'flood-opacity': 1, - 'result': 'outColor', - }, - replacementGlowFilter); - dom.createSvgElement( - Svg.FECOMPOSITE, { - 'in': 'outColor', - 'in2': 'outBlur', - 'operator': 'in', - 'result': 'outGlow', - }, - replacementGlowFilter); - dom.createSvgElement( - Svg.FECOMPOSITE, { - 'in': 'SourceGraphic', - 'in2': 'outGlow', - 'operator': 'over', - }, - replacementGlowFilter); - this.replacementGlowFilterId = replacementGlowFilter.id; - this.replacementGlowFilter_ = replacementGlowFilter; -}; - -/** - * @override - */ -ConstantProvider.prototype.getCSS_ = function(selector) { - return [ - /* eslint-disable indent */ - // Text. - selector + ' .blocklyText,', selector + ' .blocklyFlyoutLabelText {', - 'font: ' + this.FIELD_TEXT_FONTWEIGHT + ' ' + this.FIELD_TEXT_FONTSIZE + - 'pt ' + this.FIELD_TEXT_FONTFAMILY + ';', - '}', - - // Fields. - selector + ' .blocklyText {', 'fill: #fff;', '}', - selector + ' .blocklyNonEditableText>rect:not(.blocklyDropdownRect),', - selector + ' .blocklyEditableText>rect:not(.blocklyDropdownRect) {', - 'fill: ' + this.FIELD_BORDER_RECT_COLOUR + ';', '}', - selector + ' .blocklyNonEditableText>text,', - selector + ' .blocklyEditableText>text,', - selector + ' .blocklyNonEditableText>g>text,', - selector + ' .blocklyEditableText>g>text {', 'fill: #575E75;', '}', - - // Flyout labels. - selector + ' .blocklyFlyoutLabelText {', 'fill: #575E75;', '}', - - // Bubbles. - selector + ' .blocklyText.blocklyBubbleText {', 'fill: #575E75;', '}', - - // Editable field hover. - selector + ' .blocklyDraggable:not(.blocklyDisabled)', - ' .blocklyEditableText:not(.editing):hover>rect,', - selector + ' .blocklyDraggable:not(.blocklyDisabled)', - ' .blocklyEditableText:not(.editing):hover>.blocklyPath {', 'stroke: #fff;', - 'stroke-width: 2;', '}', - - // Text field input. - selector + ' .blocklyHtmlInput {', - 'font-family: ' + this.FIELD_TEXT_FONTFAMILY + ';', - 'font-weight: ' + this.FIELD_TEXT_FONTWEIGHT + ';', 'color: #575E75;', '}', - - // Dropdown field. - selector + ' .blocklyDropdownText {', 'fill: #fff !important;', '}', - // Widget and Dropdown Div - selector + '.blocklyWidgetDiv .goog-menuitem,', - selector + '.blocklyDropDownDiv .goog-menuitem {', - 'font-family: ' + this.FIELD_TEXT_FONTFAMILY + ';', '}', - selector + '.blocklyDropDownDiv .goog-menuitem-content {', 'color: #fff;', - '}', - - // Connection highlight. - selector + ' .blocklyHighlightedConnectionPath {', - 'stroke: ' + this.SELECTED_GLOW_COLOUR + ';', '}', - - // Disabled outline paths. - selector + ' .blocklyDisabled > .blocklyOutlinePath {', - 'fill: url(#blocklyDisabledPattern' + this.randomIdentifier + ')', '}', - - // Insertion marker. - selector + ' .blocklyInsertionMarker>.blocklyPath {', - 'fill-opacity: ' + this.INSERTION_MARKER_OPACITY + ';', 'stroke: none;', - '}', - /* eslint-enable indent */ - ]; -}; +} exports.ConstantProvider = ConstantProvider; diff --git a/core/renderers/zelos/drawer.js b/core/renderers/zelos/drawer.js index fc3dfbbaf4b..19301997ee9 100644 --- a/core/renderers/zelos/drawer.js +++ b/core/renderers/zelos/drawer.js @@ -16,7 +16,6 @@ goog.module('Blockly.zelos.Drawer'); const debug = goog.require('Blockly.blockRendering.debug'); -const object = goog.require('Blockly.utils.object'); const svgPaths = goog.require('Blockly.utils.svgPaths'); /* eslint-disable-next-line no-unused-vars */ const {BlockSvg} = goog.requireType('Blockly.BlockSvg'); @@ -31,203 +30,204 @@ const {Row} = goog.requireType('Blockly.blockRendering.Row'); /** * An object that draws a block based on the given rendering information. - * @param {!BlockSvg} block The block to render. - * @param {!RenderInfo} info An object containing all - * information needed to render this block. - * @package - * @constructor * @extends {BaseDrawer} - * @alias Blockly.zelos.Drawer */ -const Drawer = function(block, info) { - Drawer.superClass_.constructor.call(this, block, info); -}; -object.inherits(Drawer, BaseDrawer); - - -/** - * @override - */ -Drawer.prototype.draw = function() { - const pathObject = - /** @type {!PathObject} */ (this.block_.pathObject); - pathObject.beginDrawing(); - this.hideHiddenIcons_(); - this.drawOutline_(); - this.drawInternals_(); - - pathObject.setPath(this.outlinePath_ + '\n' + this.inlinePath_); - if (this.info_.RTL) { - pathObject.flipRTL(); +class Drawer extends BaseDrawer { + /** + * @param {!BlockSvg} block The block to render. + * @param {!RenderInfo} info An object containing all + * information needed to render this block. + * @package + * @alias Blockly.zelos.Drawer + */ + constructor(block, info) { + super(block, info); } - if (debug.isDebuggerEnabled()) { - this.block_.renderingDebugger.drawDebug(this.block_, this.info_); - } - this.recordSizeOnBlock_(); - if (this.info_.outputConnection) { - // Store the output connection shape type for parent blocks to use during - // rendering. - pathObject.outputShapeType = this.info_.outputConnection.shape.type; - } - pathObject.endDrawing(); -}; -/** - * @override - */ -Drawer.prototype.drawOutline_ = function() { - if (this.info_.outputConnection && - this.info_.outputConnection.isDynamicShape && - !this.info_.hasStatementInput && - !this.info_.bottomRow.hasNextConnection) { - this.drawFlatTop_(); - this.drawRightDynamicConnection_(); - this.drawFlatBottom_(); - this.drawLeftDynamicConnection_(); - } else { - Drawer.superClass_.drawOutline_.call(this); + /** + * @override + */ + draw() { + const pathObject = + /** @type {!PathObject} */ (this.block_.pathObject); + pathObject.beginDrawing(); + this.hideHiddenIcons_(); + this.drawOutline_(); + this.drawInternals_(); + + pathObject.setPath(this.outlinePath_ + '\n' + this.inlinePath_); + if (this.info_.RTL) { + pathObject.flipRTL(); + } + if (debug.isDebuggerEnabled()) { + this.block_.renderingDebugger.drawDebug(this.block_, this.info_); + } + this.recordSizeOnBlock_(); + if (this.info_.outputConnection) { + // Store the output connection shape type for parent blocks to use during + // rendering. + pathObject.outputShapeType = this.info_.outputConnection.shape.type; + } + pathObject.endDrawing(); } -}; -/** - * @override - */ -Drawer.prototype.drawLeft_ = function() { - if (this.info_.outputConnection && - this.info_.outputConnection.isDynamicShape) { - this.drawLeftDynamicConnection_(); - } else { - Drawer.superClass_.drawLeft_.call(this); + /** + * @override + */ + drawOutline_() { + if (this.info_.outputConnection && + this.info_.outputConnection.isDynamicShape && + !this.info_.hasStatementInput && + !this.info_.bottomRow.hasNextConnection) { + this.drawFlatTop_(); + this.drawRightDynamicConnection_(); + this.drawFlatBottom_(); + this.drawLeftDynamicConnection_(); + } else { + super.drawOutline_(); + } } -}; -/** - * Add steps for the right side of a row that does not have value or - * statement input connections. - * @param {!Row} row The row to draw the - * side of. - * @protected - */ -Drawer.prototype.drawRightSideRow_ = function(row) { - if (row.height <= 0) { - return; + /** + * @override + */ + drawLeft_() { + if (this.info_.outputConnection && + this.info_.outputConnection.isDynamicShape) { + this.drawLeftDynamicConnection_(); + } else { + super.drawLeft_(); + } } - if (row.precedesStatement || row.followsStatement) { - const cornerHeight = this.constants_.INSIDE_CORNERS.rightHeight; - const remainingHeight = - row.height - (row.precedesStatement ? cornerHeight : 0); - this.outlinePath_ += - (row.followsStatement ? this.constants_.INSIDE_CORNERS.pathBottomRight : - '') + - (remainingHeight > 0 ? - svgPaths.lineOnAxis('V', row.yPos + remainingHeight) : - '') + - (row.precedesStatement ? this.constants_.INSIDE_CORNERS.pathTopRight : - ''); - } else { - this.outlinePath_ += svgPaths.lineOnAxis('V', row.yPos + row.height); + + /** + * Add steps for the right side of a row that does not have value or + * statement input connections. + * @param {!Row} row The row to draw the + * side of. + * @protected + */ + drawRightSideRow_(row) { + if (row.height <= 0) { + return; + } + if (row.precedesStatement || row.followsStatement) { + const cornerHeight = this.constants_.INSIDE_CORNERS.rightHeight; + const remainingHeight = + row.height - (row.precedesStatement ? cornerHeight : 0); + this.outlinePath_ += + (row.followsStatement ? this.constants_.INSIDE_CORNERS.pathBottomRight : + '') + + (remainingHeight > 0 ? + svgPaths.lineOnAxis('V', row.yPos + remainingHeight) : + '') + + (row.precedesStatement ? this.constants_.INSIDE_CORNERS.pathTopRight : + ''); + } else { + this.outlinePath_ += svgPaths.lineOnAxis('V', row.yPos + row.height); + } } -}; -/** - * Add steps to draw the right side of an output with a dynamic connection. - * @protected - */ -Drawer.prototype.drawRightDynamicConnection_ = function() { - this.outlinePath_ += this.info_.outputConnection.shape.pathRightDown( - this.info_.outputConnection.height); -}; + /** + * Add steps to draw the right side of an output with a dynamic connection. + * @protected + */ + drawRightDynamicConnection_() { + this.outlinePath_ += this.info_.outputConnection.shape.pathRightDown( + this.info_.outputConnection.height); + } -/** - * Add steps to draw the left side of an output with a dynamic connection. - * @protected - */ -Drawer.prototype.drawLeftDynamicConnection_ = function() { - this.positionOutputConnection_(); + /** + * Add steps to draw the left side of an output with a dynamic connection. + * @protected + */ + drawLeftDynamicConnection_() { + this.positionOutputConnection_(); - this.outlinePath_ += this.info_.outputConnection.shape.pathUp( - this.info_.outputConnection.height); + this.outlinePath_ += this.info_.outputConnection.shape.pathUp( + this.info_.outputConnection.height); - // Close off the path. This draws a vertical line up to the start of the - // block's path, which may be either a rounded or a sharp corner. - this.outlinePath_ += 'z'; -}; + // Close off the path. This draws a vertical line up to the start of the + // block's path, which may be either a rounded or a sharp corner. + this.outlinePath_ += 'z'; + } -/** - * Add steps to draw a flat top row. - * @protected - */ -Drawer.prototype.drawFlatTop_ = function() { - const topRow = this.info_.topRow; - this.positionPreviousConnection_(); + /** + * Add steps to draw a flat top row. + * @protected + */ + drawFlatTop_() { + const topRow = this.info_.topRow; + this.positionPreviousConnection_(); - this.outlinePath_ += svgPaths.moveBy(topRow.xPos, this.info_.startY); + this.outlinePath_ += svgPaths.moveBy(topRow.xPos, this.info_.startY); - this.outlinePath_ += svgPaths.lineOnAxis('h', topRow.width); -}; + this.outlinePath_ += svgPaths.lineOnAxis('h', topRow.width); + } -/** - * Add steps to draw a flat bottom row. - * @protected - */ -Drawer.prototype.drawFlatBottom_ = function() { - const bottomRow = this.info_.bottomRow; - this.positionNextConnection_(); + /** + * Add steps to draw a flat bottom row. + * @protected + */ + drawFlatBottom_() { + const bottomRow = this.info_.bottomRow; + this.positionNextConnection_(); - this.outlinePath_ += svgPaths.lineOnAxis('V', bottomRow.baseline); + this.outlinePath_ += svgPaths.lineOnAxis('V', bottomRow.baseline); - this.outlinePath_ += svgPaths.lineOnAxis('h', -bottomRow.width); -}; + this.outlinePath_ += svgPaths.lineOnAxis('h', -bottomRow.width); + } -/** - * @override - */ -Drawer.prototype.drawInlineInput_ = function(input) { - this.positionInlineInputConnection_(input); + /** + * @override + */ + drawInlineInput_(input) { + this.positionInlineInputConnection_(input); - const inputName = input.input.name; - if (input.connectedBlock || this.info_.isInsertionMarker) { - return; - } + const inputName = input.input.name; + if (input.connectedBlock || this.info_.isInsertionMarker) { + return; + } - const width = input.width - (input.connectionWidth * 2); - const height = input.height; - const yPos = input.centerline - height / 2; + const width = input.width - (input.connectionWidth * 2); + const height = input.height; + const yPos = input.centerline - height / 2; - const connectionRight = input.xPos + input.connectionWidth; + const connectionRight = input.xPos + input.connectionWidth; - const outlinePath = svgPaths.moveTo(connectionRight, yPos) + - svgPaths.lineOnAxis('h', width) + - input.shape.pathRightDown(input.height) + - svgPaths.lineOnAxis('h', -width) + input.shape.pathUp(input.height) + 'z'; - this.block_.pathObject.setOutlinePath(inputName, outlinePath); -}; + const outlinePath = svgPaths.moveTo(connectionRight, yPos) + + svgPaths.lineOnAxis('h', width) + + input.shape.pathRightDown(input.height) + + svgPaths.lineOnAxis('h', -width) + input.shape.pathUp(input.height) + 'z'; + this.block_.pathObject.setOutlinePath(inputName, outlinePath); + } -/** - * @override - */ -Drawer.prototype.drawStatementInput_ = function(row) { - const input = row.getLastInput(); - // Where to start drawing the notch, which is on the right side in LTR. - const x = input.xPos + input.notchOffset + input.shape.width; + /** + * @override + */ + drawStatementInput_(row) { + const input = row.getLastInput(); + // Where to start drawing the notch, which is on the right side in LTR. + const x = input.xPos + input.notchOffset + input.shape.width; - const innerTopLeftCorner = input.shape.pathRight + - svgPaths.lineOnAxis( - 'h', -(input.notchOffset - this.constants_.INSIDE_CORNERS.width)) + - this.constants_.INSIDE_CORNERS.pathTop; + const innerTopLeftCorner = input.shape.pathRight + + svgPaths.lineOnAxis( + 'h', -(input.notchOffset - this.constants_.INSIDE_CORNERS.width)) + + this.constants_.INSIDE_CORNERS.pathTop; - const innerHeight = row.height - (2 * this.constants_.INSIDE_CORNERS.height); + const innerHeight = row.height - (2 * this.constants_.INSIDE_CORNERS.height); - const innerBottomLeftCorner = this.constants_.INSIDE_CORNERS.pathBottom + - svgPaths.lineOnAxis( - 'h', (input.notchOffset - this.constants_.INSIDE_CORNERS.width)) + - (input.connectedBottomNextConnection ? '' : input.shape.pathLeft); + const innerBottomLeftCorner = this.constants_.INSIDE_CORNERS.pathBottom + + svgPaths.lineOnAxis( + 'h', (input.notchOffset - this.constants_.INSIDE_CORNERS.width)) + + (input.connectedBottomNextConnection ? '' : input.shape.pathLeft); - this.outlinePath_ += svgPaths.lineOnAxis('H', x) + innerTopLeftCorner + - svgPaths.lineOnAxis('v', innerHeight) + innerBottomLeftCorner + - svgPaths.lineOnAxis('H', row.xPos + row.width); + this.outlinePath_ += svgPaths.lineOnAxis('H', x) + innerTopLeftCorner + + svgPaths.lineOnAxis('v', innerHeight) + innerBottomLeftCorner + + svgPaths.lineOnAxis('H', row.xPos + row.width); - this.positionStatementInputConnection_(row); -}; + this.positionStatementInputConnection_(row); + } +} exports.Drawer = Drawer; diff --git a/core/renderers/zelos/info.js b/core/renderers/zelos/info.js index 46b3152957e..4ca0666acfc 100644 --- a/core/renderers/zelos/info.js +++ b/core/renderers/zelos/info.js @@ -17,7 +17,6 @@ goog.module('Blockly.zelos.RenderInfo'); const {Align} = goog.require('Blockly.Input'); -const object = goog.require('Blockly.utils.object'); /* eslint-disable-next-line no-unused-vars */ const {BlockSvg} = goog.requireType('Blockly.BlockSvg'); const {BottomRow} = goog.require('Blockly.zelos.BottomRow'); @@ -45,557 +44,557 @@ const {inputTypes} = goog.require('Blockly.inputTypes'); * This measure pass does not propagate changes to the block (although fields * may choose to rerender when getSize() is called). However, calling it * repeatedly may be expensive. - * - * @param {!Renderer} renderer The renderer in use. - * @param {!BlockSvg} block The block to measure. - * @constructor - * @package * @extends {BaseRenderInfo} - * @alias Blockly.zelos.RenderInfo */ -const RenderInfo = function(renderer, block) { - RenderInfo.superClass_.constructor.call(this, renderer, block); - +class RenderInfo extends BaseRenderInfo { /** - * An object with rendering information about the top row of the block. - * @type {!TopRow} - * @override + * @param {!Renderer} renderer The renderer in use. + * @param {!BlockSvg} block The block to measure. + * @package + * @alias Blockly.zelos.RenderInfo */ - this.topRow = new TopRow(this.constants_); + constructor(renderer, block) { + super(renderer, block); + + /** + * An object with rendering information about the top row of the block. + * @type {!TopRow} + * @override + */ + this.topRow = new TopRow(this.constants_); + + /** + * An object with rendering information about the bottom row of the block. + * @type {!BottomRow} + * @override + */ + this.bottomRow = new BottomRow(this.constants_); + + /** + * @override + */ + this.isInline = true; + + /** + * Whether the block should be rendered as a multi-line block, either because + * it's not inline or because it has been collapsed. + * @type {boolean} + */ + this.isMultiRow = !block.getInputsInline() || block.isCollapsed(); + + /** + * Whether or not the block has a statement input in one of its rows. + * @type {boolean} + */ + this.hasStatementInput = block.statementInputCount > 0; + + /** + * An object with rendering information about the right connection shape. + * @type {RightConnectionShape} + */ + this.rightSide = + this.outputConnection ? new RightConnectionShape(this.constants_) : null; + } /** - * An object with rendering information about the bottom row of the block. - * @type {!BottomRow} - * @override + * Get the block renderer in use. + * @return {!Renderer} The block renderer in use. + * @package */ - this.bottomRow = new BottomRow(this.constants_); + getRenderer() { + return /** @type {!Renderer} */ (this.renderer_); + } /** * @override */ - this.isInline = true; + measure() { + // Modifying parent measure method to add `adjustXPosition_`. + this.createRows_(); + this.addElemSpacing_(); + this.addRowSpacing_(); + this.adjustXPosition_(); + this.computeBounds_(); + this.alignRowElements_(); + this.finalize_(); + } /** - * Whether the block should be rendered as a multi-line block, either because - * it's not inline or because it has been collapsed. - * @type {boolean} + * @override */ - this.isMultiRow = !block.getInputsInline() || block.isCollapsed(); + shouldStartNewRow_(input, lastInput) { + // If this is the first input, just add to the existing row. + // That row is either empty or has some icons in it. + if (!lastInput) { + return false; + } + // A statement input or an input following one always gets a new row. + if (input.type === inputTypes.STATEMENT || + lastInput.type === inputTypes.STATEMENT) { + return true; + } + // Value and dummy inputs get new row if inputs are not inlined. + if (input.type === inputTypes.VALUE || input.type === inputTypes.DUMMY) { + return !this.isInline || this.isMultiRow; + } + return false; + } /** - * Whether or not the block has a statement input in one of its rows. - * @type {boolean} + * @override */ - this.hasStatementInput = block.statementInputCount > 0; + getDesiredRowWidth_(row) { + if (row.hasStatement) { + const rightCornerWidth = this.constants_.INSIDE_CORNERS.rightWidth || 0; + return this.width - this.startX - rightCornerWidth; + } + return super.getDesiredRowWidth_(row); + } /** - * An object with rendering information about the right connection shape. - * @type {RightConnectionShape} + * @override */ - this.rightSide = - this.outputConnection ? new RightConnectionShape(this.constants_) : null; -}; -object.inherits(RenderInfo, BaseRenderInfo); - -/** - * Get the block renderer in use. - * @return {!Renderer} The block renderer in use. - * @package - */ -RenderInfo.prototype.getRenderer = function() { - return /** @type {!Renderer} */ (this.renderer_); -}; - -/** - * @override - */ -RenderInfo.prototype.measure = function() { - // Modifying parent measure method to add `adjustXPosition_`. - this.createRows_(); - this.addElemSpacing_(); - this.addRowSpacing_(); - this.adjustXPosition_(); - this.computeBounds_(); - this.alignRowElements_(); - this.finalize_(); -}; - -/** - * @override - */ -RenderInfo.prototype.shouldStartNewRow_ = function(input, lastInput) { - // If this is the first input, just add to the existing row. - // That row is either empty or has some icons in it. - if (!lastInput) { - return false; - } - // A statement input or an input following one always gets a new row. - if (input.type === inputTypes.STATEMENT || - lastInput.type === inputTypes.STATEMENT) { - return true; - } - // Value and dummy inputs get new row if inputs are not inlined. - if (input.type === inputTypes.VALUE || input.type === inputTypes.DUMMY) { - return !this.isInline || this.isMultiRow; - } - return false; -}; - - -/** - * @override - */ -RenderInfo.prototype.getDesiredRowWidth_ = function(row) { - if (row.hasStatement) { - const rightCornerWidth = this.constants_.INSIDE_CORNERS.rightWidth || 0; - return this.width - this.startX - rightCornerWidth; - } - return RenderInfo.superClass_.getDesiredRowWidth_.call(this, row); -}; - -/** - * @override - */ -RenderInfo.prototype.getInRowSpacing_ = function(prev, next) { - if (!prev || !next) { - // No need for padding at the beginning or end of the row if the - // output shape is dynamic. - if (this.outputConnection && this.outputConnection.isDynamicShape && - !this.hasStatementInput && !this.bottomRow.hasNextConnection) { - return this.constants_.NO_PADDING; + getInRowSpacing_(prev, next) { + if (!prev || !next) { + // No need for padding at the beginning or end of the row if the + // output shape is dynamic. + if (this.outputConnection && this.outputConnection.isDynamicShape && + !this.hasStatementInput && !this.bottomRow.hasNextConnection) { + return this.constants_.NO_PADDING; + } } - } - if (!prev) { - // Statement input padding. - if (next && Types.isStatementInput(next)) { - return this.constants_.STATEMENT_INPUT_PADDING_LEFT; + if (!prev) { + // Statement input padding. + if (next && Types.isStatementInput(next)) { + return this.constants_.STATEMENT_INPUT_PADDING_LEFT; + } } - } - // Spacing between a rounded corner and a previous or next connection. - if (prev && Types.isLeftRoundedCorner(prev) && next) { - if (Types.isPreviousConnection(next) || Types.isNextConnection(next)) { - return next.notchOffset - this.constants_.CORNER_RADIUS; + // Spacing between a rounded corner and a previous or next connection. + if (prev && Types.isLeftRoundedCorner(prev) && next) { + if (Types.isPreviousConnection(next) || Types.isNextConnection(next)) { + return next.notchOffset - this.constants_.CORNER_RADIUS; + } } + // Spacing between a square corner and a hat. + if (prev && Types.isLeftSquareCorner(prev) && next && Types.isHat(next)) { + return this.constants_.NO_PADDING; + } + return this.constants_.MEDIUM_PADDING; } - // Spacing between a square corner and a hat. - if (prev && Types.isLeftSquareCorner(prev) && next && Types.isHat(next)) { - return this.constants_.NO_PADDING; - } - return this.constants_.MEDIUM_PADDING; -}; -/** - * @override - */ -RenderInfo.prototype.getSpacerRowHeight_ = function(prev, next) { - // If we have an empty block add a spacer to increase the height. - if (Types.isTopRow(prev) && Types.isBottomRow(next)) { - return this.constants_.EMPTY_BLOCK_SPACER_HEIGHT; - } - const followsStatement = Types.isInputRow(prev) && prev.hasStatement; - const precedesStatement = Types.isInputRow(next) && next.hasStatement; - if (precedesStatement || followsStatement) { - const cornerHeight = this.constants_.INSIDE_CORNERS.rightHeight || 0; - const height = Math.max(this.constants_.NOTCH_HEIGHT, cornerHeight); - return precedesStatement && followsStatement ? - Math.max(height, this.constants_.DUMMY_INPUT_MIN_HEIGHT) : - height; - } - // Top and bottom rows act as a spacer so we don't need any extra padding. - if (Types.isTopRow(prev)) { - if (!prev.hasPreviousConnection && - (!this.outputConnection || this.hasStatementInput)) { - return Math.abs( - this.constants_.NOTCH_HEIGHT - this.constants_.CORNER_RADIUS); + /** + * @override + */ + getSpacerRowHeight_(prev, next) { + // If we have an empty block add a spacer to increase the height. + if (Types.isTopRow(prev) && Types.isBottomRow(next)) { + return this.constants_.EMPTY_BLOCK_SPACER_HEIGHT; } - return this.constants_.NO_PADDING; - } - if (Types.isBottomRow(next)) { - if (!this.outputConnection) { - const topHeight = Math.max( - this.topRow.minHeight, - Math.max( - this.constants_.NOTCH_HEIGHT, - this.constants_.CORNER_RADIUS)) - - this.constants_.CORNER_RADIUS; - return topHeight; - } else if (!next.hasNextConnection && this.hasStatementInput) { - return Math.abs( - this.constants_.NOTCH_HEIGHT - this.constants_.CORNER_RADIUS); + const followsStatement = Types.isInputRow(prev) && prev.hasStatement; + const precedesStatement = Types.isInputRow(next) && next.hasStatement; + if (precedesStatement || followsStatement) { + const cornerHeight = this.constants_.INSIDE_CORNERS.rightHeight || 0; + const height = Math.max(this.constants_.NOTCH_HEIGHT, cornerHeight); + return precedesStatement && followsStatement ? + Math.max(height, this.constants_.DUMMY_INPUT_MIN_HEIGHT) : + height; } - return this.constants_.NO_PADDING; - } - return this.constants_.MEDIUM_PADDING; -}; - -/** - * @override - */ -RenderInfo.prototype.getSpacerRowWidth_ = function(prev, next) { - const width = this.width - this.startX; - if ((Types.isInputRow(prev) && prev.hasStatement) || - (Types.isInputRow(next) && next.hasStatement)) { - return Math.max(width, this.constants_.STATEMENT_INPUT_SPACER_MIN_WIDTH); + // Top and bottom rows act as a spacer so we don't need any extra padding. + if (Types.isTopRow(prev)) { + if (!prev.hasPreviousConnection && + (!this.outputConnection || this.hasStatementInput)) { + return Math.abs( + this.constants_.NOTCH_HEIGHT - this.constants_.CORNER_RADIUS); + } + return this.constants_.NO_PADDING; + } + if (Types.isBottomRow(next)) { + if (!this.outputConnection) { + const topHeight = Math.max( + this.topRow.minHeight, + Math.max( + this.constants_.NOTCH_HEIGHT, + this.constants_.CORNER_RADIUS)) - + this.constants_.CORNER_RADIUS; + return topHeight; + } else if (!next.hasNextConnection && this.hasStatementInput) { + return Math.abs( + this.constants_.NOTCH_HEIGHT - this.constants_.CORNER_RADIUS); + } + return this.constants_.NO_PADDING; + } + return this.constants_.MEDIUM_PADDING; } - return width; -}; -/** - * @override - */ -RenderInfo.prototype.getElemCenterline_ = function(row, elem) { - if (row.hasStatement && !Types.isSpacer(elem) && - !Types.isStatementInput(elem)) { - return row.yPos + this.constants_.EMPTY_STATEMENT_INPUT_HEIGHT / 2; - } - if (Types.isInlineInput(elem)) { - const connectedBlock = elem.connectedBlock; - if (connectedBlock && connectedBlock.outputConnection && - connectedBlock.nextConnection) { - return row.yPos + connectedBlock.height / 2; + /** + * @override + */ + getSpacerRowWidth_(prev, next) { + const width = this.width - this.startX; + if ((Types.isInputRow(prev) && prev.hasStatement) || + (Types.isInputRow(next) && next.hasStatement)) { + return Math.max(width, this.constants_.STATEMENT_INPUT_SPACER_MIN_WIDTH); } + return width; } - return RenderInfo.superClass_.getElemCenterline_.call(this, row, elem); -}; -/** - * @override - */ -RenderInfo.prototype.addInput_ = function(input, activeRow) { - // 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 && - activeRow.align === Align.LEFT && input.align === Align.RIGHT) { - activeRow.rightAlignedDummyInput = input; - } else if (input.type === inputTypes.STATEMENT) { - // Handle statements without next connections correctly. - activeRow.elements.push(new StatementInput(this.constants_, input)); - activeRow.hasStatement = true; - - if (activeRow.align === null) { - activeRow.align = input.align; + /** + * @override + */ + getElemCenterline_(row, elem) { + if (row.hasStatement && !Types.isSpacer(elem) && + !Types.isStatementInput(elem)) { + return row.yPos + this.constants_.EMPTY_STATEMENT_INPUT_HEIGHT / 2; } - return; + if (Types.isInlineInput(elem)) { + const connectedBlock = elem.connectedBlock; + if (connectedBlock && connectedBlock.outputConnection && + connectedBlock.nextConnection) { + return row.yPos + connectedBlock.height / 2; + } + } + return super.getElemCenterline_(row, elem); } - RenderInfo.superClass_.addInput_.call(this, input, activeRow); -}; -/** - * @override - */ -RenderInfo.prototype.addAlignmentPadding_ = function(row, missingSpace) { - if (row.rightAlignedDummyInput) { - let alignmentDivider; - for (let i = 0; i < row.elements.length; i++) { - const elem = row.elements[i]; - if (Types.isSpacer(elem)) { - alignmentDivider = elem; - } - if (Types.isField(elem) && - elem.parentInput === row.rightAlignedDummyInput) { - break; + /** + * @override + */ + addInput_(input, activeRow) { + // 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 && + activeRow.align === Align.LEFT && input.align === Align.RIGHT) { + activeRow.rightAlignedDummyInput = input; + } else if (input.type === inputTypes.STATEMENT) { + // Handle statements without next connections correctly. + activeRow.elements.push(new StatementInput(this.constants_, input)); + activeRow.hasStatement = true; + + if (activeRow.align === null) { + activeRow.align = input.align; } - } - if (alignmentDivider) { - alignmentDivider.width += missingSpace; - row.width += missingSpace; return; } + super.addInput_(input, activeRow); } - RenderInfo.superClass_.addAlignmentPadding_.call(this, row, missingSpace); -}; -/** - * Adjust the x position of fields to bump all non-label fields in the first row - * past the notch position. This must be called before ``computeBounds`` is - * called. - * @protected - */ -RenderInfo.prototype.adjustXPosition_ = function() { - const notchTotalWidth = - this.constants_.NOTCH_OFFSET_LEFT + this.constants_.NOTCH_WIDTH; - let minXPos = notchTotalWidth; - // Run through every input row on the block and only apply bump logic to the - // first input row (if the block has prev connection) and every input row that - // has a prev and next notch. - for (let i = 2; i < this.rows.length - 1; i += 2) { - const prevSpacer = this.rows[i - 1]; - const row = this.rows[i]; - const nextSpacer = this.rows[i + 1]; - - const hasPrevNotch = i === 2 ? !!this.topRow.hasPreviousConnection : - !!prevSpacer.followsStatement; - const hasNextNotch = i + 2 >= this.rows.length - 1 ? - !!this.bottomRow.hasNextConnection : - !!nextSpacer.precedesStatement; - - if (Types.isInputRow(row) && row.hasStatement) { - row.measure(); - minXPos = row.width - row.getLastInput().width + notchTotalWidth; - } else if ( - hasPrevNotch && (i === 2 || hasNextNotch) && Types.isInputRow(row) && - !row.hasStatement) { - let xCursor = row.xPos; - let prevInRowSpacer = null; - for (let j = 0; j < row.elements.length; j++) { - const elem = row.elements[j]; + /** + * @override + */ + addAlignmentPadding_(row, missingSpace) { + if (row.rightAlignedDummyInput) { + let alignmentDivider; + for (let i = 0; i < row.elements.length; i++) { + const elem = row.elements[i]; if (Types.isSpacer(elem)) { - prevInRowSpacer = elem; + alignmentDivider = elem; } - if (prevInRowSpacer && (Types.isField(elem) || Types.isInput(elem))) { - if (xCursor < minXPos && - !(Types.isField(elem) && - (elem.field instanceof FieldLabel || - elem.field instanceof FieldImage))) { - const difference = minXPos - xCursor; - prevInRowSpacer.width += difference; - } + if (Types.isField(elem) && + elem.parentInput === row.rightAlignedDummyInput) { + break; } - xCursor += elem.width; + } + if (alignmentDivider) { + alignmentDivider.width += missingSpace; + row.width += missingSpace; + return; } } + super.addAlignmentPadding_(row, missingSpace); } -}; -/** - * Finalize the output connection info. In particular, set the height of the - * output connection to match that of the block. For the right side, add a - * right connection shape element and have it match the dimensions of the - * output connection. - * @protected - */ -RenderInfo.prototype.finalizeOutputConnection_ = function() { - // Dynamic output connections depend on the height of the block. - if (!this.outputConnection || !this.outputConnection.isDynamicShape) { - return; - } - let yCursor = 0; - // Determine the block height. - for (let i = 0; i < this.rows.length; i++) { - const row = this.rows[i]; - row.yPos = yCursor; - yCursor += row.height; - } - this.height = yCursor; - - // Adjust the height of the output connection. - const blockHeight = this.bottomRow.hasNextConnection ? - this.height - this.bottomRow.descenderHeight : - this.height; - const connectionHeight = this.outputConnection.shape.height(blockHeight); - const connectionWidth = this.outputConnection.shape.width(blockHeight); - - this.outputConnection.height = connectionHeight; - this.outputConnection.width = connectionWidth; - this.outputConnection.startX = connectionWidth; - this.outputConnection.connectionOffsetY = - this.outputConnection.shape.connectionOffsetY(connectionHeight); - this.outputConnection.connectionOffsetX = - this.outputConnection.shape.connectionOffsetX(connectionWidth); - - // Add the right connection measurable. - // Don't add it if we have a value-to-statement or a value-to-stack block. - let rightConnectionWidth = 0; - if (!this.hasStatementInput && !this.bottomRow.hasNextConnection) { - rightConnectionWidth = connectionWidth; - this.rightSide.height = connectionHeight; - this.rightSide.width = rightConnectionWidth; - this.rightSide.centerline = connectionHeight / 2; - this.rightSide.xPos = this.width + rightConnectionWidth; + /** + * Adjust the x position of fields to bump all non-label fields in the first row + * past the notch position. This must be called before ``computeBounds`` is + * called. + * @protected + */ + adjustXPosition_() { + const notchTotalWidth = + this.constants_.NOTCH_OFFSET_LEFT + this.constants_.NOTCH_WIDTH; + let minXPos = notchTotalWidth; + // Run through every input row on the block and only apply bump logic to the + // first input row (if the block has prev connection) and every input row that + // has a prev and next notch. + for (let i = 2; i < this.rows.length - 1; i += 2) { + const prevSpacer = this.rows[i - 1]; + const row = this.rows[i]; + const nextSpacer = this.rows[i + 1]; + + const hasPrevNotch = i === 2 ? !!this.topRow.hasPreviousConnection : + !!prevSpacer.followsStatement; + const hasNextNotch = i + 2 >= this.rows.length - 1 ? + !!this.bottomRow.hasNextConnection : + !!nextSpacer.precedesStatement; + + if (Types.isInputRow(row) && row.hasStatement) { + row.measure(); + minXPos = row.width - row.getLastInput().width + notchTotalWidth; + } else if ( + hasPrevNotch && (i === 2 || hasNextNotch) && Types.isInputRow(row) && + !row.hasStatement) { + let xCursor = row.xPos; + let prevInRowSpacer = null; + for (let j = 0; j < row.elements.length; j++) { + const elem = row.elements[j]; + if (Types.isSpacer(elem)) { + prevInRowSpacer = elem; + } + if (prevInRowSpacer && (Types.isField(elem) || Types.isInput(elem))) { + if (xCursor < minXPos && + !(Types.isField(elem) && + (elem.field instanceof FieldLabel || + elem.field instanceof FieldImage))) { + const difference = minXPos - xCursor; + prevInRowSpacer.width += difference; + } + } + xCursor += elem.width; + } + } + } } - this.startX = connectionWidth; - this.width += connectionWidth + rightConnectionWidth; - this.widthWithChildren += connectionWidth + rightConnectionWidth; -}; -/** - * Finalize horizontal alignment of elements on the block. In particular, - * reduce the implicit spacing created by the left and right output connection - * shapes by adding setting negative spacing onto the leftmost and rightmost - * spacers. - * @protected - */ -RenderInfo.prototype.finalizeHorizontalAlignment_ = function() { - if (!this.outputConnection || this.hasStatementInput || - this.bottomRow.hasNextConnection) { - return; - } - let totalNegativeSpacing = 0; - for (let i = 0; i < this.rows.length; i++) { - const row = this.rows[i]; - if (!Types.isInputRow(row)) { - continue; + /** + * Finalize the output connection info. In particular, set the height of the + * output connection to match that of the block. For the right side, add a + * right connection shape element and have it match the dimensions of the + * output connection. + * @protected + */ + finalizeOutputConnection_() { + // Dynamic output connections depend on the height of the block. + if (!this.outputConnection || !this.outputConnection.isDynamicShape) { + return; } - const firstElem = row.elements[1]; - const lastElem = row.elements[row.elements.length - 2]; - let leftNegPadding = this.getNegativeSpacing_(firstElem); - let rightNegPadding = this.getNegativeSpacing_(lastElem); - totalNegativeSpacing = leftNegPadding + rightNegPadding; - const minBlockWidth = - this.constants_.MIN_BLOCK_WIDTH + this.outputConnection.width * 2; - if (this.width - totalNegativeSpacing < minBlockWidth) { - // Maintain a minimum block width, split negative spacing between left - // and right edge. - totalNegativeSpacing = this.width - minBlockWidth; - leftNegPadding = totalNegativeSpacing / 2; - rightNegPadding = totalNegativeSpacing / 2; + let yCursor = 0; + // Determine the block height. + for (let i = 0; i < this.rows.length; i++) { + const row = this.rows[i]; + row.yPos = yCursor; + yCursor += row.height; } - // Add a negative spacer on the start and end of the block. - row.elements.unshift(new InRowSpacer(this.constants_, -leftNegPadding)); - row.elements.push(new InRowSpacer(this.constants_, -rightNegPadding)); + this.height = yCursor; + + // Adjust the height of the output connection. + const blockHeight = this.bottomRow.hasNextConnection ? + this.height - this.bottomRow.descenderHeight : + this.height; + const connectionHeight = this.outputConnection.shape.height(blockHeight); + const connectionWidth = this.outputConnection.shape.width(blockHeight); + + this.outputConnection.height = connectionHeight; + this.outputConnection.width = connectionWidth; + this.outputConnection.startX = connectionWidth; + this.outputConnection.connectionOffsetY = + this.outputConnection.shape.connectionOffsetY(connectionHeight); + this.outputConnection.connectionOffsetX = + this.outputConnection.shape.connectionOffsetX(connectionWidth); + + // Add the right connection measurable. + // Don't add it if we have a value-to-statement or a value-to-stack block. + let rightConnectionWidth = 0; + if (!this.hasStatementInput && !this.bottomRow.hasNextConnection) { + rightConnectionWidth = connectionWidth; + this.rightSide.height = connectionHeight; + this.rightSide.width = rightConnectionWidth; + this.rightSide.centerline = connectionHeight / 2; + this.rightSide.xPos = this.width + rightConnectionWidth; + } + this.startX = connectionWidth; + this.width += connectionWidth + rightConnectionWidth; + this.widthWithChildren += connectionWidth + rightConnectionWidth; } - if (totalNegativeSpacing) { - this.width -= totalNegativeSpacing; - this.widthWithChildren -= totalNegativeSpacing; - this.rightSide.xPos -= totalNegativeSpacing; + + /** + * Finalize horizontal alignment of elements on the block. In particular, + * reduce the implicit spacing created by the left and right output connection + * shapes by adding setting negative spacing onto the leftmost and rightmost + * spacers. + * @protected + */ + finalizeHorizontalAlignment_() { + if (!this.outputConnection || this.hasStatementInput || + this.bottomRow.hasNextConnection) { + return; + } + let totalNegativeSpacing = 0; for (let i = 0; i < this.rows.length; i++) { const row = this.rows[i]; - if (Types.isTopOrBottomRow(row)) { - row.elements[1].width -= totalNegativeSpacing; - row.elements[1].widthWithConnectedBlocks -= totalNegativeSpacing; + if (!Types.isInputRow(row)) { + continue; + } + const firstElem = row.elements[1]; + const lastElem = row.elements[row.elements.length - 2]; + let leftNegPadding = this.getNegativeSpacing_(firstElem); + let rightNegPadding = this.getNegativeSpacing_(lastElem); + totalNegativeSpacing = leftNegPadding + rightNegPadding; + const minBlockWidth = + this.constants_.MIN_BLOCK_WIDTH + this.outputConnection.width * 2; + if (this.width - totalNegativeSpacing < minBlockWidth) { + // Maintain a minimum block width, split negative spacing between left + // and right edge. + totalNegativeSpacing = this.width - minBlockWidth; + leftNegPadding = totalNegativeSpacing / 2; + rightNegPadding = totalNegativeSpacing / 2; } - row.width -= totalNegativeSpacing; - row.widthWithConnectedBlocks -= totalNegativeSpacing; + // Add a negative spacer on the start and end of the block. + row.elements.unshift(new InRowSpacer(this.constants_, -leftNegPadding)); + row.elements.push(new InRowSpacer(this.constants_, -rightNegPadding)); } - } -}; - -/** - * Calculate the spacing to reduce the left and right edges by based on the - * outer and inner connection shape. - * @param {Measurable} elem The first or last element on - * a block. - * @return {number} The amount of spacing to reduce the first or last spacer. - * @protected - */ -RenderInfo.prototype.getNegativeSpacing_ = function(elem) { - if (!elem) { - return 0; - } - const connectionWidth = this.outputConnection.width; - const outerShape = this.outputConnection.shape.type; - const constants = - /** @type {!ConstantProvider} */ (this.constants_); - if (this.isMultiRow && this.inputRows.length > 1) { - switch (outerShape) { - case constants.SHAPES.ROUND: { - // Special case for multi-row round reporter blocks. - const maxWidth = this.constants_.MAX_DYNAMIC_CONNECTION_SHAPE_WIDTH; - const width = this.height / 2 > maxWidth ? maxWidth : this.height / 2; - const topPadding = this.constants_.SMALL_PADDING; - const roundPadding = - width * (1 - Math.sin(Math.acos((width - topPadding) / width))); - return connectionWidth - roundPadding; + if (totalNegativeSpacing) { + this.width -= totalNegativeSpacing; + this.widthWithChildren -= totalNegativeSpacing; + this.rightSide.xPos -= totalNegativeSpacing; + for (let i = 0; i < this.rows.length; i++) { + const row = this.rows[i]; + if (Types.isTopOrBottomRow(row)) { + row.elements[1].width -= totalNegativeSpacing; + row.elements[1].widthWithConnectedBlocks -= totalNegativeSpacing; + } + row.width -= totalNegativeSpacing; + row.widthWithConnectedBlocks -= totalNegativeSpacing; } - default: - return 0; } } - if (Types.isInlineInput(elem)) { - const connectedBlock = elem.connectedBlock; - const innerShape = connectedBlock ? - connectedBlock.pathObject.outputShapeType : - elem.shape.type; - // Special case for value to stack / value to statement blocks. - if (connectedBlock && connectedBlock.outputConnection && - (connectedBlock.statementInputCount || connectedBlock.nextConnection)) { + + /** + * Calculate the spacing to reduce the left and right edges by based on the + * outer and inner connection shape. + * @param {Measurable} elem The first or last element on + * a block. + * @return {number} The amount of spacing to reduce the first or last spacer. + * @protected + */ + getNegativeSpacing_(elem) { + if (!elem) { return 0; } - // Special case for hexagonal output. - if (outerShape === constants.SHAPES.HEXAGONAL && - outerShape !== innerShape) { - return 0; + const connectionWidth = this.outputConnection.width; + const outerShape = this.outputConnection.shape.type; + const constants = + /** @type {!ConstantProvider} */ (this.constants_); + if (this.isMultiRow && this.inputRows.length > 1) { + switch (outerShape) { + case constants.SHAPES.ROUND: { + // Special case for multi-row round reporter blocks. + const maxWidth = this.constants_.MAX_DYNAMIC_CONNECTION_SHAPE_WIDTH; + const width = this.height / 2 > maxWidth ? maxWidth : this.height / 2; + const topPadding = this.constants_.SMALL_PADDING; + const roundPadding = + width * (1 - Math.sin(Math.acos((width - topPadding) / width))); + return connectionWidth - roundPadding; + } + default: + return 0; + } } - return connectionWidth - - this.constants_.SHAPE_IN_SHAPE_PADDING[outerShape][innerShape]; - } else if (Types.isField(elem)) { - // Special case for text inputs. - if (outerShape === constants.SHAPES.ROUND && - elem.field instanceof FieldTextInput) { - return connectionWidth - (2.75 * constants.GRID_UNIT); + if (Types.isInlineInput(elem)) { + const connectedBlock = elem.connectedBlock; + const innerShape = connectedBlock ? + connectedBlock.pathObject.outputShapeType : + elem.shape.type; + // Special case for value to stack / value to statement blocks. + if (connectedBlock && connectedBlock.outputConnection && + (connectedBlock.statementInputCount || connectedBlock.nextConnection)) { + return 0; + } + // Special case for hexagonal output. + if (outerShape === constants.SHAPES.HEXAGONAL && + outerShape !== innerShape) { + return 0; + } + return connectionWidth - + this.constants_.SHAPE_IN_SHAPE_PADDING[outerShape][innerShape]; + } else if (Types.isField(elem)) { + // Special case for text inputs. + if (outerShape === constants.SHAPES.ROUND && + elem.field instanceof FieldTextInput) { + return connectionWidth - (2.75 * constants.GRID_UNIT); + } + return connectionWidth - + this.constants_.SHAPE_IN_SHAPE_PADDING[outerShape][0]; + } else if (Types.isIcon(elem)) { + return this.constants_.SMALL_PADDING; } - return connectionWidth - - this.constants_.SHAPE_IN_SHAPE_PADDING[outerShape][0]; - } else if (Types.isIcon(elem)) { - return this.constants_.SMALL_PADDING; + return 0; } - return 0; -}; -/** - * Finalize vertical alignment of rows on a block. In particular, reduce the - * implicit spacing when a non-shadow block is connected to any of an input - * row's inline inputs. - * @protected - */ -RenderInfo.prototype.finalizeVerticalAlignment_ = function() { - if (this.outputConnection) { - return; - } - // Run through every input row on the block and only apply tight nesting logic - // to input rows that have a prev and next notch. - for (let i = 2; i < this.rows.length - 1; i += 2) { - const prevSpacer = this.rows[i - 1]; - const row = this.rows[i]; - const nextSpacer = this.rows[i + 1]; - - const firstRow = i === 2; - const hasPrevNotch = firstRow ? !!this.topRow.hasPreviousConnection : - !!prevSpacer.followsStatement; - const hasNextNotch = i + 2 >= this.rows.length - 1 ? - !!this.bottomRow.hasNextConnection : - !!nextSpacer.precedesStatement; - - if (hasPrevNotch) { - const hasSingleTextOrImageField = row.elements.length === 3 && - (row.elements[1].field instanceof FieldLabel || - row.elements[1].field instanceof FieldImage); - if (!firstRow && hasSingleTextOrImageField) { - // Remove some padding if we have a single image or text field. - prevSpacer.height -= this.constants_.SMALL_PADDING; - nextSpacer.height -= this.constants_.SMALL_PADDING; - row.height -= this.constants_.MEDIUM_PADDING; - } else if (!firstRow && !hasNextNotch) { - // Add a small padding so the notch doesn't clash with inputs/fields. - prevSpacer.height += this.constants_.SMALL_PADDING; - } else if (hasNextNotch) { - // Determine if the input row has non-shadow connected blocks. - let hasNonShadowConnectedBlocks = false; - const minVerticalTightNestingHeight = 40; - for (let j = 0; j < row.elements.length; j++) { - const elem = row.elements[j]; - if (Types.isInlineInput(elem) && elem.connectedBlock && - !elem.connectedBlock.isShadow() && - elem.connectedBlock.getHeightWidth().height >= - minVerticalTightNestingHeight) { - hasNonShadowConnectedBlocks = true; - break; - } - } - // Apply tight-nesting if we have both a prev and next notch and the - // block has non-shadow connected blocks. - if (hasNonShadowConnectedBlocks) { + /** + * Finalize vertical alignment of rows on a block. In particular, reduce the + * implicit spacing when a non-shadow block is connected to any of an input + * row's inline inputs. + * @protected + */ + finalizeVerticalAlignment_() { + if (this.outputConnection) { + return; + } + // Run through every input row on the block and only apply tight nesting logic + // to input rows that have a prev and next notch. + for (let i = 2; i < this.rows.length - 1; i += 2) { + const prevSpacer = this.rows[i - 1]; + const row = this.rows[i]; + const nextSpacer = this.rows[i + 1]; + + const firstRow = i === 2; + const hasPrevNotch = firstRow ? !!this.topRow.hasPreviousConnection : + !!prevSpacer.followsStatement; + const hasNextNotch = i + 2 >= this.rows.length - 1 ? + !!this.bottomRow.hasNextConnection : + !!nextSpacer.precedesStatement; + + if (hasPrevNotch) { + const hasSingleTextOrImageField = row.elements.length === 3 && + (row.elements[1].field instanceof FieldLabel || + row.elements[1].field instanceof FieldImage); + if (!firstRow && hasSingleTextOrImageField) { + // Remove some padding if we have a single image or text field. prevSpacer.height -= this.constants_.SMALL_PADDING; nextSpacer.height -= this.constants_.SMALL_PADDING; + row.height -= this.constants_.MEDIUM_PADDING; + } else if (!firstRow && !hasNextNotch) { + // Add a small padding so the notch doesn't clash with inputs/fields. + prevSpacer.height += this.constants_.SMALL_PADDING; + } else if (hasNextNotch) { + // Determine if the input row has non-shadow connected blocks. + let hasNonShadowConnectedBlocks = false; + const minVerticalTightNestingHeight = 40; + for (let j = 0; j < row.elements.length; j++) { + const elem = row.elements[j]; + if (Types.isInlineInput(elem) && elem.connectedBlock && + !elem.connectedBlock.isShadow() && + elem.connectedBlock.getHeightWidth().height >= + minVerticalTightNestingHeight) { + hasNonShadowConnectedBlocks = true; + break; + } + } + // Apply tight-nesting if we have both a prev and next notch and the + // block has non-shadow connected blocks. + if (hasNonShadowConnectedBlocks) { + prevSpacer.height -= this.constants_.SMALL_PADDING; + nextSpacer.height -= this.constants_.SMALL_PADDING; + } } } } } -}; -/** - * @override - */ -RenderInfo.prototype.finalize_ = function() { - this.finalizeOutputConnection_(); - this.finalizeHorizontalAlignment_(); - this.finalizeVerticalAlignment_(); - RenderInfo.superClass_.finalize_.call(this); - - if (this.rightSide) { - this.widthWithChildren += this.rightSide.width; + /** + * @override + */ + finalize_() { + this.finalizeOutputConnection_(); + this.finalizeHorizontalAlignment_(); + this.finalizeVerticalAlignment_(); + super.finalize_(); + + if (this.rightSide) { + this.widthWithChildren += this.rightSide.width; + } } -}; +} exports.RenderInfo = RenderInfo; diff --git a/core/renderers/zelos/marker_svg.js b/core/renderers/zelos/marker_svg.js index 0fc1a7132a2..6e84d6831d5 100644 --- a/core/renderers/zelos/marker_svg.js +++ b/core/renderers/zelos/marker_svg.js @@ -16,7 +16,6 @@ goog.module('Blockly.zelos.MarkerSvg'); const dom = goog.require('Blockly.utils.dom'); -const object = goog.require('Blockly.utils.object'); /* eslint-disable-next-line no-unused-vars */ const {ASTNode} = goog.requireType('Blockly.ASTNode'); /* eslint-disable-next-line no-unused-vars */ @@ -35,131 +34,139 @@ const {WorkspaceSvg} = goog.requireType('Blockly.WorkspaceSvg'); /** * Class to draw a marker. - * @param {!WorkspaceSvg} workspace The workspace the marker belongs to. - * @param {!ConstantProvider} constants The constants for - * the renderer. - * @param {!Marker} marker The marker to draw. - * @constructor * @extends {BaseMarkerSvg} - * @alias Blockly.zelos.MarkerSvg */ -const MarkerSvg = function(workspace, constants, marker) { - MarkerSvg.superClass_.constructor.call(this, workspace, constants, marker); -}; -object.inherits(MarkerSvg, BaseMarkerSvg); - -/** - * Position and display the marker for an input or an output connection. - * @param {!ASTNode} curNode The node to draw the marker for. - * @private - */ -MarkerSvg.prototype.showWithInputOutput_ = function(curNode) { - const block = /** @type {!BlockSvg} */ (curNode.getSourceBlock()); - const connection = /** @type {!Connection} */ (curNode.getLocation()); - const offsetInBlock = connection.getOffsetInBlock(); - - this.positionCircle_(offsetInBlock.x, offsetInBlock.y); - this.setParent_(block); - this.showCurrent_(); -}; +class MarkerSvg extends BaseMarkerSvg { + /** + * @param {!WorkspaceSvg} workspace The workspace the marker belongs to. + * @param {!ConstantProvider} constants The constants for + * the renderer. + * @param {!Marker} marker The marker to draw. + * @alias Blockly.zelos.MarkerSvg + */ + constructor(workspace, constants, marker) { + super(workspace, constants, marker); + + /** + * @type {SVGCircleElement} + * @private + */ + this.markerCircle_ = null; + } -/** - * @override - */ -MarkerSvg.prototype.showWithOutput_ = function(curNode) { - this.showWithInputOutput_(curNode); -}; + /** + * Position and display the marker for an input or an output connection. + * @param {!ASTNode} curNode The node to draw the marker for. + * @private + */ + showWithInputOutput_(curNode) { + const block = /** @type {!BlockSvg} */ (curNode.getSourceBlock()); + const connection = /** @type {!Connection} */ (curNode.getLocation()); + const offsetInBlock = connection.getOffsetInBlock(); + + this.positionCircle_(offsetInBlock.x, offsetInBlock.y); + this.setParent_(block); + this.showCurrent_(); + } -/** - * @override - */ -MarkerSvg.prototype.showWithInput_ = function(curNode) { - this.showWithInputOutput_(curNode); -}; + /** + * @override + */ + showWithOutput_(curNode) { + this.showWithInputOutput_(curNode); + } -/** - * Draw a rectangle around the block. - * @param {!ASTNode} curNode The current node of the marker. - */ -MarkerSvg.prototype.showWithBlock_ = function(curNode) { - const block = /** @type {!BlockSvg} */ (curNode.getLocation()); + /** + * @override + */ + showWithInput_(curNode) { + this.showWithInputOutput_(curNode); + } - // Gets the height and width of entire stack. - const heightWidth = block.getHeightWidth(); + /** + * Draw a rectangle around the block. + * @param {!ASTNode} curNode The current node of the marker. + */ + showWithBlock_(curNode) { + const block = /** @type {!BlockSvg} */ (curNode.getLocation()); - // 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_(); -}; + // Gets the height and width of entire stack. + const heightWidth = block.getHeightWidth(); -/** - * Position the circle we use for input and output connections. - * @param {number} x The x position of the circle. - * @param {number} y The y position of the circle. - * @private - */ -MarkerSvg.prototype.positionCircle_ = function(x, y) { - this.markerCircle_.setAttribute('cx', x); - this.markerCircle_.setAttribute('cy', y); - this.currentMarkerSvg = this.markerCircle_; -}; + // 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_(); + } -/** - * @override - */ -MarkerSvg.prototype.hide = function() { - MarkerSvg.superClass_.hide.call(this); - this.markerCircle_.style.display = 'none'; -}; + /** + * Position the circle we use for input and output connections. + * @param {number} x The x position of the circle. + * @param {number} y The y position of the circle. + * @private + */ + positionCircle_(x, y) { + this.markerCircle_.setAttribute('cx', x); + this.markerCircle_.setAttribute('cy', y); + this.currentMarkerSvg = this.markerCircle_; + } -/** - * @override - */ -MarkerSvg.prototype.createDomInternal_ = function() { - /* clang-format off */ - /* This markup will be generated and added to the .svgGroup_: - - - - - - */ - /* clang-format on */ - - MarkerSvg.superClass_.createDomInternal_.call(this); - - this.markerCircle_ = dom.createSvgElement( - Svg.CIRCLE, { - 'r': this.constants_.CURSOR_RADIUS, - 'style': 'display: none', - 'stroke-width': this.constants_.CURSOR_STROKE_WIDTH, - }, - this.markerSvg_); - - // Markers and stack cursors don't blink. - if (this.isCursor()) { - const blinkProperties = this.getBlinkProperties_(); - dom.createSvgElement(Svg.ANIMATE, blinkProperties, this.markerCircle_); + /** + * @override + */ + hide() { + super.hide(); + this.markerCircle_.style.display = 'none'; } - return this.markerSvg_; -}; + /** + * @override + */ + createDomInternal_() { + /* clang-format off */ + /* This markup will be generated and added to the .svgGroup_: + + + + + + */ + /* clang-format on */ + + super.createDomInternal_(); + + this.markerCircle_ = dom.createSvgElement( + Svg.CIRCLE, { + 'r': this.constants_.CURSOR_RADIUS, + 'style': 'display: none', + 'stroke-width': this.constants_.CURSOR_STROKE_WIDTH, + }, + this.markerSvg_); + + // 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 - */ -MarkerSvg.prototype.applyColour_ = function(curNode) { - MarkerSvg.superClass_.applyColour_.call(this, curNode); + /** + * @override + */ + applyColour_(curNode) { + super.applyColour_(curNode); - this.markerCircle_.setAttribute('fill', this.colour_); - this.markerCircle_.setAttribute('stroke', this.colour_); + this.markerCircle_.setAttribute('fill', this.colour_); + this.markerCircle_.setAttribute('stroke', this.colour_); - if (this.isCursor()) { - const values = this.colour_ + ';transparent;transparent;'; - this.markerCircle_.firstChild.setAttribute('values', values); + if (this.isCursor()) { + const values = this.colour_ + ';transparent;transparent;'; + this.markerCircle_.firstChild.setAttribute('values', values); + } } -}; +} exports.MarkerSvg = MarkerSvg; diff --git a/core/renderers/zelos/path_object.js b/core/renderers/zelos/path_object.js index a4697c09db4..f3c79ec9509 100644 --- a/core/renderers/zelos/path_object.js +++ b/core/renderers/zelos/path_object.js @@ -17,7 +17,6 @@ goog.module('Blockly.zelos.PathObject'); const dom = goog.require('Blockly.utils.dom'); -const object = goog.require('Blockly.utils.object'); /* eslint-disable-next-line no-unused-vars */ const {ConstantProvider} = goog.requireType('Blockly.zelos.ConstantProvider'); const {PathObject: BasePathObject} = goog.require('Blockly.blockRendering.PathObject'); @@ -29,218 +28,220 @@ const {Theme} = goog.requireType('Blockly.Theme'); /** * An object that handles creating and setting each of the SVG elements * used by the renderer. - * @param {!SVGElement} root The root SVG element. - * @param {!Theme.BlockStyle} style The style object to use for - * colouring. - * @param {!ConstantProvider} constants The renderer's constants. - * @constructor * @extends {BasePathObject} - * @package - * @alias Blockly.zelos.PathObject */ -const PathObject = function(root, style, constants) { - PathObject.superClass_.constructor.call(this, root, style, constants); - +class PathObject extends BasePathObject { /** - * The renderer's constant provider. - * @type {!ConstantProvider} + * @param {!SVGElement} root The root SVG element. + * @param {!Theme.BlockStyle} style The style object to use for + * colouring. + * @param {!ConstantProvider} constants The renderer's constants. + * @package + * @alias Blockly.zelos.PathObject */ - this.constants = constants; + constructor(root, style, constants) { + super(root, style, constants); + + /** + * The renderer's constant provider. + * @type {!ConstantProvider} + */ + this.constants = constants; + + /** + * The selected path of the block. + * @type {?SVGElement} + * @private + */ + this.svgPathSelected_ = null; + + /** + * The outline paths on the block. + * @type {!Object} + * @private + */ + this.outlines_ = Object.create(null); + + /** + * A set used to determine which outlines were used during a draw pass. The + * set is initialized with a reference to all the outlines in + * `this.outlines_`. Every time we use an outline during the draw pass, the + * reference is removed from this set. + * @type {Object} + * @private + */ + this.remainingOutlines_ = null; + + /** + * The type of block's output connection shape. This is set when a block with + * an output connection is drawn. + * @package + */ + this.outputShapeType = null; + } /** - * The selected path of the block. - * @type {?SVGElement} - * @private + * @override */ - this.svgPathSelected_ = null; + setPath(pathString) { + super.setPath(pathString); + if (this.svgPathSelected_) { + this.svgPathSelected_.setAttribute('d', pathString); + } + } /** - * The outline paths on the block. - * @type {!Object} - * @private + * @override */ - this.outlines_ = Object.create(null); + applyColour(block) { + super.applyColour(block); + // Set shadow stroke colour. + if (block.isShadow() && block.getParent()) { + this.svgPath.setAttribute('stroke', block.getParent().style.colourTertiary); + } - /** - * A set used to determine which outlines were used during a draw pass. The - * set is initialized with a reference to all the outlines in - * `this.outlines_`. Every time we use an outline during the draw pass, the - * reference is removed from this set. - * @type {Object} - * @private - */ - this.remainingOutlines_ = null; + // Apply colour to outlines. + for (const key in this.outlines_) { + this.outlines_[key].setAttribute('fill', this.style.colourTertiary); + } + } /** - * The type of block's output connection shape. This is set when a block with - * an output connection is drawn. - * @package + * @override */ - this.outputShapeType = null; -}; -object.inherits(PathObject, BasePathObject); - -/** - * @override - */ -PathObject.prototype.setPath = function(pathString) { - PathObject.superClass_.setPath.call(this, pathString); - if (this.svgPathSelected_) { - this.svgPathSelected_.setAttribute('d', pathString); - } -}; - -/** - * @override - */ -PathObject.prototype.applyColour = function(block) { - PathObject.superClass_.applyColour.call(this, block); - // Set shadow stroke colour. - if (block.isShadow() && block.getParent()) { - this.svgPath.setAttribute('stroke', block.getParent().style.colourTertiary); + flipRTL() { + super.flipRTL(); + // Mirror each input outline path. + for (const key in this.outlines_) { + this.outlines_[key].setAttribute('transform', 'scale(-1 1)'); + } } - // Apply colour to outlines. - for (const key in this.outlines_) { - this.outlines_[key].setAttribute('fill', this.style.colourTertiary); + /** + * @override + */ + updateSelected(enable) { + this.setClass_('blocklySelected', enable); + if (enable) { + if (!this.svgPathSelected_) { + this.svgPathSelected_ = + /** @type {!SVGElement} */ (this.svgPath.cloneNode(true)); + this.svgPathSelected_.setAttribute('fill', 'none'); + this.svgPathSelected_.setAttribute( + 'filter', 'url(#' + this.constants.selectedGlowFilterId + ')'); + this.svgRoot.appendChild(this.svgPathSelected_); + } + } else { + if (this.svgPathSelected_) { + this.svgRoot.removeChild(this.svgPathSelected_); + this.svgPathSelected_ = null; + } + } } -}; -/** - * @override - */ -PathObject.prototype.flipRTL = function() { - PathObject.superClass_.flipRTL.call(this); - // Mirror each input outline path. - for (const key in this.outlines_) { - this.outlines_[key].setAttribute('transform', 'scale(-1 1)'); + /** + * @override + */ + updateReplacementFade(enable) { + this.setClass_('blocklyReplaceable', enable); + if (enable) { + this.svgPath.setAttribute( + 'filter', 'url(#' + this.constants.replacementGlowFilterId + ')'); + } else { + this.svgPath.removeAttribute('filter'); + } } -}; -/** - * @override - */ -PathObject.prototype.updateSelected = function(enable) { - this.setClass_('blocklySelected', enable); - if (enable) { - if (!this.svgPathSelected_) { - this.svgPathSelected_ = - /** @type {!SVGElement} */ (this.svgPath.cloneNode(true)); - this.svgPathSelected_.setAttribute('fill', 'none'); - this.svgPathSelected_.setAttribute( - 'filter', 'url(#' + this.constants.selectedGlowFilterId + ')'); - this.svgRoot.appendChild(this.svgPathSelected_); + /** + * @override + */ + updateShapeForInputHighlight(conn, enable) { + const name = conn.getParentInput().name; + const outlinePath = this.getOutlinePath_(name); + if (!outlinePath) { + return; } - } else { - if (this.svgPathSelected_) { - this.svgRoot.removeChild(this.svgPathSelected_); - this.svgPathSelected_ = null; + if (enable) { + outlinePath.setAttribute( + 'filter', 'url(#' + this.constants.replacementGlowFilterId + ')'); + } else { + outlinePath.removeAttribute('filter'); } } -}; -/** - * @override - */ -PathObject.prototype.updateReplacementFade = function(enable) { - this.setClass_('blocklyReplaceable', enable); - if (enable) { - this.svgPath.setAttribute( - 'filter', 'url(#' + this.constants.replacementGlowFilterId + ')'); - } else { - this.svgPath.removeAttribute('filter'); + /** + * Method that's called when the drawer is about to draw the block. + * @package + */ + beginDrawing() { + this.remainingOutlines_ = Object.create(null); + for (const key in this.outlines_) { + // The value set here isn't used anywhere, we are just using the + // object as a Set data structure. + this.remainingOutlines_[key] = 1; + } } -}; -/** - * @override - */ -PathObject.prototype.updateShapeForInputHighlight = function(conn, enable) { - const name = conn.getParentInput().name; - const outlinePath = this.getOutlinePath_(name); - if (!outlinePath) { - return; - } - if (enable) { - outlinePath.setAttribute( - 'filter', 'url(#' + this.constants.replacementGlowFilterId + ')'); - } else { - outlinePath.removeAttribute('filter'); + /** + * Method that's called when the drawer is done drawing. + * @package + */ + endDrawing() { + // Go through all remaining outlines that were not used this draw pass, and + // remove them. + if (this.remainingOutlines_) { + for (const key in this.remainingOutlines_) { + this.removeOutlinePath_(key); + } + } + this.remainingOutlines_ = null; } -}; -/** - * Method that's called when the drawer is about to draw the block. - * @package - */ -PathObject.prototype.beginDrawing = function() { - this.remainingOutlines_ = Object.create(null); - for (const key in this.outlines_) { - // The value set here isn't used anywhere, we are just using the - // object as a Set data structure. - this.remainingOutlines_[key] = 1; + /** + * Set the path generated by the renderer for an outline path on the respective + * outline path SVG element. + * @param {string} name The input name. + * @param {string} pathString The path. + * @package + */ + setOutlinePath(name, pathString) { + const outline = this.getOutlinePath_(name); + outline.setAttribute('d', pathString); + outline.setAttribute('fill', this.style.colourTertiary); } -}; -/** - * Method that's called when the drawer is done drawing. - * @package - */ -PathObject.prototype.endDrawing = function() { - // Go through all remaining outlines that were not used this draw pass, and - // remove them. - if (this.remainingOutlines_) { - for (const key in this.remainingOutlines_) { - this.removeOutlinePath_(key); + /** + * Create's an outline path for the specified input. + * @param {string} name The input name. + * @return {!SVGElement} The SVG outline path. + * @private + */ + getOutlinePath_(name) { + if (!this.outlines_[name]) { + this.outlines_[name] = dom.createSvgElement( + Svg.PATH, { + 'class': 'blocklyOutlinePath', + // IE doesn't like paths without the data definition, set empty + // default + 'd': '', + }, + this.svgRoot); } + if (this.remainingOutlines_) { + delete this.remainingOutlines_[name]; + } + return this.outlines_[name]; } - this.remainingOutlines_ = null; -}; - -/** - * Set the path generated by the renderer for an outline path on the respective - * outline path SVG element. - * @param {string} name The input name. - * @param {string} pathString The path. - * @package - */ -PathObject.prototype.setOutlinePath = function(name, pathString) { - const outline = this.getOutlinePath_(name); - outline.setAttribute('d', pathString); - outline.setAttribute('fill', this.style.colourTertiary); -}; -/** - * Create's an outline path for the specified input. - * @param {string} name The input name. - * @return {!SVGElement} The SVG outline path. - * @private - */ -PathObject.prototype.getOutlinePath_ = function(name) { - if (!this.outlines_[name]) { - this.outlines_[name] = dom.createSvgElement( - Svg.PATH, { - 'class': 'blocklyOutlinePath', - // IE doesn't like paths without the data definition, set empty - // default - 'd': '', - }, - this.svgRoot); - } - if (this.remainingOutlines_) { - delete this.remainingOutlines_[name]; + /** + * Remove an outline path that is associated with the specified input. + * @param {string} name The input name. + * @private + */ + removeOutlinePath_(name) { + this.outlines_[name].parentNode.removeChild(this.outlines_[name]); + delete this.outlines_[name]; } - return this.outlines_[name]; -}; - -/** - * Remove an outline path that is associated with the specified input. - * @param {string} name The input name. - * @private - */ -PathObject.prototype.removeOutlinePath_ = function(name) { - this.outlines_[name].parentNode.removeChild(this.outlines_[name]); - delete this.outlines_[name]; -}; +} exports.PathObject = PathObject; diff --git a/core/renderers/zelos/renderer.js b/core/renderers/zelos/renderer.js index 62d7a27f278..75ff4e24cf6 100644 --- a/core/renderers/zelos/renderer.js +++ b/core/renderers/zelos/renderer.js @@ -16,7 +16,6 @@ goog.module('Blockly.zelos.Renderer'); const blockRendering = goog.require('Blockly.blockRendering'); -const object = goog.require('Blockly.utils.object'); /* eslint-disable-next-line no-unused-vars */ const {BlockSvg} = goog.requireType('Blockly.BlockSvg'); const {ConnectionType} = goog.require('Blockly.ConnectionType'); @@ -39,109 +38,111 @@ const {WorkspaceSvg} = goog.requireType('Blockly.WorkspaceSvg'); /** * The zelos renderer. - * @param {string} name The renderer name. - * @package - * @constructor * @extends {BaseRenderer} - * @alias Blockly.zelos.Renderer */ -const Renderer = function(name) { - Renderer.superClass_.constructor.call(this, name); -}; -object.inherits(Renderer, BaseRenderer); +class Renderer extends BaseRenderer { + /** + * @param {string} name The renderer name. + * @package + * @alias Blockly.zelos.Renderer + */ + constructor(name) { + super(name); + } -/** - * Create a new instance of the renderer's constant provider. - * @return {!ConstantProvider} The constant provider. - * @protected - * @override - */ -Renderer.prototype.makeConstants_ = function() { - return new ConstantProvider(); -}; + /** + * Create a new instance of the renderer's constant provider. + * @return {!ConstantProvider} The constant provider. + * @protected + * @override + */ + makeConstants_() { + return new ConstantProvider(); + } -/** - * Create a new instance of the renderer's render info object. - * @param {!BlockSvg} block The block to measure. - * @return {!RenderInfo} The render info object. - * @protected - * @override - */ -Renderer.prototype.makeRenderInfo_ = function(block) { - return new RenderInfo(this, block); -}; + /** + * Create a new instance of the renderer's render info object. + * @param {!BlockSvg} block The block to measure. + * @return {!RenderInfo} The render info object. + * @protected + * @override + */ + makeRenderInfo_(block) { + return new RenderInfo(this, block); + } -/** - * Create a new instance of the renderer's drawer. - * @param {!BlockSvg} block The block to render. - * @param {!BaseRenderInfo} info An object containing all - * information needed to render this block. - * @return {!Drawer} The drawer. - * @protected - * @override - */ -Renderer.prototype.makeDrawer_ = function(block, info) { - return new Drawer( - block, - /** @type {!RenderInfo} */ (info)); -}; + /** + * Create a new instance of the renderer's drawer. + * @param {!BlockSvg} block The block to render. + * @param {!BaseRenderInfo} info An object containing all + * information needed to render this block. + * @return {!Drawer} The drawer. + * @protected + * @override + */ + makeDrawer_(block, info) { + return new Drawer( + block, + /** @type {!RenderInfo} */ (info)); + } -/** - * Create a new instance of the renderer's cursor drawer. - * @param {!WorkspaceSvg} workspace The workspace the cursor belongs to. - * @param {!Marker} marker The marker. - * @return {!MarkerSvg} The object in charge of drawing - * the marker. - * @package - * @override - */ -Renderer.prototype.makeMarkerDrawer = function(workspace, marker) { - return new MarkerSvg(workspace, this.getConstants(), marker); -}; + /** + * Create a new instance of the renderer's cursor drawer. + * @param {!WorkspaceSvg} workspace The workspace the cursor belongs to. + * @param {!Marker} marker The marker. + * @return {!MarkerSvg} The object in charge of drawing + * the marker. + * @package + * @override + */ + makeMarkerDrawer(workspace, marker) { + return new MarkerSvg(workspace, this.getConstants(), marker); + } -/** - * Create a new instance of a renderer path object. - * @param {!SVGElement} root The root SVG element. - * @param {!Theme.BlockStyle} style The style object to use for - * colouring. - * @return {!PathObject} The renderer path object. - * @package - * @override - */ -Renderer.prototype.makePathObject = function(root, style) { - return new PathObject( - root, style, - /** @type {!ConstantProvider} */ (this.getConstants())); -}; + /** + * Create a new instance of a renderer path object. + * @param {!SVGElement} root The root SVG element. + * @param {!Theme.BlockStyle} style The style object to use for + * colouring. + * @return {!PathObject} The renderer path object. + * @package + * @override + */ + makePathObject(root, style) { + return new PathObject( + root, style, + /** @type {!ConstantProvider} */ (this.getConstants())); + } -/** - * @override - */ -Renderer.prototype.shouldHighlightConnection = function(conn) { - return conn.type !== ConnectionType.INPUT_VALUE && - conn.type !== ConnectionType.OUTPUT_VALUE; -}; + /** + * @override + */ + shouldHighlightConnection(conn) { + return conn.type !== ConnectionType.INPUT_VALUE && + conn.type !== ConnectionType.OUTPUT_VALUE; + } -/** - * @override - */ -Renderer.prototype.getConnectionPreviewMethod = function( - closest, local, topBlock) { - if (local.type === ConnectionType.OUTPUT_VALUE) { - if (!closest.isConnected()) { - return InsertionMarkerManager.PREVIEW_TYPE.INPUT_OUTLINE; + /** + * @override + */ + getConnectionPreviewMethod( + closest, local, topBlock) { + if (local.type === ConnectionType.OUTPUT_VALUE) { + if (!closest.isConnected()) { + return InsertionMarkerManager.PREVIEW_TYPE.INPUT_OUTLINE; + } + // TODO: Returning this is a total hack, because we don't want to show + // a replacement fade, we want to show an outline affect. + // Sadly zelos does not support showing an outline around filled + // inputs, so we have to pretend like the connected block is getting + // replaced. + return InsertionMarkerManager.PREVIEW_TYPE.REPLACEMENT_FADE; } - // TODO: Returning this is a total hack, because we don't want to show - // a replacement fade, we want to show an outline affect. - // Sadly zelos does not support showing an outline around filled - // inputs, so we have to pretend like the connected block is getting - // replaced. - return InsertionMarkerManager.PREVIEW_TYPE.REPLACEMENT_FADE; - } - return Renderer.superClass_.getConnectionPreviewMethod( - closest, local, topBlock); -}; + return super.getConnectionPreviewMethod( + closest, local, topBlock); + } +} blockRendering.register('zelos', Renderer); From b3c2cf012d7c60a4bee0ef430c56aad863466782 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 12 Jan 2022 17:29:23 -0800 Subject: [PATCH 2/7] refactor: convert zelos measurables to es6 classes --- .../renderers/zelos/measurables/bottom_row.js | 63 ++++++++--------- core/renderers/zelos/measurables/inputs.js | 45 ++++++------ .../zelos/measurables/row_elements.js | 29 ++++---- core/renderers/zelos/measurables/top_row.js | 69 ++++++++++--------- 4 files changed, 105 insertions(+), 101 deletions(-) diff --git a/core/renderers/zelos/measurables/bottom_row.js b/core/renderers/zelos/measurables/bottom_row.js index 2a6045f646c..4d3180938e3 100644 --- a/core/renderers/zelos/measurables/bottom_row.js +++ b/core/renderers/zelos/measurables/bottom_row.js @@ -15,7 +15,6 @@ */ goog.module('Blockly.zelos.BottomRow'); -const object = goog.require('Blockly.utils.object'); const {BottomRow: BaseBottomRow} = goog.require('Blockly.blockRendering.BottomRow'); /* eslint-disable-next-line no-unused-vars */ const {ConstantProvider} = goog.requireType('Blockly.blockRendering.ConstantProvider'); @@ -26,40 +25,42 @@ const {ConstantProvider} = goog.requireType('Blockly.blockRendering.ConstantProv * a block as well as spacing information for the top row. * Elements in a bottom row can consist of corners, spacers and next * connections. - * @param {!ConstantProvider} constants The rendering - * constants provider. - * @package - * @constructor * @extends {BaseBottomRow} - * @alias Blockly.zelos.BottomRow */ -const BottomRow = function(constants) { - BottomRow.superClass_.constructor.call(this, constants); -}; -object.inherits(BottomRow, BaseBottomRow); +class BottomRow extends BaseBottomRow { + /** + * @param {!ConstantProvider} constants The rendering + * constants provider. + * @package + * @alias Blockly.zelos.BottomRow + */ + constructor(constants) { + super(constants); + } -/** - * @override - */ -BottomRow.prototype.endsWithElemSpacer = function() { - return false; -}; + /** + * @override + */ + endsWithElemSpacer() { + return false; + } -/** - * Render a round corner unless the block has an output connection. - * @override - */ -BottomRow.prototype.hasLeftSquareCorner = function(block) { - return !!block.outputConnection; -}; + /** + * Render a round corner unless the block has an output connection. + * @override + */ + hasLeftSquareCorner(block) { + return !!block.outputConnection; + } -/** - * Render a round corner unless the block has an output connection. - * @override - */ -BottomRow.prototype.hasRightSquareCorner = function(block) { - return !!block.outputConnection && !block.statementInputCount && - !block.nextConnection; -}; + /** + * Render a round corner unless the block has an output connection. + * @override + */ + hasRightSquareCorner(block) { + return !!block.outputConnection && !block.statementInputCount && + !block.nextConnection; + } +} exports.BottomRow = BottomRow; diff --git a/core/renderers/zelos/measurables/inputs.js b/core/renderers/zelos/measurables/inputs.js index ada8cdea3e1..11c517a77e0 100644 --- a/core/renderers/zelos/measurables/inputs.js +++ b/core/renderers/zelos/measurables/inputs.js @@ -16,7 +16,6 @@ */ goog.module('Blockly.zelos.StatementInput'); -const object = goog.require('Blockly.utils.object'); /* eslint-disable-next-line no-unused-vars */ const {ConstantProvider} = goog.requireType('Blockly.blockRendering.ConstantProvider'); /* eslint-disable-next-line no-unused-vars */ @@ -26,31 +25,33 @@ const {StatementInput: BaseStatementInput} = goog.require('Blockly.blockRenderin /** * An object containing information about the space a statement input takes up - * during rendering - * @param {!ConstantProvider} constants The rendering constants provider. - * @param {!Input} input The statement input to measure and store information - * for. - * @package - * @constructor + * during rendering. * @extends {BaseStatementInput} - * @alias Blockly.zelos.StatementInput */ -const StatementInput = function(constants, input) { - StatementInput.superClass_.constructor.call(this, constants, input); +class StatementInput extends BaseStatementInput { + /** + * @param {!ConstantProvider} constants The rendering constants provider. + * @param {!Input} input The statement input to measure and store information + * for. + * @package + * @alias Blockly.zelos.StatementInput + */ + constructor(constants, input) { + super(constants, input); - if (this.connectedBlock) { - // Find the bottom-most connected block in the stack. - let block = this.connectedBlock; - let nextBlock; - while ((nextBlock = block.getNextBlock())) { - block = nextBlock; - } - if (!block.nextConnection) { - this.height = this.connectedBlockHeight; - this.connectedBottomNextConnection = true; + if (this.connectedBlock) { + // Find the bottom-most connected block in the stack. + let block = this.connectedBlock; + let nextBlock; + while ((nextBlock = block.getNextBlock())) { + block = nextBlock; + } + if (!block.nextConnection) { + this.height = this.connectedBlockHeight; + this.connectedBottomNextConnection = true; + } } } -}; -object.inherits(StatementInput, BaseStatementInput); +} exports.StatementInput = StatementInput; diff --git a/core/renderers/zelos/measurables/row_elements.js b/core/renderers/zelos/measurables/row_elements.js index d0634f3ddb8..5a7d73b878a 100644 --- a/core/renderers/zelos/measurables/row_elements.js +++ b/core/renderers/zelos/measurables/row_elements.js @@ -16,7 +16,6 @@ */ goog.module('Blockly.zelos.RightConnectionShape'); -const object = goog.require('Blockly.utils.object'); /* eslint-disable-next-line no-unused-vars */ const {ConstantProvider} = goog.requireType('Blockly.blockRendering.ConstantProvider'); const {Measurable} = goog.require('Blockly.blockRendering.Measurable'); @@ -26,20 +25,22 @@ const {Types} = goog.require('Blockly.blockRendering.Types'); /** * An object containing information about the space a right connection shape * takes up during rendering. - * @param {!ConstantProvider} constants The rendering - * constants provider. - * @package - * @constructor * @extends {Measurable} - * @alias Blockly.zelos.RightConnectionShape */ -const RightConnectionShape = function(constants) { - RightConnectionShape.superClass_.constructor.call(this, constants); - this.type |= Types.getType('RIGHT_CONNECTION'); - // Size is dynamic - this.height = 0; - this.width = 0; -}; -object.inherits(RightConnectionShape, Measurable); +class RightConnectionShape extends Measurable { + /** + * @param {!ConstantProvider} constants The rendering + * constants provider. + * @package + * @alias Blockly.zelos.RightConnectionShape + */ + constructor(constants) { + super(constants); + this.type |= Types.getType('RIGHT_CONNECTION'); + // Size is dynamic + this.height = 0; + this.width = 0; + } +} exports.RightConnectionShape = RightConnectionShape; diff --git a/core/renderers/zelos/measurables/top_row.js b/core/renderers/zelos/measurables/top_row.js index 50df37d9648..049ae0f9184 100644 --- a/core/renderers/zelos/measurables/top_row.js +++ b/core/renderers/zelos/measurables/top_row.js @@ -15,7 +15,6 @@ */ goog.module('Blockly.zelos.TopRow'); -const object = goog.require('Blockly.utils.object'); /* eslint-disable-next-line no-unused-vars */ const {ConstantProvider} = goog.requireType('Blockly.blockRendering.ConstantProvider'); const {TopRow: BaseTopRow} = goog.require('Blockly.blockRendering.TopRow'); @@ -28,43 +27,45 @@ const {TopRow: BaseTopRow} = goog.require('Blockly.blockRendering.TopRow'); * connections. * After this constructor is called, the row will contain all non-spacer * elements it needs. - * @param {!ConstantProvider} constants The rendering - * constants provider. - * @package - * @constructor * @extends {BaseTopRow} - * @alias Blockly.zelos.TopRow */ -const TopRow = function(constants) { - TopRow.superClass_.constructor.call(this, constants); -}; -object.inherits(TopRow, BaseTopRow); +class TopRow extends BaseTopRow { + /** + * @param {!ConstantProvider} constants The rendering + * constants provider. + * @package + * @alias Blockly.zelos.TopRow + */ + constructor(constants) { + super(constants); + } -/** - * @override - */ -TopRow.prototype.endsWithElemSpacer = function() { - return false; -}; + /** + * @override + */ + endsWithElemSpacer() { + return false; + } -/** - * Render a round corner unless the block has an output connection. - * @override - */ -TopRow.prototype.hasLeftSquareCorner = function(block) { - const hasHat = - (block.hat ? block.hat === 'cap' : this.constants_.ADD_START_HATS) && - !block.outputConnection && !block.previousConnection; - return !!block.outputConnection || hasHat; -}; + /** + * Render a round corner unless the block has an output connection. + * @override + */ + hasLeftSquareCorner(block) { + const hasHat = + (block.hat ? block.hat === 'cap' : this.constants_.ADD_START_HATS) && + !block.outputConnection && !block.previousConnection; + return !!block.outputConnection || hasHat; + } -/** - * Render a round corner unless the block has an output connection. - * @override - */ -TopRow.prototype.hasRightSquareCorner = function(block) { - return !!block.outputConnection && !block.statementInputCount && - !block.nextConnection; -}; + /** + * Render a round corner unless the block has an output connection. + * @override + */ + hasRightSquareCorner(block) { + return !!block.outputConnection && !block.statementInputCount && + !block.nextConnection; + } +} exports.TopRow = TopRow; From 2da01e31cfdf531c46b673639f1e256c9d0dd978 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 12 Jan 2022 17:33:06 -0800 Subject: [PATCH 3/7] refactor: convert thrasos classes to es6 classes --- core/renderers/thrasos/info.js | 498 ++++++++++++++--------------- core/renderers/thrasos/renderer.js | 42 +-- 2 files changed, 270 insertions(+), 270 deletions(-) diff --git a/core/renderers/thrasos/info.js b/core/renderers/thrasos/info.js index a8cb2bfed83..584fecd9553 100644 --- a/core/renderers/thrasos/info.js +++ b/core/renderers/thrasos/info.js @@ -17,7 +17,6 @@ */ goog.module('Blockly.thrasos.RenderInfo'); -const object = goog.require('Blockly.utils.object'); /* eslint-disable-next-line no-unused-vars */ const {BlockSvg} = goog.requireType('Blockly.BlockSvg'); /* eslint-disable-next-line no-unused-vars */ @@ -35,295 +34,296 @@ const {Types} = goog.require('Blockly.blockRendering.Types'); * This measure pass does not propagate changes to the block (although fields * may choose to rerender when getSize() is called). However, calling it * repeatedly may be expensive. - * - * @param {!Renderer} renderer The renderer in use. - * @param {!BlockSvg} block The block to measure. - * @constructor - * @package * @extends {BaseRenderInfo} - * @alias Blockly.thrasos.RenderInfo - */ -const RenderInfo = function(renderer, block) { - RenderInfo.superClass_.constructor.call(this, renderer, block); -}; -object.inherits(RenderInfo, BaseRenderInfo); - -/** - * Get the block renderer in use. - * @return {!Renderer} The block renderer in use. - * @package */ -RenderInfo.prototype.getRenderer = function() { - return /** @type {!Renderer} */ (this.renderer_); -}; +class RenderInfo extends BaseRenderInfo { + /** + * @param {!Renderer} renderer The renderer in use. + * @param {!BlockSvg} block The block to measure. + * @package + * @alias Blockly.thrasos.RenderInfo + */ + constructor(renderer, block) { + super(renderer, block); + } -/** - * @override - */ -RenderInfo.prototype.addElemSpacing_ = function() { - let hasExternalInputs = false; - for (let i = 0; i < this.rows.length; i++) { - const row = this.rows[i]; - if (row.hasExternalInput) { - hasExternalInputs = true; - break; - } + /** + * Get the block renderer in use. + * @return {!Renderer} The block renderer in use. + * @package + */ + getRenderer() { + return /** @type {!Renderer} */ (this.renderer_); } - for (let i = 0; i < this.rows.length; i++) { - const row = this.rows[i]; - const oldElems = row.elements; - row.elements = []; - // No spacing needed before the corner on the top row or the bottom row. - if (row.startsWithElemSpacer()) { - // There's a spacer before the first element in the row. - row.elements.push(new InRowSpacer( - this.constants_, this.getInRowSpacing_(null, oldElems[0]))); - } - for (let e = 0; e < oldElems.length - 1; e++) { - row.elements.push(oldElems[e]); - const spacing = this.getInRowSpacing_(oldElems[e], oldElems[e + 1]); - row.elements.push(new InRowSpacer(this.constants_, spacing)); + + /** + * @override + */ + addElemSpacing_() { + let hasExternalInputs = false; + for (let i = 0; i < this.rows.length; i++) { + const row = this.rows[i]; + if (row.hasExternalInput) { + hasExternalInputs = true; + break; + } } - row.elements.push(oldElems[oldElems.length - 1]); - if (row.endsWithElemSpacer()) { - let spacing = this.getInRowSpacing_(oldElems[oldElems.length - 1], null); - if (hasExternalInputs && row.hasDummyInput) { - spacing += this.constants_.TAB_WIDTH; + for (let i = 0; i < this.rows.length; i++) { + const row = this.rows[i]; + const oldElems = row.elements; + row.elements = []; + // No spacing needed before the corner on the top row or the bottom row. + if (row.startsWithElemSpacer()) { + // There's a spacer before the first element in the row. + row.elements.push(new InRowSpacer( + this.constants_, this.getInRowSpacing_(null, oldElems[0]))); + } + for (let e = 0; e < oldElems.length - 1; e++) { + row.elements.push(oldElems[e]); + const spacing = this.getInRowSpacing_(oldElems[e], oldElems[e + 1]); + row.elements.push(new InRowSpacer(this.constants_, spacing)); + } + row.elements.push(oldElems[oldElems.length - 1]); + if (row.endsWithElemSpacer()) { + let spacing = this.getInRowSpacing_(oldElems[oldElems.length - 1], null); + if (hasExternalInputs && row.hasDummyInput) { + spacing += this.constants_.TAB_WIDTH; + } + // There's a spacer after the last element in the row. + row.elements.push(new InRowSpacer(this.constants_, spacing)); } - // There's a spacer after the last element in the row. - row.elements.push(new InRowSpacer(this.constants_, spacing)); } } -}; -/** - * @override - */ -RenderInfo.prototype.getInRowSpacing_ = function(prev, next) { - if (!prev) { - // Between an editable field and the beginning of the row. - if (next && Types.isField(next) && - (/** @type {Field} */ (next)).isEditable) { - return this.constants_.MEDIUM_PADDING; - } - // Inline input at the beginning of the row. - if (next && Types.isInlineInput(next)) { - return this.constants_.MEDIUM_LARGE_PADDING; - } - if (next && Types.isStatementInput(next)) { - return this.constants_.STATEMENT_INPUT_PADDING_LEFT; + /** + * @override + */ + getInRowSpacing_(prev, next) { + if (!prev) { + // Between an editable field and the beginning of the row. + if (next && Types.isField(next) && + (/** @type {Field} */ (next)).isEditable) { + return this.constants_.MEDIUM_PADDING; + } + // Inline input at the beginning of the row. + if (next && Types.isInlineInput(next)) { + return this.constants_.MEDIUM_LARGE_PADDING; + } + if (next && Types.isStatementInput(next)) { + return this.constants_.STATEMENT_INPUT_PADDING_LEFT; + } + // Anything else at the beginning of the row. + return this.constants_.LARGE_PADDING; } - // Anything else at the beginning of the row. - return this.constants_.LARGE_PADDING; - } - // 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) && (/** @type {Field} */ (prev)).isEditable) { - return this.constants_.MEDIUM_PADDING; - } - // Padding at the end of an icon-only row to make the block shape clearer. - if (Types.isIcon(prev)) { - return (this.constants_.LARGE_PADDING * 2) + 1; - } - if (Types.isHat(prev)) { - return this.constants_.NO_PADDING; - } - // Establish a minimum width for a block with a previous or next connection. - if (Types.isPreviousOrNextConnection(prev)) { + // 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) && (/** @type {Field} */ (prev)).isEditable) { + return this.constants_.MEDIUM_PADDING; + } + // Padding at the end of an icon-only row to make the block shape clearer. + if (Types.isIcon(prev)) { + return (this.constants_.LARGE_PADDING * 2) + 1; + } + if (Types.isHat(prev)) { + return this.constants_.NO_PADDING; + } + // Establish a minimum width for a block with a previous or next connection. + if (Types.isPreviousOrNextConnection(prev)) { + return this.constants_.LARGE_PADDING; + } + // Between rounded corner and the end of the row. + if (Types.isLeftRoundedCorner(prev)) { + return this.constants_.MIN_BLOCK_WIDTH; + } + // Between a jagged edge and the end of the row. + if (Types.isJaggedEdge(prev)) { + return this.constants_.NO_PADDING; + } + // Between noneditable fields and icons and the end of the row. return this.constants_.LARGE_PADDING; } - // Between rounded corner and the end of the row. - if (Types.isLeftRoundedCorner(prev)) { - return this.constants_.MIN_BLOCK_WIDTH; + + // Between inputs and the end of the row. + if (Types.isInput(prev) && !next) { + if (Types.isExternalInput(prev)) { + return this.constants_.NO_PADDING; + } else if (Types.isInlineInput(prev)) { + return this.constants_.LARGE_PADDING; + } else if (Types.isStatementInput(prev)) { + return this.constants_.NO_PADDING; + } } - // Between a jagged edge and the end of the row. - if (Types.isJaggedEdge(prev)) { - return this.constants_.NO_PADDING; + + // 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) && (/** @type {Field} */ (prev)).isEditable) { + if (Types.isInlineInput(next)) { + return this.constants_.SMALL_PADDING; + } else if (Types.isExternalInput(next)) { + return this.constants_.SMALL_PADDING; + } + } else { + if (Types.isInlineInput(next)) { + return this.constants_.MEDIUM_LARGE_PADDING; + } else if (Types.isExternalInput(next)) { + return this.constants_.MEDIUM_LARGE_PADDING; + } else if (Types.isStatementInput(next)) { + return this.constants_.LARGE_PADDING; + } + } + return this.constants_.LARGE_PADDING - 1; } - // Between noneditable fields and icons and the end of the row. - return this.constants_.LARGE_PADDING; - } - // Between inputs and the end of the row. - if (Types.isInput(prev) && !next) { - if (Types.isExternalInput(prev)) { - return this.constants_.NO_PADDING; - } else if (Types.isInlineInput(prev)) { + // Spacing between an icon and an icon or field. + if (Types.isIcon(prev) && next && !Types.isInput(next)) { return this.constants_.LARGE_PADDING; - } else if (Types.isStatementInput(prev)) { - return this.constants_.NO_PADDING; } - } - // 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) && (/** @type {Field} */ (prev)).isEditable) { - if (Types.isInlineInput(next)) { - return this.constants_.SMALL_PADDING; - } else if (Types.isExternalInput(next)) { - return this.constants_.SMALL_PADDING; - } - } else { - if (Types.isInlineInput(next)) { - return this.constants_.MEDIUM_LARGE_PADDING; - } else if (Types.isExternalInput(next)) { - return this.constants_.MEDIUM_LARGE_PADDING; - } else if (Types.isStatementInput(next)) { + // Spacing between an inline input and a field. + if (Types.isInlineInput(prev) && next && Types.isField(next)) { + // Editable field after inline input. + if ((/** @type {Field} */ (next)).isEditable) { + return this.constants_.MEDIUM_PADDING; + } else { + // Noneditable field after inline input. return this.constants_.LARGE_PADDING; } } - return this.constants_.LARGE_PADDING - 1; - } - - // Spacing between an icon and an icon or field. - if (Types.isIcon(prev) && next && !Types.isInput(next)) { - return this.constants_.LARGE_PADDING; - } - // Spacing between an inline input and a field. - if (Types.isInlineInput(prev) && next && Types.isField(next)) { - // Editable field after inline input. - if ((/** @type {Field} */ (next)).isEditable) { - return this.constants_.MEDIUM_PADDING; - } else { - // Noneditable field after inline input. - return this.constants_.LARGE_PADDING; + if (Types.isLeftSquareCorner(prev) && next) { + // Spacing between a hat and a corner + if (Types.isHat(next)) { + return this.constants_.NO_PADDING; + } + // Spacing between a square corner and a previous or next connection + if (Types.isPreviousConnection(next) || Types.isNextConnection(next)) { + return next.notchOffset; + } } - } - if (Types.isLeftSquareCorner(prev) && next) { - // Spacing between a hat and a corner - if (Types.isHat(next)) { - return this.constants_.NO_PADDING; + // Spacing between a rounded corner and a previous or next connection. + if (Types.isLeftRoundedCorner(prev) && next) { + return next.notchOffset - this.constants_.CORNER_RADIUS; } - // Spacing between a square corner and a previous or next connection - if (Types.isPreviousConnection(next) || Types.isNextConnection(next)) { - return next.notchOffset; - } - } - // Spacing between a rounded corner and a previous or next connection. - if (Types.isLeftRoundedCorner(prev) && next) { - return next.notchOffset - this.constants_.CORNER_RADIUS; - } + // Spacing between two fields of the same editability. + if (Types.isField(prev) && next && Types.isField(next) && + ((/** @type {Field} */ (prev)).isEditable === + (/** @type {Field} */ (next)).isEditable)) { + return this.constants_.LARGE_PADDING; + } - // Spacing between two fields of the same editability. - if (Types.isField(prev) && next && Types.isField(next) && - ((/** @type {Field} */ (prev)).isEditable === - (/** @type {Field} */ (next)).isEditable)) { - return this.constants_.LARGE_PADDING; - } + // Spacing between anything and a jagged edge. + if (next && Types.isJaggedEdge(next)) { + return this.constants_.LARGE_PADDING; + } - // Spacing between anything and a jagged edge. - if (next && Types.isJaggedEdge(next)) { - return this.constants_.LARGE_PADDING; + return this.constants_.MEDIUM_PADDING; } - return this.constants_.MEDIUM_PADDING; -}; - -/** - * @override - */ -RenderInfo.prototype.getSpacerRowHeight_ = function(prev, next) { - // If we have an empty block add a spacer to increase the height. - if (Types.isTopRow(prev) && Types.isBottomRow(next)) { - return this.constants_.EMPTY_BLOCK_SPACER_HEIGHT; - } - // Top and bottom rows act as a spacer so we don't need any extra padding. - if (Types.isTopRow(prev) || Types.isBottomRow(next)) { - return this.constants_.NO_PADDING; - } - if (prev.hasExternalInput && next.hasExternalInput) { - return this.constants_.LARGE_PADDING; - } - if (!prev.hasStatement && next.hasStatement) { - return this.constants_.BETWEEN_STATEMENT_PADDING_Y; - } - if (prev.hasStatement && next.hasStatement) { - return this.constants_.LARGE_PADDING; - } - if (prev.hasDummyInput || next.hasDummyInput) { - return this.constants_.LARGE_PADDING; + /** + * @override + */ + getSpacerRowHeight_(prev, next) { + // If we have an empty block add a spacer to increase the height. + if (Types.isTopRow(prev) && Types.isBottomRow(next)) { + return this.constants_.EMPTY_BLOCK_SPACER_HEIGHT; + } + // Top and bottom rows act as a spacer so we don't need any extra padding. + if (Types.isTopRow(prev) || Types.isBottomRow(next)) { + return this.constants_.NO_PADDING; + } + if (prev.hasExternalInput && next.hasExternalInput) { + return this.constants_.LARGE_PADDING; + } + if (!prev.hasStatement && next.hasStatement) { + return this.constants_.BETWEEN_STATEMENT_PADDING_Y; + } + if (prev.hasStatement && next.hasStatement) { + return this.constants_.LARGE_PADDING; + } + if (prev.hasDummyInput || next.hasDummyInput) { + return this.constants_.LARGE_PADDING; + } + return this.constants_.MEDIUM_PADDING; } - return this.constants_.MEDIUM_PADDING; -}; -/** - * @override - */ -RenderInfo.prototype.getElemCenterline_ = function(row, elem) { - if (Types.isSpacer(elem)) { - return row.yPos + elem.height / 2; - } - if (Types.isBottomRow(row)) { - const baseline = row.yPos + row.height - row.descenderHeight; - if (Types.isNextConnection(elem)) { - return baseline + elem.height / 2; + /** + * @override + */ + getElemCenterline_(row, elem) { + if (Types.isSpacer(elem)) { + return row.yPos + elem.height / 2; } - return baseline - elem.height / 2; - } - if (Types.isTopRow(row)) { - if (Types.isHat(elem)) { - return row.capline - elem.height / 2; + if (Types.isBottomRow(row)) { + 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)) { + if (Types.isHat(elem)) { + return row.capline - elem.height / 2; + } + return row.capline + elem.height / 2; } - return row.capline + elem.height / 2; - } - let result = row.yPos; - if (Types.isField(elem) && row.hasStatement) { - const offset = this.constants_.TALL_INPUT_FIELD_OFFSET_Y + elem.height / 2; - result += offset; - } else { - result += (row.height / 2); + let result = row.yPos; + if (Types.isField(elem) && row.hasStatement) { + const offset = this.constants_.TALL_INPUT_FIELD_OFFSET_Y + elem.height / 2; + result += offset; + } else { + result += (row.height / 2); + } + return result; } - return result; -}; -/** - * @override - */ -RenderInfo.prototype.finalize_ = function() { - // Performance note: this could be combined with the draw pass, if the time - // that this takes is excessive. But it shouldn't be, because it only - // accesses and sets properties that already exist on the objects. - let widestRowWithConnectedBlocks = 0; - let yCursor = 0; - for (let i = 0; i < this.rows.length; i++) { - const row = this.rows[i]; - row.yPos = yCursor; - row.xPos = this.startX; - yCursor += row.height; + /** + * @override + */ + finalize_() { + // Performance note: this could be combined with the draw pass, if the time + // that this takes is excessive. But it shouldn't be, because it only + // accesses and sets properties that already exist on the objects. + let widestRowWithConnectedBlocks = 0; + let yCursor = 0; + for (let i = 0; i < this.rows.length; i++) { + const row = this.rows[i]; + row.yPos = yCursor; + row.xPos = this.startX; + yCursor += row.height; - widestRowWithConnectedBlocks = - Math.max(widestRowWithConnectedBlocks, row.widthWithConnectedBlocks); - // Add padding to the bottom row if block height is less than minimum - const heightWithoutHat = yCursor - this.topRow.ascenderHeight; - if (row === this.bottomRow && - heightWithoutHat < this.constants_.MIN_BLOCK_HEIGHT) { - // But the hat height shouldn't be part of this. - const diff = this.constants_.MIN_BLOCK_HEIGHT - heightWithoutHat; - this.bottomRow.height += diff; - yCursor += diff; + widestRowWithConnectedBlocks = + Math.max(widestRowWithConnectedBlocks, row.widthWithConnectedBlocks); + // Add padding to the bottom row if block height is less than minimum + const heightWithoutHat = yCursor - this.topRow.ascenderHeight; + if (row === this.bottomRow && + heightWithoutHat < this.constants_.MIN_BLOCK_HEIGHT) { + // But the hat height shouldn't be part of this. + const diff = this.constants_.MIN_BLOCK_HEIGHT - heightWithoutHat; + this.bottomRow.height += diff; + yCursor += diff; + } + this.recordElemPositions_(row); + } + if (this.outputConnection && this.block_.nextConnection && + this.block_.nextConnection.isConnected()) { + // Include width of connected block in value to stack width measurement. + widestRowWithConnectedBlocks = Math.max( + widestRowWithConnectedBlocks, + this.block_.nextConnection.targetBlock().getHeightWidth().width); } - this.recordElemPositions_(row); - } - if (this.outputConnection && this.block_.nextConnection && - this.block_.nextConnection.isConnected()) { - // Include width of connected block in value to stack width measurement. - widestRowWithConnectedBlocks = Math.max( - widestRowWithConnectedBlocks, - this.block_.nextConnection.targetBlock().getHeightWidth().width); - } - this.bottomRow.baseline = yCursor - this.bottomRow.descenderHeight; - this.widthWithChildren = widestRowWithConnectedBlocks + this.startX; + this.bottomRow.baseline = yCursor - this.bottomRow.descenderHeight; + this.widthWithChildren = widestRowWithConnectedBlocks + this.startX; - this.height = yCursor; - this.startY = this.topRow.capline; -}; + this.height = yCursor; + this.startY = this.topRow.capline; + } +} exports.RenderInfo = RenderInfo; diff --git a/core/renderers/thrasos/renderer.js b/core/renderers/thrasos/renderer.js index b192c314514..0c19240ba34 100644 --- a/core/renderers/thrasos/renderer.js +++ b/core/renderers/thrasos/renderer.js @@ -16,7 +16,6 @@ goog.module('Blockly.thrasos.Renderer'); const blockRendering = goog.require('Blockly.blockRendering'); -const object = goog.require('Blockly.utils.object'); /* eslint-disable-next-line no-unused-vars */ const {BlockSvg} = goog.requireType('Blockly.BlockSvg'); const {RenderInfo} = goog.require('Blockly.thrasos.RenderInfo'); @@ -25,28 +24,29 @@ const {Renderer: BaseRenderer} = goog.require('Blockly.blockRendering.Renderer') /** * The thrasos renderer. - * @param {string} name The renderer name. - * @package - * @constructor * @extends {BaseRenderer} - * @alias Blockly.thrasos.Renderer */ -const Renderer = function(name) { - Renderer.superClass_.constructor.call(this, name); -}; -object.inherits(Renderer, BaseRenderer); - -/** - * Create a new instance of the renderer's render info object. - * @param {!BlockSvg} block The block to measure. - * @return {!RenderInfo} The render info object. - * @protected - * @override - */ -Renderer.prototype.makeRenderInfo_ = function(block) { - return new RenderInfo(this, block); -}; - +class Renderer extends BaseRenderer { + /** + * @param {string} name The renderer name. + * @package + * @alias Blockly.thrasos.Renderer + */ + constructor(name) { + super(name); + } + + /** + * Create a new instance of the renderer's render info object. + * @param {!BlockSvg} block The block to measure. + * @return {!RenderInfo} The render info object. + * @protected + * @override + */ + makeRenderInfo_(block) { + return new RenderInfo(this, block); + } +} blockRendering.register('thrasos', Renderer); From 0db5d7255cbd2d00289b4f3e5fc2543746f24fed Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 12 Jan 2022 17:40:49 -0800 Subject: [PATCH 4/7] refactor: convert minimalist classes to es6 classes --- core/renderers/minimalist/constants.js | 17 +++--- core/renderers/minimalist/drawer.js | 23 ++++---- core/renderers/minimalist/info.js | 38 ++++++------ core/renderers/minimalist/renderer.js | 81 +++++++++++++------------- 4 files changed, 81 insertions(+), 78 deletions(-) diff --git a/core/renderers/minimalist/constants.js b/core/renderers/minimalist/constants.js index eb89ea2ad8f..48817862918 100644 --- a/core/renderers/minimalist/constants.js +++ b/core/renderers/minimalist/constants.js @@ -17,20 +17,21 @@ */ goog.module('Blockly.minimalist.ConstantProvider'); -const object = goog.require('Blockly.utils.object'); const {ConstantProvider: BaseConstantProvider} = goog.require('Blockly.blockRendering.ConstantProvider'); /** * An object that provides constants for rendering blocks in the sample. - * @constructor - * @package * @extends {BaseConstantProvider} - * @alias Blockly.minimalist.ConstantProvider */ -const ConstantProvider = function() { - ConstantProvider.superClass_.constructor.call(this); -}; -object.inherits(ConstantProvider, BaseConstantProvider); +class ConstantProvider extends BaseConstantProvider { + /** + * @package + * @alias Blockly.minimalist.ConstantProvider + */ + constructor() { + super(); + } +} exports.ConstantProvider = ConstantProvider; diff --git a/core/renderers/minimalist/drawer.js b/core/renderers/minimalist/drawer.js index 13eb682a1d5..829e24b4a90 100644 --- a/core/renderers/minimalist/drawer.js +++ b/core/renderers/minimalist/drawer.js @@ -15,7 +15,6 @@ */ goog.module('Blockly.minimalist.Drawer'); -const object = goog.require('Blockly.utils.object'); /* eslint-disable-next-line no-unused-vars */ const {BlockSvg} = goog.requireType('Blockly.BlockSvg'); const {Drawer: BaseDrawer} = goog.require('Blockly.blockRendering.Drawer'); @@ -25,17 +24,19 @@ const {RenderInfo} = goog.requireType('Blockly.minimalist.RenderInfo'); /** * An object that draws a block based on the given rendering information. - * @param {!BlockSvg} block The block to render. - * @param {!RenderInfo} info An object containing all - * information needed to render this block. - * @package - * @constructor * @extends {BaseDrawer} - * @alias Blockly.minimalist.Drawer */ -const Drawer = function(block, info) { - Drawer.superClass_.constructor.call(this, block, info); -}; -object.inherits(Drawer, BaseDrawer); +class Drawer extends BaseDrawer { + /** + * @param {!BlockSvg} block The block to render. + * @param {!RenderInfo} info An object containing all + * information needed to render this block. + * @package + * @alias Blockly.minimalist.Drawer + */ + constructor(block, info) { + super(block, info); + } +} exports.Drawer = Drawer; diff --git a/core/renderers/minimalist/info.js b/core/renderers/minimalist/info.js index 36e66f9e514..1f2172b3729 100644 --- a/core/renderers/minimalist/info.js +++ b/core/renderers/minimalist/info.js @@ -15,7 +15,6 @@ */ goog.module('Blockly.minimalist.RenderInfo'); -const object = goog.require('Blockly.utils.object'); /* eslint-disable-next-line no-unused-vars */ const {BlockSvg} = goog.requireType('Blockly.BlockSvg'); const {RenderInfo: BaseRenderInfo} = goog.require('Blockly.blockRendering.RenderInfo'); @@ -29,26 +28,27 @@ const {Renderer} = goog.requireType('Blockly.minimalist.Renderer'); * This measure pass does not propagate changes to the block (although fields * may choose to rerender when getSize() is called). However, calling it * repeatedly may be expensive. - * - * @param {!Renderer} renderer The renderer in use. - * @param {!BlockSvg} block The block to measure. - * @constructor - * @package * @extends {BaseRenderInfo} - * @alias Blockly.minimalist.RenderInfo */ -const RenderInfo = function(renderer, block) { - RenderInfo.superClass_.constructor.call(this, renderer, block); -}; -object.inherits(RenderInfo, BaseRenderInfo); +class RenderInfo extends BaseRenderInfo { + /** + * @param {!Renderer} renderer The renderer in use. + * @param {!BlockSvg} block The block to measure. + * @package + * @alias Blockly.minimalist.RenderInfo + */ + constructor(renderer, block) { + super(renderer, block); + } -/** - * Get the block renderer in use. - * @return {!Renderer} The block renderer in use. - * @package - */ -RenderInfo.prototype.getRenderer = function() { - return /** @type {!Renderer} */ (this.renderer_); -}; + /** + * Get the block renderer in use. + * @return {!Renderer} The block renderer in use. + * @package + */ + getRenderer() { + return /** @type {!Renderer} */ (this.renderer_); + } +} exports.RenderInfo = RenderInfo; diff --git a/core/renderers/minimalist/renderer.js b/core/renderers/minimalist/renderer.js index 6cc7c9e2f1c..46522172db1 100644 --- a/core/renderers/minimalist/renderer.js +++ b/core/renderers/minimalist/renderer.js @@ -16,7 +16,6 @@ goog.module('Blockly.minimalist.Renderer'); const blockRendering = goog.require('Blockly.blockRendering'); -const object = goog.require('Blockly.utils.object'); /* eslint-disable-next-line no-unused-vars */ const {BlockSvg} = goog.requireType('Blockly.BlockSvg'); const {ConstantProvider} = goog.require('Blockly.minimalist.ConstantProvider'); @@ -29,50 +28,52 @@ const {Renderer: BaseRenderer} = goog.require('Blockly.blockRendering.Renderer') /** * The minimalist renderer. - * @param {string} name The renderer name. - * @package - * @constructor * @extends {BaseRenderer} - * @alias Blockly.minimalist.Renderer */ -const Renderer = function(name) { - Renderer.superClass_.constructor.call(this, name); -}; -object.inherits(Renderer, BaseRenderer); +class Renderer extends BaseRenderer { + /** + * @param {string} name The renderer name. + * @package + * @alias Blockly.minimalist.Renderer + */ + constructor(name) { + super(name); + } -/** - * Create a new instance of the renderer's constant provider. - * @return {!ConstantProvider} The constant provider. - * @protected - * @override - */ -Renderer.prototype.makeConstants_ = function() { - return new ConstantProvider(); -}; + /** + * Create a new instance of the renderer's constant provider. + * @return {!ConstantProvider} The constant provider. + * @protected + * @override + */ + makeConstants_() { + return new ConstantProvider(); + } -/** - * Create a new instance of the renderer's render info object. - * @param {!BlockSvg} block The block to measure. - * @return {!RenderInfo} The render info object. - * @protected - * @override - */ -Renderer.prototype.makeRenderInfo_ = function(block) { - return new RenderInfo(this, block); -}; + /** + * Create a new instance of the renderer's render info object. + * @param {!BlockSvg} block The block to measure. + * @return {!RenderInfo} The render info object. + * @protected + * @override + */ + makeRenderInfo_(block) { + return new RenderInfo(this, block); + } -/** - * Create a new instance of the renderer's drawer. - * @param {!BlockSvg} block The block to render. - * @param {!BaseRenderInfo} info An object containing all - * information needed to render this block. - * @return {!Drawer} The drawer. - * @protected - * @override - */ -Renderer.prototype.makeDrawer_ = function(block, info) { - return new Drawer(block, /** @type {!RenderInfo} */ (info)); -}; + /** + * Create a new instance of the renderer's drawer. + * @param {!BlockSvg} block The block to render. + * @param {!BaseRenderInfo} info An object containing all + * information needed to render this block. + * @return {!Drawer} The drawer. + * @protected + * @override + */ + makeDrawer_(block, info) { + return new Drawer(block, /** @type {!RenderInfo} */ (info)); + } +} blockRendering.register('minimalist', Renderer); From f65991966aa9381963ca285eacfc11c624cf8a57 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 13 Jan 2022 10:25:03 -0800 Subject: [PATCH 5/7] refactor: update geras classes to es6 classes --- core/renderers/geras/constants.js | 74 +- core/renderers/geras/drawer.js | 299 +++---- core/renderers/geras/highlight_constants.js | 481 ++++++------ core/renderers/geras/highlighter.js | 409 +++++----- core/renderers/geras/info.js | 740 +++++++++--------- .../geras/measurables/inline_input.js | 37 +- .../geras/measurables/statement_input.js | 35 +- core/renderers/geras/renderer.js | 199 ++--- 8 files changed, 1166 insertions(+), 1108 deletions(-) diff --git a/core/renderers/geras/constants.js b/core/renderers/geras/constants.js index 9f4f77c8c6e..26e7f5ebd16 100644 --- a/core/renderers/geras/constants.js +++ b/core/renderers/geras/constants.js @@ -17,57 +17,57 @@ */ goog.module('Blockly.geras.ConstantProvider'); -const object = goog.require('Blockly.utils.object'); const {ConstantProvider: BaseConstantProvider} = goog.require('Blockly.blockRendering.ConstantProvider'); /** * An object that provides constants for rendering blocks in Geras mode. - * @constructor - * @package * @extends {BaseConstantProvider} - * @alias Blockly.geras.ConstantProvider */ -const ConstantProvider = function() { - ConstantProvider.superClass_.constructor.call(this); - +class ConstantProvider extends BaseConstantProvider { /** - * @override + * @package + * @alias Blockly.geras.ConstantProvider */ - this.FIELD_TEXT_BASELINE_CENTER = false; + constructor() { + super(); - // The dark/shadow path in classic rendering is the same as the normal block - // path, but translated down one and right one. - this.DARK_PATH_OFFSET = 1; + /** + * @override + */ + this.FIELD_TEXT_BASELINE_CENTER = false; - /** - * The maximum width of a bottom row that follows a statement input and has - * inputs inline. - * @type {number} - */ - this.MAX_BOTTOM_WIDTH = 30; + // The dark/shadow path in classic rendering is the same as the normal block + // path, but translated down one and right one. + this.DARK_PATH_OFFSET = 1; + + /** + * The maximum width of a bottom row that follows a statement input and has + * inputs inline. + * @type {number} + */ + this.MAX_BOTTOM_WIDTH = 30; + + /** + * @override + */ + this.STATEMENT_BOTTOM_SPACER = -this.NOTCH_HEIGHT / 2; + } /** * @override */ - this.STATEMENT_BOTTOM_SPACER = -this.NOTCH_HEIGHT / 2; -}; -object.inherits(ConstantProvider, BaseConstantProvider); - - -/** - * @override - */ -ConstantProvider.prototype.getCSS_ = function(selector) { - return ConstantProvider.superClass_.getCSS_.call(this, selector).concat([ - /* eslint-disable indent */ - // Insertion marker. - selector + ' .blocklyInsertionMarker>.blocklyPathLight,', - selector + ' .blocklyInsertionMarker>.blocklyPathDark {', - 'fill-opacity: ' + this.INSERTION_MARKER_OPACITY + ';', 'stroke: none;', - '}', - /* eslint-enable indent */ - ]); -}; + getCSS_(selector) { + return super.getCSS_(selector).concat([ + /* eslint-disable indent */ + // Insertion marker. + selector + ' .blocklyInsertionMarker>.blocklyPathLight,', + selector + ' .blocklyInsertionMarker>.blocklyPathDark {', + 'fill-opacity: ' + this.INSERTION_MARKER_OPACITY + ';', 'stroke: none;', + '}', + /* eslint-enable indent */ + ]); + } +} exports.ConstantProvider = ConstantProvider; diff --git a/core/renderers/geras/drawer.js b/core/renderers/geras/drawer.js index c56b6e5fe6d..73186e86a29 100644 --- a/core/renderers/geras/drawer.js +++ b/core/renderers/geras/drawer.js @@ -16,13 +16,14 @@ goog.module('Blockly.geras.Drawer'); const debug = goog.require('Blockly.blockRendering.debug'); -const object = goog.require('Blockly.utils.object'); const svgPaths = goog.require('Blockly.utils.svgPaths'); /* eslint-disable-next-line no-unused-vars */ const {BlockSvg} = goog.requireType('Blockly.BlockSvg'); const {Drawer: BaseDrawer} = goog.require('Blockly.blockRendering.Drawer'); const {Highlighter} = goog.require('Blockly.geras.Highlighter'); /* eslint-disable-next-line no-unused-vars */ +const {InlineInput} = goog.require('Blockly.geras.InlineInput'); +/* eslint-disable-next-line no-unused-vars */ const {PathObject} = goog.requireType('Blockly.geras.PathObject'); /* eslint-disable-next-line no-unused-vars */ const {RenderInfo} = goog.requireType('Blockly.geras.RenderInfo'); @@ -30,183 +31,185 @@ const {RenderInfo} = goog.requireType('Blockly.geras.RenderInfo'); /** * An object that draws a block based on the given rendering information. - * @param {!BlockSvg} block The block to render. - * @param {!RenderInfo} info An object containing all - * information needed to render this block. - * @package - * @constructor * @extends {BaseDrawer} - * @alias Blockly.geras.Drawer - */ -const Drawer = function(block, info) { - Drawer.superClass_.constructor.call(this, block, info); - // Unlike Thrasos, Geras has highlights and drop shadows. - this.highlighter_ = new Highlighter(info); -}; -object.inherits(Drawer, BaseDrawer); - -/** - * @override */ -Drawer.prototype.draw = function() { - this.hideHiddenIcons_(); - this.drawOutline_(); - this.drawInternals_(); - - const pathObject = - /** @type {!PathObject} */ (this.block_.pathObject); - pathObject.setPath(this.outlinePath_ + '\n' + this.inlinePath_); - pathObject.setHighlightPath(this.highlighter_.getPath()); - if (this.info_.RTL) { - pathObject.flipRTL(); +class Drawer extends BaseDrawer { + /** + * @param {!BlockSvg} block The block to render. + * @param {!RenderInfo} info An object containing all + * information needed to render this block. + * @package + * @alias Blockly.geras.Drawer + */ + constructor(block, info) { + super(block, info); + // Unlike Thrasos, Geras has highlights and drop shadows. + this.highlighter_ = new Highlighter(info); } - if (debug.isDebuggerEnabled()) { - this.block_.renderingDebugger.drawDebug(this.block_, this.info_); + + /** + * @override + */ + draw() { + this.hideHiddenIcons_(); + this.drawOutline_(); + this.drawInternals_(); + + const pathObject = + /** @type {!PathObject} */ (this.block_.pathObject); + pathObject.setPath(this.outlinePath_ + '\n' + this.inlinePath_); + pathObject.setHighlightPath(this.highlighter_.getPath()); + if (this.info_.RTL) { + pathObject.flipRTL(); + } + if (debug.isDebuggerEnabled()) { + this.block_.renderingDebugger.drawDebug(this.block_, this.info_); + } + this.recordSizeOnBlock_(); } - this.recordSizeOnBlock_(); -}; -/** - * @override - */ -Drawer.prototype.drawTop_ = function() { - this.highlighter_.drawTopCorner(this.info_.topRow); - this.highlighter_.drawRightSideRow(this.info_.topRow); + /** + * @override + */ + drawTop_() { + this.highlighter_.drawTopCorner(this.info_.topRow); + this.highlighter_.drawRightSideRow(this.info_.topRow); - Drawer.superClass_.drawTop_.call(this); -}; + super.drawTop_(); + } -/** - * @override - */ -Drawer.prototype.drawJaggedEdge_ = function(row) { - this.highlighter_.drawJaggedEdge_(row); + /** + * @override + */ + drawJaggedEdge_(row) { + this.highlighter_.drawJaggedEdge_(row); - Drawer.superClass_.drawJaggedEdge_.call(this, row); -}; + super.drawJaggedEdge_(row); + } -/** - * @override - */ -Drawer.prototype.drawValueInput_ = function(row) { - this.highlighter_.drawValueInput(row); + /** + * @override + */ + drawValueInput_(row) { + this.highlighter_.drawValueInput(row); - Drawer.superClass_.drawValueInput_.call(this, row); -}; + super.drawValueInput_(row); + } -/** - * @override - */ -Drawer.prototype.drawStatementInput_ = function(row) { - this.highlighter_.drawStatementInput(row); + /** + * @override + */ + drawStatementInput_(row) { + this.highlighter_.drawStatementInput(row); - Drawer.superClass_.drawStatementInput_.call(this, row); -}; + super.drawStatementInput_(row); + } -/** - * @override - */ -Drawer.prototype.drawRightSideRow_ = function(row) { - this.highlighter_.drawRightSideRow(row); + /** + * @override + */ + drawRightSideRow_(row) { + this.highlighter_.drawRightSideRow(row); - this.outlinePath_ += svgPaths.lineOnAxis('H', row.xPos + row.width) + - svgPaths.lineOnAxis('V', row.yPos + row.height); -}; + this.outlinePath_ += svgPaths.lineOnAxis('H', row.xPos + row.width) + + svgPaths.lineOnAxis('V', row.yPos + row.height); + } -/** - * @override - */ -Drawer.prototype.drawBottom_ = function() { - this.highlighter_.drawBottomRow(this.info_.bottomRow); + /** + * @override + */ + drawBottom_() { + this.highlighter_.drawBottomRow(this.info_.bottomRow); - Drawer.superClass_.drawBottom_.call(this); -}; + super.drawBottom_(); + } -/** - * Add steps for the left side of the block, which may include an output - * connection - * @protected - * @override - */ -Drawer.prototype.drawLeft_ = function() { - this.highlighter_.drawLeft(); + /** + * Add steps for the left side of the block, which may include an output + * connection + * @protected + * @override + */ + drawLeft_() { + this.highlighter_.drawLeft(); - Drawer.superClass_.drawLeft_.call(this); -}; + super.drawLeft_(); + } -/** - * @override - */ -Drawer.prototype.drawInlineInput_ = function(input) { - this.highlighter_.drawInlineInput(input); + /** + * @override + */ + drawInlineInput_(input) { + this.highlighter_.drawInlineInput(/** @type {!InlineInput} */ (input)); - Drawer.superClass_.drawInlineInput_.call(this, input); -}; + super.drawInlineInput_(input); + } -/** - * @override - */ -Drawer.prototype.positionInlineInputConnection_ = function(input) { - const yPos = input.centerline - input.height / 2; - // Move the connection. - if (input.connectionModel) { - // xPos already contains info about startX - let connX = - input.xPos + input.connectionWidth + this.constants_.DARK_PATH_OFFSET; - if (this.info_.RTL) { - connX *= -1; + /** + * @override + */ + positionInlineInputConnection_(input) { + const yPos = input.centerline - input.height / 2; + // Move the connection. + if (input.connectionModel) { + // xPos already contains info about startX + let connX = + input.xPos + input.connectionWidth + this.constants_.DARK_PATH_OFFSET; + if (this.info_.RTL) { + connX *= -1; + } + input.connectionModel.setOffsetInBlock( + connX, + yPos + input.connectionOffsetY + this.constants_.DARK_PATH_OFFSET); } - input.connectionModel.setOffsetInBlock( - connX, - yPos + input.connectionOffsetY + this.constants_.DARK_PATH_OFFSET); } -}; -/** - * @override - */ -Drawer.prototype.positionStatementInputConnection_ = function(row) { - const input = row.getLastInput(); - if (input.connectionModel) { - let connX = row.xPos + row.statementEdge + input.notchOffset; - if (this.info_.RTL) { - connX *= -1; - } else { - connX += this.constants_.DARK_PATH_OFFSET; + /** + * @override + */ + positionStatementInputConnection_(row) { + const input = row.getLastInput(); + if (input.connectionModel) { + let connX = row.xPos + row.statementEdge + input.notchOffset; + if (this.info_.RTL) { + connX *= -1; + } else { + connX += this.constants_.DARK_PATH_OFFSET; + } + input.connectionModel.setOffsetInBlock( + connX, row.yPos + this.constants_.DARK_PATH_OFFSET); } - input.connectionModel.setOffsetInBlock( - connX, row.yPos + this.constants_.DARK_PATH_OFFSET); } -}; -/** - * @override - */ -Drawer.prototype.positionExternalValueConnection_ = function(row) { - const input = row.getLastInput(); - if (input.connectionModel) { - let connX = row.xPos + row.width + this.constants_.DARK_PATH_OFFSET; - if (this.info_.RTL) { - connX *= -1; + /** + * @override + */ + positionExternalValueConnection_(row) { + const input = row.getLastInput(); + if (input.connectionModel) { + let connX = row.xPos + row.width + this.constants_.DARK_PATH_OFFSET; + if (this.info_.RTL) { + connX *= -1; + } + input.connectionModel.setOffsetInBlock(connX, row.yPos); } - input.connectionModel.setOffsetInBlock(connX, row.yPos); } -}; -/** - * @override - */ -Drawer.prototype.positionNextConnection_ = function() { - const bottomRow = this.info_.bottomRow; - - if (bottomRow.connection) { - const connInfo = bottomRow.connection; - const x = connInfo.xPos; // Already contains info about startX. - const connX = - (this.info_.RTL ? -x : x) + (this.constants_.DARK_PATH_OFFSET / 2); - connInfo.connectionModel.setOffsetInBlock( - connX, bottomRow.baseline + this.constants_.DARK_PATH_OFFSET); + /** + * @override + */ + positionNextConnection_() { + const bottomRow = this.info_.bottomRow; + + if (bottomRow.connection) { + const connInfo = bottomRow.connection; + const x = connInfo.xPos; // Already contains info about startX. + const connX = + (this.info_.RTL ? -x : x) + (this.constants_.DARK_PATH_OFFSET / 2); + connInfo.connectionModel.setOffsetInBlock( + connX, bottomRow.baseline + this.constants_.DARK_PATH_OFFSET); + } } -}; +} exports.Drawer = Drawer; diff --git a/core/renderers/geras/highlight_constants.js b/core/renderers/geras/highlight_constants.js index 5627e40c990..8ed71679acc 100644 --- a/core/renderers/geras/highlight_constants.js +++ b/core/renderers/geras/highlight_constants.js @@ -25,269 +25,272 @@ const {ConstantProvider} = goog.requireType('Blockly.blockRendering.ConstantProv * Some highlights are simple offsets of the parent paths and can be generated * programmatically. Others, especially on curves, are just made out of piles * of constants and are hard to tweak. - * @param {!ConstantProvider} constants The rendering - * constants provider. - * @constructor - * @package - * @alias Blockly.geras.HighlightConstantProvider */ -const HighlightConstantProvider = function(constants) { +class HighlightConstantProvider { /** - * The renderer's constant provider. - * @type {!ConstantProvider} - */ - this.constantProvider = constants; - - /** - * The offset between the block's main path and highlight path. - * @type {number} + * @param {!ConstantProvider} constants The rendering + * constants provider. * @package + * @alias Blockly.geras.HighlightConstantProvider */ - this.OFFSET = 0.5; - - /** - * The start point, which is offset in both X and Y, as an SVG path chunk. - * @type {string} - */ - this.START_POINT = svgPaths.moveBy(this.OFFSET, this.OFFSET); -}; + constructor(constants) { + /** + * The renderer's constant provider. + * @type {!ConstantProvider} + */ + this.constantProvider = constants; + + /** + * The offset between the block's main path and highlight path. + * @type {number} + * @package + */ + this.OFFSET = 0.5; + + /** + * The start point, which is offset in both X and Y, as an SVG path chunk. + * @type {string} + */ + this.START_POINT = svgPaths.moveBy(this.OFFSET, this.OFFSET); + } -/** - * Initialize shape objects based on the constants set in the constructor. - * @package - */ -HighlightConstantProvider.prototype.init = function() { /** - * An object containing sizing and path information about inside corner - * highlights. - * @type {!Object} - */ - this.INSIDE_CORNER = this.makeInsideCorner(); - - /** - * An object containing sizing and path information about outside corner - * highlights. - * @type {!Object} + * Initialize shape objects based on the constants set in the constructor. + * @package */ - this.OUTSIDE_CORNER = this.makeOutsideCorner(); + init() { + /** + * An object containing sizing and path information about inside corner + * highlights. + * @type {!Object} + */ + this.INSIDE_CORNER = this.makeInsideCorner(); + + /** + * An object containing sizing and path information about outside corner + * highlights. + * @type {!Object} + */ + this.OUTSIDE_CORNER = this.makeOutsideCorner(); + + /** + * An object containing sizing and path information about puzzle tab + * highlights. + * @type {!Object} + */ + this.PUZZLE_TAB = this.makePuzzleTab(); + + /** + * An object containing sizing and path information about notch highlights. + * @type {!Object} + */ + this.NOTCH = this.makeNotch(); + + /** + * An object containing sizing and path information about highlights for + * collapsed block indicators. + * @type {!Object} + */ + this.JAGGED_TEETH = this.makeJaggedTeeth(); + + /** + * An object containing sizing and path information about start hat + * highlights. + * @type {!Object} + */ + this.START_HAT = this.makeStartHat(); + } /** - * An object containing sizing and path information about puzzle tab - * highlights. - * @type {!Object} + * @return {!Object} An object containing sizing and path information about + * inside corner highlights. + * @package */ - this.PUZZLE_TAB = this.makePuzzleTab(); + makeInsideCorner() { + const radius = this.constantProvider.CORNER_RADIUS; + const offset = this.OFFSET; + + /** + * Distance from shape edge to intersect with a curved corner at 45 degrees. + * Applies to highlighting on around the outside of a curve. + * @const + */ + const distance45outside = (1 - Math.SQRT1_2) * (radius + offset) - offset; + + const pathTopRtl = svgPaths.moveBy(distance45outside, distance45outside) + + svgPaths.arc( + 'a', '0 0,0', radius, + svgPaths.point( + -distance45outside - offset, radius - distance45outside)); + + const pathBottomRtl = svgPaths.arc( + 'a', '0 0,0', radius + offset, + svgPaths.point(radius + offset, radius + offset)); + + const pathBottomLtr = svgPaths.moveBy(distance45outside, -distance45outside) + + svgPaths.arc( + 'a', '0 0,0', radius + offset, + svgPaths.point( + radius - distance45outside, distance45outside + offset)); + + return { + width: radius + offset, + height: radius, + pathTop: function(rtl) { + return rtl ? pathTopRtl : ''; + }, + pathBottom: function(rtl) { + return rtl ? pathBottomRtl : pathBottomLtr; + }, + }; + } /** - * An object containing sizing and path information about notch highlights. - * @type {!Object} + * @return {!Object} An object containing sizing and path information about + * outside corner highlights. + * @package */ - this.NOTCH = this.makeNotch(); + makeOutsideCorner() { + const radius = this.constantProvider.CORNER_RADIUS; + const offset = this.OFFSET; + + /** + * Distance from shape edge to intersect with a curved corner at 45 degrees. + * Applies to highlighting on around the inside of a curve. + * @const + */ + const distance45inside = (1 - Math.SQRT1_2) * (radius - offset) + offset; + + const topLeftStartX = distance45inside; + const topLeftStartY = distance45inside; + const topLeftCornerHighlightRtl = + svgPaths.moveBy(topLeftStartX, topLeftStartY) + + svgPaths.arc( + 'a', '0 0,1', radius - offset, + svgPaths.point(radius - topLeftStartX, -topLeftStartY + offset)); + /** + * SVG path for drawing the highlight on the rounded top-left corner. + * @const + */ + const topLeftCornerHighlightLtr = svgPaths.moveBy(offset, radius) + + svgPaths.arc( + 'a', '0 0,1', radius - offset, + svgPaths.point(radius, -radius + offset)); + + const bottomLeftStartX = distance45inside; + const bottomLeftStartY = -distance45inside; + const bottomLeftPath = svgPaths.moveBy(bottomLeftStartX, bottomLeftStartY) + + svgPaths.arc( + 'a', '0 0,1', radius - offset, + svgPaths.point( + -bottomLeftStartX + offset, -bottomLeftStartY - radius)); + + return { + height: radius, + topLeft: function(rtl) { + return rtl ? topLeftCornerHighlightRtl : topLeftCornerHighlightLtr; + }, + bottomLeft: function() { + return bottomLeftPath; + }, + }; + } /** - * An object containing sizing and path information about highlights for - * collapsed block indicators. - * @type {!Object} + * @return {!Object} An object containing sizing and path information about + * puzzle tab highlights. + * @package */ - this.JAGGED_TEETH = this.makeJaggedTeeth(); + makePuzzleTab() { + const width = this.constantProvider.TAB_WIDTH; + const height = this.constantProvider.TAB_HEIGHT; + + // This is how much of the vertical block edge is actually drawn by the puzzle + // tab. + const verticalOverlap = 2.5; + + const highlightRtlUp = svgPaths.moveBy(-2, -height + verticalOverlap + 0.9) + + svgPaths.lineTo(width * -0.45, -2.1); + + const highlightRtlDown = svgPaths.lineOnAxis('v', verticalOverlap) + + svgPaths.moveBy(-width * 0.97, 2.5) + + svgPaths.curve( + 'q', + [ + svgPaths.point(-width * 0.05, 10), + svgPaths.point(width * 0.3, 9.5), + ]) + + svgPaths.moveBy(width * 0.67, -1.9) + + svgPaths.lineOnAxis('v', verticalOverlap); + + const highlightLtrUp = svgPaths.lineOnAxis('v', -1.5) + + svgPaths.moveBy(width * -0.92, -0.5) + + svgPaths.curve( + 'q', [svgPaths.point(width * -0.19, -5.5), svgPaths.point(0, -11)]) + + svgPaths.moveBy(width * 0.92, 1); + + const highlightLtrDown = + svgPaths.moveBy(-5, height - 0.7) + svgPaths.lineTo(width * 0.46, -2.1); + + return { + width: width, + height: height, + pathUp: function(rtl) { + return rtl ? highlightRtlUp : highlightLtrUp; + }, + pathDown: function(rtl) { + return rtl ? highlightRtlDown : highlightLtrDown; + }, + }; + } /** - * An object containing sizing and path information about start hat - * highlights. - * @type {!Object} + * @return {!Object} An object containing sizing and path information about + * notch highlights. + * @package */ - this.START_HAT = this.makeStartHat(); -}; - -/** - * @return {!Object} An object containing sizing and path information about - * inside corner highlights. - * @package - */ -HighlightConstantProvider.prototype.makeInsideCorner = function() { - const radius = this.constantProvider.CORNER_RADIUS; - const offset = this.OFFSET; + makeNotch() { + // This is only for the previous connection. + const pathLeft = svgPaths.lineOnAxis('h', this.OFFSET) + + this.constantProvider.NOTCH.pathLeft; + return {pathLeft: pathLeft}; + } /** - * Distance from shape edge to intersect with a curved corner at 45 degrees. - * Applies to highlighting on around the outside of a curve. - * @const + * @return {!Object} An object containing sizing and path information about + * collapsed block edge highlights. + * @package */ - const distance45outside = (1 - Math.SQRT1_2) * (radius + offset) - offset; - - const pathTopRtl = svgPaths.moveBy(distance45outside, distance45outside) + - svgPaths.arc( - 'a', '0 0,0', radius, - svgPaths.point( - -distance45outside - offset, radius - distance45outside)); - - const pathBottomRtl = svgPaths.arc( - 'a', '0 0,0', radius + offset, - svgPaths.point(radius + offset, radius + offset)); - - const pathBottomLtr = svgPaths.moveBy(distance45outside, -distance45outside) + - svgPaths.arc( - 'a', '0 0,0', radius + offset, - svgPaths.point( - radius - distance45outside, distance45outside + offset)); - - return { - width: radius + offset, - height: radius, - pathTop: function(rtl) { - return rtl ? pathTopRtl : ''; - }, - pathBottom: function(rtl) { - return rtl ? pathBottomRtl : pathBottomLtr; - }, - }; -}; + makeJaggedTeeth() { + const pathLeft = svgPaths.lineTo(5.1, 2.6) + svgPaths.moveBy(-10.2, 6.8) + + svgPaths.lineTo(5.1, 2.6); + return {pathLeft: pathLeft, height: 12, width: 10.2}; + } -/** - * @return {!Object} An object containing sizing and path information about - * outside corner highlights. - * @package - */ -HighlightConstantProvider.prototype.makeOutsideCorner = function() { - const radius = this.constantProvider.CORNER_RADIUS; - const offset = this.OFFSET; - - /** - * Distance from shape edge to intersect with a curved corner at 45 degrees. - * Applies to highlighting on around the inside of a curve. - * @const - */ - const distance45inside = (1 - Math.SQRT1_2) * (radius - offset) + offset; - - const topLeftStartX = distance45inside; - const topLeftStartY = distance45inside; - const topLeftCornerHighlightRtl = - svgPaths.moveBy(topLeftStartX, topLeftStartY) + - svgPaths.arc( - 'a', '0 0,1', radius - offset, - svgPaths.point(radius - topLeftStartX, -topLeftStartY + offset)); /** - * SVG path for drawing the highlight on the rounded top-left corner. - * @const + * @return {!Object} An object containing sizing and path information about + * start highlights. + * @package */ - const topLeftCornerHighlightLtr = svgPaths.moveBy(offset, radius) + - svgPaths.arc( - 'a', '0 0,1', radius - offset, - svgPaths.point(radius, -radius + offset)); - - const bottomLeftStartX = distance45inside; - const bottomLeftStartY = -distance45inside; - const bottomLeftPath = svgPaths.moveBy(bottomLeftStartX, bottomLeftStartY) + - svgPaths.arc( - 'a', '0 0,1', radius - offset, - svgPaths.point( - -bottomLeftStartX + offset, -bottomLeftStartY - radius)); - - return { - height: radius, - topLeft: function(rtl) { - return rtl ? topLeftCornerHighlightRtl : topLeftCornerHighlightLtr; - }, - bottomLeft: function() { - return bottomLeftPath; - }, - }; -}; - -/** - * @return {!Object} An object containing sizing and path information about - * puzzle tab highlights. - * @package - */ -HighlightConstantProvider.prototype.makePuzzleTab = function() { - const width = this.constantProvider.TAB_WIDTH; - const height = this.constantProvider.TAB_HEIGHT; - - // This is how much of the vertical block edge is actually drawn by the puzzle - // tab. - const verticalOverlap = 2.5; - - const highlightRtlUp = svgPaths.moveBy(-2, -height + verticalOverlap + 0.9) + - svgPaths.lineTo(width * -0.45, -2.1); - - const highlightRtlDown = svgPaths.lineOnAxis('v', verticalOverlap) + - svgPaths.moveBy(-width * 0.97, 2.5) + - svgPaths.curve( - 'q', - [ - svgPaths.point(-width * 0.05, 10), - svgPaths.point(width * 0.3, 9.5), - ]) + - svgPaths.moveBy(width * 0.67, -1.9) + - svgPaths.lineOnAxis('v', verticalOverlap); - - const highlightLtrUp = svgPaths.lineOnAxis('v', -1.5) + - svgPaths.moveBy(width * -0.92, -0.5) + - svgPaths.curve( - 'q', [svgPaths.point(width * -0.19, -5.5), svgPaths.point(0, -11)]) + - svgPaths.moveBy(width * 0.92, 1); - - const highlightLtrDown = - svgPaths.moveBy(-5, height - 0.7) + svgPaths.lineTo(width * 0.46, -2.1); - - return { - width: width, - height: height, - pathUp: function(rtl) { - return rtl ? highlightRtlUp : highlightLtrUp; - }, - pathDown: function(rtl) { - return rtl ? highlightRtlDown : highlightLtrDown; - }, - }; -}; - -/** - * @return {!Object} An object containing sizing and path information about - * notch highlights. - * @package - */ -HighlightConstantProvider.prototype.makeNotch = function() { - // This is only for the previous connection. - const pathLeft = svgPaths.lineOnAxis('h', this.OFFSET) + - this.constantProvider.NOTCH.pathLeft; - return {pathLeft: pathLeft}; -}; - -/** - * @return {!Object} An object containing sizing and path information about - * collapsed block edge highlights. - * @package - */ -HighlightConstantProvider.prototype.makeJaggedTeeth = function() { - const pathLeft = svgPaths.lineTo(5.1, 2.6) + svgPaths.moveBy(-10.2, 6.8) + - svgPaths.lineTo(5.1, 2.6); - return {pathLeft: pathLeft, height: 12, width: 10.2}; -}; - -/** - * @return {!Object} An object containing sizing and path information about - * start highlights. - * @package - */ -HighlightConstantProvider.prototype.makeStartHat = function() { - const hatHeight = this.constantProvider.START_HAT.height; - const pathRtl = svgPaths.moveBy(25, -8.7) + svgPaths.curve('c', [ - svgPaths.point(29.7, -6.2), - svgPaths.point(57.2, -0.5), - svgPaths.point(75, 8.7), - ]); - - const pathLtr = svgPaths.curve('c', [ - svgPaths.point(17.8, -9.2), - svgPaths.point(45.3, -14.9), - svgPaths.point(75, -8.7), - ]) + svgPaths.moveTo(100.5, hatHeight + 0.5); - return { - path: function(rtl) { - return rtl ? pathRtl : pathLtr; - }, - }; -}; + makeStartHat() { + const hatHeight = this.constantProvider.START_HAT.height; + const pathRtl = svgPaths.moveBy(25, -8.7) + svgPaths.curve('c', [ + svgPaths.point(29.7, -6.2), + svgPaths.point(57.2, -0.5), + svgPaths.point(75, 8.7), + ]); + + const pathLtr = svgPaths.curve('c', [ + svgPaths.point(17.8, -9.2), + svgPaths.point(45.3, -14.9), + svgPaths.point(75, -8.7), + ]) + svgPaths.moveTo(100.5, hatHeight + 0.5); + return { + path: function(rtl) { + return rtl ? pathRtl : pathLtr; + }, + }; + } +} exports.HighlightConstantProvider = HighlightConstantProvider; diff --git a/core/renderers/geras/highlighter.js b/core/renderers/geras/highlighter.js index e54d26837f3..c0fbf74b782 100644 --- a/core/renderers/geras/highlighter.js +++ b/core/renderers/geras/highlighter.js @@ -19,13 +19,21 @@ goog.module('Blockly.geras.Highlighter'); const svgPaths = goog.require('Blockly.utils.svgPaths'); /* eslint-disable-next-line no-unused-vars */ +const {BottomRow} = goog.require('Blockly.blockRendering.BottomRow'); +/* eslint-disable-next-line no-unused-vars */ const {ConstantProvider} = goog.requireType('Blockly.blockRendering.ConstantProvider'); /* eslint-disable-next-line no-unused-vars */ const {HighlightConstantProvider} = goog.requireType('Blockly.geras.HighlightConstantProvider'); /* eslint-disable-next-line no-unused-vars */ +const {InlineInput} = goog.require('Blockly.geras.InlineInput'); +/* eslint-disable-next-line no-unused-vars */ const {RenderInfo} = goog.requireType('Blockly.geras.RenderInfo'); /* eslint-disable-next-line no-unused-vars */ const {Renderer} = goog.requireType('Blockly.geras.Renderer'); +/* eslint-disable-next-line no-unused-vars */ +const {Row} = goog.requireType('Blockly.blockRendering.Row'); +/* eslint-disable-next-line no-unused-vars */ +const {TopRow} = goog.require('Blockly.blockRendering.TopRow'); const {Types} = goog.require('Blockly.blockRendering.Types'); @@ -39,221 +47,262 @@ const {Types} = goog.require('Blockly.blockRendering.Types'); * position of each part of the block. The resulting paths are not continuous * or closed paths. The highlights for tabs and notches are loosely based on * tab and notch shapes, but are not exactly the same. - * - * @param {!RenderInfo} info An object containing all - * information needed to render this block. - * @package - * @constructor - * @alias Blockly.geras.Highlighter */ -const Highlighter = function(info) { - this.info_ = info; - this.steps_ = ''; - this.inlineSteps_ = ''; - - this.RTL_ = this.info_.RTL; - - const renderer = /** @type {!Renderer} */ (info.getRenderer()); - +class Highlighter { /** - * The renderer's constant provider. - * @type {!ConstantProvider} + * @param {!RenderInfo} info An object containing all + * information needed to render this block. + * @package + * @alias Blockly.geras.Highlighter */ - this.constants_ = renderer.getConstants(); + constructor(info) { + this.info_ = info; + this.steps_ = ''; + this.inlineSteps_ = ''; - /** - * @type {!HighlightConstantProvider} - */ - this.highlightConstants_ = renderer.getHighlightConstants(); - /** - * The offset between the block's main path and highlight path. - * @type {number} - * @private - */ - this.highlightOffset_ = this.highlightConstants_.OFFSET; + this.RTL_ = this.info_.RTL; - this.outsideCornerPaths_ = this.highlightConstants_.OUTSIDE_CORNER; - this.insideCornerPaths_ = this.highlightConstants_.INSIDE_CORNER; - this.puzzleTabPaths_ = this.highlightConstants_.PUZZLE_TAB; - this.notchPaths_ = this.highlightConstants_.NOTCH; - this.startPaths_ = this.highlightConstants_.START_HAT; - this.jaggedTeethPaths_ = this.highlightConstants_.JAGGED_TEETH; -}; + const renderer = /** @type {!Renderer} */ (info.getRenderer()); -/** - * Get the steps for the highlight path. - * @return {string} The steps for the highlight path. - * @package - */ -Highlighter.prototype.getPath = function() { - return this.steps_ + '\n' + this.inlineSteps_; -}; + /** + * The renderer's constant provider. + * @type {!ConstantProvider} + */ + this.constants_ = renderer.getConstants(); -Highlighter.prototype.drawTopCorner = function(row) { - this.steps_ += svgPaths.moveBy(row.xPos, this.info_.startY); - for (let i = 0, elem; (elem = row.elements[i]); i++) { - if (Types.isLeftSquareCorner(elem)) { - this.steps_ += this.highlightConstants_.START_POINT; - } else if (Types.isLeftRoundedCorner(elem)) { - this.steps_ += this.outsideCornerPaths_.topLeft(this.RTL_); - } else if (Types.isPreviousConnection(elem)) { - this.steps_ += this.notchPaths_.pathLeft; - } else if (Types.isHat(elem)) { - this.steps_ += this.startPaths_.path(this.RTL_); - } else if (Types.isSpacer(elem) && elem.width !== 0) { - // The end point of the spacer needs to be offset by the highlight amount. - // So instead of using the spacer's width for a relative horizontal, use - // its width and position for an absolute horizontal move. - this.steps_ += svgPaths.lineOnAxis( - 'H', elem.xPos + elem.width - this.highlightOffset_); - } - } + /** + * @type {!HighlightConstantProvider} + */ + this.highlightConstants_ = renderer.getHighlightConstants(); + /** + * The offset between the block's main path and highlight path. + * @type {number} + * @private + */ + this.highlightOffset_ = this.highlightConstants_.OFFSET; - const right = row.xPos + row.width - this.highlightOffset_; - this.steps_ += svgPaths.lineOnAxis('H', right); -}; + this.outsideCornerPaths_ = this.highlightConstants_.OUTSIDE_CORNER; + this.insideCornerPaths_ = this.highlightConstants_.INSIDE_CORNER; + this.puzzleTabPaths_ = this.highlightConstants_.PUZZLE_TAB; + this.notchPaths_ = this.highlightConstants_.NOTCH; + this.startPaths_ = this.highlightConstants_.START_HAT; + this.jaggedTeethPaths_ = this.highlightConstants_.JAGGED_TEETH; + } -Highlighter.prototype.drawJaggedEdge_ = function(row) { - if (this.info_.RTL) { - const remainder = - row.height - this.jaggedTeethPaths_.height - this.highlightOffset_; - this.steps_ += - this.jaggedTeethPaths_.pathLeft + svgPaths.lineOnAxis('v', remainder); + /** + * Get the steps for the highlight path. + * @return {string} The steps for the highlight path. + * @package + */ + getPath() { + return this.steps_ + '\n' + this.inlineSteps_; } -}; -Highlighter.prototype.drawValueInput = function(row) { - const input = row.getLastInput(); - if (this.RTL_) { - const belowTabHeight = row.height - input.connectionHeight; + /** + * Add a highlight to the top corner of a block. + * @param {!TopRow} row The top row of the block. + * @package + */ + drawTopCorner(row) { + this.steps_ += svgPaths.moveBy(row.xPos, this.info_.startY); + for (let i = 0, elem; (elem = row.elements[i]); i++) { + if (Types.isLeftSquareCorner(elem)) { + this.steps_ += this.highlightConstants_.START_POINT; + } else if (Types.isLeftRoundedCorner(elem)) { + this.steps_ += this.outsideCornerPaths_.topLeft(this.RTL_); + } else if (Types.isPreviousConnection(elem)) { + this.steps_ += this.notchPaths_.pathLeft; + } else if (Types.isHat(elem)) { + this.steps_ += this.startPaths_.path(this.RTL_); + } else if (Types.isSpacer(elem) && elem.width !== 0) { + // The end point of the spacer needs to be offset by the highlight amount. + // So instead of using the spacer's width for a relative horizontal, use + // its width and position for an absolute horizontal move. + this.steps_ += svgPaths.lineOnAxis( + 'H', elem.xPos + elem.width - this.highlightOffset_); + } + } - this.steps_ += - svgPaths.moveTo( - input.xPos + input.width - this.highlightOffset_, row.yPos) + - this.puzzleTabPaths_.pathDown(this.RTL_) + - svgPaths.lineOnAxis('v', belowTabHeight); - } else { - this.steps_ += svgPaths.moveTo(input.xPos + input.width, row.yPos) + - this.puzzleTabPaths_.pathDown(this.RTL_); + const right = row.xPos + row.width - this.highlightOffset_; + this.steps_ += svgPaths.lineOnAxis('H', right); } -}; -Highlighter.prototype.drawStatementInput = function(row) { - const input = row.getLastInput(); - if (this.RTL_) { - const innerHeight = row.height - (2 * this.insideCornerPaths_.height); - this.steps_ += svgPaths.moveTo(input.xPos, row.yPos) + - this.insideCornerPaths_.pathTop(this.RTL_) + - svgPaths.lineOnAxis('v', innerHeight) + - this.insideCornerPaths_.pathBottom(this.RTL_) + - svgPaths.lineTo( - row.width - input.xPos - this.insideCornerPaths_.width, 0); - } else { - this.steps_ += svgPaths.moveTo(input.xPos, row.yPos + row.height) + - this.insideCornerPaths_.pathBottom(this.RTL_) + - svgPaths.lineTo( - row.width - input.xPos - this.insideCornerPaths_.width, 0); + /** + * Add a highlight on a jagged edge for a collapsed block. + * @param {!Row} row The row to highlight. + * @package + */ + drawJaggedEdge_(row) { + if (this.info_.RTL) { + const remainder = + row.height - this.jaggedTeethPaths_.height - this.highlightOffset_; + this.steps_ += + this.jaggedTeethPaths_.pathLeft + svgPaths.lineOnAxis('v', remainder); + } } -}; -Highlighter.prototype.drawRightSideRow = function(row) { - const rightEdge = row.xPos + row.width - this.highlightOffset_; - if (row.followsStatement) { - this.steps_ += svgPaths.lineOnAxis('H', rightEdge); + /** + * Add a highlight on a value input. + * @param {!Row} row The row the input belongs to. + * @package + */ + drawValueInput(row) { + const input = row.getLastInput(); + if (this.RTL_) { + const belowTabHeight = row.height - input.connectionHeight; + + this.steps_ += + svgPaths.moveTo( + input.xPos + input.width - this.highlightOffset_, row.yPos) + + this.puzzleTabPaths_.pathDown(this.RTL_) + + svgPaths.lineOnAxis('v', belowTabHeight); + } else { + this.steps_ += svgPaths.moveTo(input.xPos + input.width, row.yPos) + + this.puzzleTabPaths_.pathDown(this.RTL_); + } } - if (this.RTL_) { - this.steps_ += svgPaths.lineOnAxis('H', rightEdge); - if (row.height > this.highlightOffset_) { - this.steps_ += svgPaths.lineOnAxis( - 'V', row.yPos + row.height - this.highlightOffset_); + + /** + * Add a highlight on a statement input. + * @param {!Row} row The row to highlight. + * @package + */ + drawStatementInput(row) { + const input = row.getLastInput(); + if (this.RTL_) { + const innerHeight = row.height - (2 * this.insideCornerPaths_.height); + this.steps_ += svgPaths.moveTo(input.xPos, row.yPos) + + this.insideCornerPaths_.pathTop(this.RTL_) + + svgPaths.lineOnAxis('v', innerHeight) + + this.insideCornerPaths_.pathBottom(this.RTL_) + + svgPaths.lineTo( + row.width - input.xPos - this.insideCornerPaths_.width, 0); + } else { + this.steps_ += svgPaths.moveTo(input.xPos, row.yPos + row.height) + + this.insideCornerPaths_.pathBottom(this.RTL_) + + svgPaths.lineTo( + row.width - input.xPos - this.insideCornerPaths_.width, 0); } } -}; -Highlighter.prototype.drawBottomRow = function(row) { - // Highlight the vertical edge of the bottom row on the input side. - // Highlighting is always from the top left, both in LTR and RTL. - if (this.RTL_) { - this.steps_ += - svgPaths.lineOnAxis('V', row.baseline - this.highlightOffset_); - } else { - const cornerElem = this.info_.bottomRow.elements[0]; - if (Types.isLeftSquareCorner(cornerElem)) { - this.steps_ += svgPaths.moveTo( - row.xPos + this.highlightOffset_, - row.baseline - this.highlightOffset_); - } else if (Types.isLeftRoundedCorner(cornerElem)) { - this.steps_ += svgPaths.moveTo(row.xPos, row.baseline); - this.steps_ += this.outsideCornerPaths_.bottomLeft(); + /** + * Add a highlight on the right side of a row. + * @param {!Row} row The row to highlight. + * @package + */ + drawRightSideRow(row) { + const rightEdge = row.xPos + row.width - this.highlightOffset_; + if (row.followsStatement) { + this.steps_ += svgPaths.lineOnAxis('H', rightEdge); + } + if (this.RTL_) { + this.steps_ += svgPaths.lineOnAxis('H', rightEdge); + if (row.height > this.highlightOffset_) { + this.steps_ += svgPaths.lineOnAxis( + 'V', row.yPos + row.height - this.highlightOffset_); + } } } -}; -Highlighter.prototype.drawLeft = function() { - const outputConnection = this.info_.outputConnection; - if (outputConnection) { - const tabBottom = - outputConnection.connectionOffsetY + outputConnection.height; - // Draw a line up to the bottom of the tab. + /** + * Add a highlight to the bottom row. + * @param {!BottomRow} row The row to highlight. + * @package + */ + drawBottomRow(row) { + // Highlight the vertical edge of the bottom row on the input side. + // Highlighting is always from the top left, both in LTR and RTL. if (this.RTL_) { - this.steps_ += svgPaths.moveTo(this.info_.startX, tabBottom); + this.steps_ += + svgPaths.lineOnAxis('V', row.baseline - this.highlightOffset_); } else { - const left = this.info_.startX + this.highlightOffset_; - const bottom = this.info_.bottomRow.baseline - this.highlightOffset_; - this.steps_ += svgPaths.moveTo(left, bottom); - this.steps_ += svgPaths.lineOnAxis('V', tabBottom); + const cornerElem = this.info_.bottomRow.elements[0]; + if (Types.isLeftSquareCorner(cornerElem)) { + this.steps_ += svgPaths.moveTo( + row.xPos + this.highlightOffset_, + row.baseline - this.highlightOffset_); + } else if (Types.isLeftRoundedCorner(cornerElem)) { + this.steps_ += svgPaths.moveTo(row.xPos, row.baseline); + this.steps_ += this.outsideCornerPaths_.bottomLeft(); + } } - this.steps_ += this.puzzleTabPaths_.pathUp(this.RTL_); } - if (!this.RTL_) { - const topRow = this.info_.topRow; - if (Types.isLeftRoundedCorner(topRow.elements[0])) { - this.steps_ += svgPaths.lineOnAxis('V', this.outsideCornerPaths_.height); - } else { - this.steps_ += - svgPaths.lineOnAxis('V', topRow.capline + this.highlightOffset_); + /** + * Draw the highlight on the left side of the block. + * @package + */ + drawLeft() { + const outputConnection = this.info_.outputConnection; + if (outputConnection) { + const tabBottom = + outputConnection.connectionOffsetY + outputConnection.height; + // Draw a line up to the bottom of the tab. + if (this.RTL_) { + this.steps_ += svgPaths.moveTo(this.info_.startX, tabBottom); + } else { + const left = this.info_.startX + this.highlightOffset_; + const bottom = this.info_.bottomRow.baseline - this.highlightOffset_; + this.steps_ += svgPaths.moveTo(left, bottom); + this.steps_ += svgPaths.lineOnAxis('V', tabBottom); + } + this.steps_ += this.puzzleTabPaths_.pathUp(this.RTL_); + } + + if (!this.RTL_) { + const topRow = this.info_.topRow; + if (Types.isLeftRoundedCorner(topRow.elements[0])) { + this.steps_ += svgPaths.lineOnAxis('V', this.outsideCornerPaths_.height); + } else { + this.steps_ += + svgPaths.lineOnAxis('V', topRow.capline + this.highlightOffset_); + } } } -}; -Highlighter.prototype.drawInlineInput = function(input) { - const offset = this.highlightOffset_; + /** + * Add a highlight to an inline input. + * @param {!InlineInput} input The input to highlight. + * @package + */ + drawInlineInput(input) { + const offset = this.highlightOffset_; - // Relative to the block's left. - const connectionRight = input.xPos + input.connectionWidth; - const yPos = input.centerline - input.height / 2; - const bottomHighlightWidth = input.width - input.connectionWidth; - const startY = yPos + offset; + // Relative to the block's left. + const connectionRight = input.xPos + input.connectionWidth; + const yPos = input.centerline - input.height / 2; + const bottomHighlightWidth = input.width - input.connectionWidth; + const startY = yPos + offset; - if (this.RTL_) { - const aboveTabHeight = input.connectionOffsetY - offset; - const belowTabHeight = input.height - - (input.connectionOffsetY + input.connectionHeight) + offset; + if (this.RTL_) { + const aboveTabHeight = input.connectionOffsetY - offset; + const belowTabHeight = input.height - + (input.connectionOffsetY + input.connectionHeight) + offset; - const startX = connectionRight - offset; + const startX = connectionRight - offset; - this.inlineSteps_ += svgPaths.moveTo(startX, startY) + - // Right edge above tab. - svgPaths.lineOnAxis('v', aboveTabHeight) + - // Back of tab. - this.puzzleTabPaths_.pathDown(this.RTL_) + - // Right edge below tab. - svgPaths.lineOnAxis('v', belowTabHeight) + - // Bottom. - svgPaths.lineOnAxis('h', bottomHighlightWidth); - } else { - this.inlineSteps_ += - // Go to top right corner. - svgPaths.moveTo(input.xPos + input.width + offset, startY) + - // Highlight right edge, bottom. - svgPaths.lineOnAxis('v', input.height) + - svgPaths.lineOnAxis('h', -bottomHighlightWidth) + - // Go to top of tab. - svgPaths.moveTo(connectionRight, yPos + input.connectionOffsetY) + - // Short highlight glint at bottom of tab. - this.puzzleTabPaths_.pathDown(this.RTL_); + this.inlineSteps_ += svgPaths.moveTo(startX, startY) + + // Right edge above tab. + svgPaths.lineOnAxis('v', aboveTabHeight) + + // Back of tab. + this.puzzleTabPaths_.pathDown(this.RTL_) + + // Right edge below tab. + svgPaths.lineOnAxis('v', belowTabHeight) + + // Bottom. + svgPaths.lineOnAxis('h', bottomHighlightWidth); + } else { + this.inlineSteps_ += + // Go to top right corner. + svgPaths.moveTo(input.xPos + input.width + offset, startY) + + // Highlight right edge, bottom. + svgPaths.lineOnAxis('v', input.height) + + svgPaths.lineOnAxis('h', -bottomHighlightWidth) + + // Go to top of tab. + svgPaths.moveTo(connectionRight, yPos + input.connectionOffsetY) + + // Short highlight glint at bottom of tab. + this.puzzleTabPaths_.pathDown(this.RTL_); + } } -}; +} exports.Highlighter = Highlighter; diff --git a/core/renderers/geras/info.js b/core/renderers/geras/info.js index a78ab4ab713..d7a2b8f6d5f 100644 --- a/core/renderers/geras/info.js +++ b/core/renderers/geras/info.js @@ -17,7 +17,6 @@ */ goog.module('Blockly.geras.RenderInfo'); -const object = goog.require('Blockly.utils.object'); /* eslint-disable-next-line no-unused-vars */ const {BlockSvg} = goog.requireType('Blockly.BlockSvg'); const {ExternalValueInput} = goog.require('Blockly.blockRendering.ExternalValueInput'); @@ -41,425 +40,426 @@ const {inputTypes} = goog.require('Blockly.inputTypes'); * This measure pass does not propagate changes to the block (although fields * may choose to rerender when getSize() is called). However, calling it * repeatedly may be expensive. - * - * @param {!Renderer} renderer The renderer in use. - * @param {!BlockSvg} block The block to measure. - * @constructor - * @package * @extends {BaseRenderInfo} - * @alias Blockly.geras.RenderInfo - */ -const RenderInfo = function(renderer, block) { - RenderInfo.superClass_.constructor.call(this, renderer, block); -}; -object.inherits(RenderInfo, BaseRenderInfo); - -/** - * Get the block renderer in use. - * @return {!Renderer} The block renderer in use. - * @package */ -RenderInfo.prototype.getRenderer = function() { - return /** @type {!Renderer} */ (this.renderer_); -}; - -/** - * @override - */ -RenderInfo.prototype.populateBottomRow_ = function() { - RenderInfo.superClass_.populateBottomRow_.call(this); - - const followsStatement = this.block_.inputList.length && - this.block_.inputList[this.block_.inputList.length - 1].type === - inputTypes.STATEMENT; - - // 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 overwritten - // in the compute pass. - if (!followsStatement) { - this.bottomRow.minHeight = - this.constants_.MEDIUM_PADDING - this.constants_.DARK_PATH_OFFSET; +class RenderInfo extends BaseRenderInfo { + /** + * @param {!Renderer} renderer The renderer in use. + * @param {!BlockSvg} block The block to measure. + * @package + * @alias Blockly.geras.RenderInfo + */ + constructor(renderer, block) { + super(renderer, block); + } + + /** + * Get the block renderer in use. + * @return {!Renderer} The block renderer in use. + * @package + */ + getRenderer() { + return /** @type {!Renderer} */ (this.renderer_); + } + + /** + * @override + */ + populateBottomRow_() { + super.populateBottomRow_(); + + const followsStatement = this.block_.inputList.length && + this.block_.inputList[this.block_.inputList.length - 1].type === + inputTypes.STATEMENT; + + // 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 overwritten + // in the compute pass. + if (!followsStatement) { + this.bottomRow.minHeight = + this.constants_.MEDIUM_PADDING - this.constants_.DARK_PATH_OFFSET; + } } -}; -/** - * @override - */ -RenderInfo.prototype.addInput_ = function(input, activeRow) { - // Non-dummy inputs have visual representations onscreen. - if (this.isInline && input.type === inputTypes.VALUE) { - 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)); - activeRow.hasStatement = true; - } else if (input.type === inputTypes.VALUE) { - activeRow.elements.push(new ExternalValueInput(this.constants_, input)); - activeRow.hasExternalInput = true; - } else if (input.type === inputTypes.DUMMY) { - // Dummy inputs have no visual representation, but the information is still - // important. - activeRow.minHeight = - Math.max(activeRow.minHeight, this.constants_.DUMMY_INPUT_MIN_HEIGHT); - activeRow.hasDummyInput = true; - } - // Ignore row alignment if inline. - if (!this.isInline && activeRow.align === null) { - activeRow.align = input.align; + /** + * @override + */ + addInput_(input, activeRow) { + // Non-dummy inputs have visual representations onscreen. + if (this.isInline && input.type === inputTypes.VALUE) { + 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)); + activeRow.hasStatement = true; + } else if (input.type === inputTypes.VALUE) { + activeRow.elements.push(new ExternalValueInput(this.constants_, input)); + activeRow.hasExternalInput = true; + } else if (input.type === inputTypes.DUMMY) { + // Dummy inputs have no visual representation, but the information is still + // important. + activeRow.minHeight = + Math.max(activeRow.minHeight, this.constants_.DUMMY_INPUT_MIN_HEIGHT); + activeRow.hasDummyInput = true; + } + // Ignore row alignment if inline. + if (!this.isInline && activeRow.align === null) { + activeRow.align = input.align; + } } -}; -/** - * @override - */ -RenderInfo.prototype.addElemSpacing_ = function() { - let hasExternalInputs = false; - for (let i = 0, row; (row = this.rows[i]); i++) { - if (row.hasExternalInput) { - hasExternalInputs = true; + /** + * @override + */ + addElemSpacing_() { + let hasExternalInputs = false; + for (let i = 0, row; (row = this.rows[i]); i++) { + if (row.hasExternalInput) { + hasExternalInputs = true; + } } - } - for (let i = 0, row; (row = this.rows[i]); i++) { - const oldElems = row.elements; - row.elements = []; - // No spacing needed before the corner on the top row or the bottom row. - if (row.startsWithElemSpacer()) { - // There's a spacer before the first element in the row. - row.elements.push(new InRowSpacer( - this.constants_, this.getInRowSpacing_(null, oldElems[0]))); - } - if (!oldElems.length) { - continue; - } - for (let e = 0; e < oldElems.length - 1; e++) { - row.elements.push(oldElems[e]); - const spacing = this.getInRowSpacing_(oldElems[e], oldElems[e + 1]); - row.elements.push(new InRowSpacer(this.constants_, spacing)); - } - row.elements.push(oldElems[oldElems.length - 1]); - if (row.endsWithElemSpacer()) { - let spacing = this.getInRowSpacing_(oldElems[oldElems.length - 1], null); - if (hasExternalInputs && row.hasDummyInput) { - spacing += this.constants_.TAB_WIDTH; + for (let i = 0, row; (row = this.rows[i]); i++) { + const oldElems = row.elements; + row.elements = []; + // No spacing needed before the corner on the top row or the bottom row. + if (row.startsWithElemSpacer()) { + // There's a spacer before the first element in the row. + row.elements.push(new InRowSpacer( + this.constants_, this.getInRowSpacing_(null, oldElems[0]))); + } + if (!oldElems.length) { + continue; + } + for (let e = 0; e < oldElems.length - 1; e++) { + row.elements.push(oldElems[e]); + const spacing = this.getInRowSpacing_(oldElems[e], oldElems[e + 1]); + row.elements.push(new InRowSpacer(this.constants_, spacing)); + } + row.elements.push(oldElems[oldElems.length - 1]); + if (row.endsWithElemSpacer()) { + let spacing = this.getInRowSpacing_(oldElems[oldElems.length - 1], null); + if (hasExternalInputs && row.hasDummyInput) { + spacing += this.constants_.TAB_WIDTH; + } + // There's a spacer after the last element in the row. + row.elements.push(new InRowSpacer(this.constants_, spacing)); } - // There's a spacer after the last element in the row. - row.elements.push(new InRowSpacer(this.constants_, spacing)); } } -}; - -/** - * @override - */ -RenderInfo.prototype.getInRowSpacing_ = function(prev, next) { - if (!prev) { - // Between an editable field and the beginning of the row. - if (next && Types.isField(next) && - (/** @type {Field} */ (next)).isEditable) { - return this.constants_.MEDIUM_PADDING; - } - // Inline input at the beginning of the row. - if (next && Types.isInlineInput(next)) { - return this.constants_.MEDIUM_LARGE_PADDING; - } - if (next && Types.isStatementInput(next)) { - return this.constants_.STATEMENT_INPUT_PADDING_LEFT; - } - // Anything else at the beginning of the row. - return this.constants_.LARGE_PADDING; - } - // 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) && (/** @type {Field} */ (prev)).isEditable) { - return this.constants_.MEDIUM_PADDING; - } - // Padding at the end of an icon-only row to make the block shape clearer. - if (Types.isIcon(prev)) { - return (this.constants_.LARGE_PADDING * 2) + 1; - } - if (Types.isHat(prev)) { - return this.constants_.NO_PADDING; - } - // Establish a minimum width for a block with a previous or next connection. - if (Types.isPreviousOrNextConnection(prev)) { + /** + * @override + */ + getInRowSpacing_(prev, next) { + if (!prev) { + // Between an editable field and the beginning of the row. + if (next && Types.isField(next) && + (/** @type {Field} */ (next)).isEditable) { + return this.constants_.MEDIUM_PADDING; + } + // Inline input at the beginning of the row. + if (next && Types.isInlineInput(next)) { + return this.constants_.MEDIUM_LARGE_PADDING; + } + if (next && Types.isStatementInput(next)) { + return this.constants_.STATEMENT_INPUT_PADDING_LEFT; + } + // Anything else at the beginning of the row. return this.constants_.LARGE_PADDING; } - // Between rounded corner and the end of the row. - if (Types.isLeftRoundedCorner(prev)) { - return this.constants_.MIN_BLOCK_WIDTH; - } - // Between a jagged edge and the end of the row. - if (Types.isJaggedEdge(prev)) { - return this.constants_.NO_PADDING; - } - // Between noneditable fields and icons and the end of the row. - return this.constants_.LARGE_PADDING; - } - // Between inputs and the end of the row. - if (Types.isInput(prev) && !next) { - if (Types.isExternalInput(prev)) { - return this.constants_.NO_PADDING; - } else if (Types.isInlineInput(prev)) { + // 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) && (/** @type {Field} */ (prev)).isEditable) { + return this.constants_.MEDIUM_PADDING; + } + // Padding at the end of an icon-only row to make the block shape clearer. + if (Types.isIcon(prev)) { + return (this.constants_.LARGE_PADDING * 2) + 1; + } + if (Types.isHat(prev)) { + return this.constants_.NO_PADDING; + } + // Establish a minimum width for a block with a previous or next connection. + if (Types.isPreviousOrNextConnection(prev)) { + return this.constants_.LARGE_PADDING; + } + // Between rounded corner and the end of the row. + if (Types.isLeftRoundedCorner(prev)) { + return this.constants_.MIN_BLOCK_WIDTH; + } + // Between a jagged edge and the end of the row. + if (Types.isJaggedEdge(prev)) { + return this.constants_.NO_PADDING; + } + // Between noneditable fields and icons and the end of the row. return this.constants_.LARGE_PADDING; - } else if (Types.isStatementInput(prev)) { - return this.constants_.NO_PADDING; } - } - // 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) && (/** @type {Field} */ (prev)).isEditable) { - if (Types.isInlineInput(next)) { - return this.constants_.SMALL_PADDING; - } else if (Types.isExternalInput(next)) { - return this.constants_.SMALL_PADDING; - } - } else { - if (Types.isInlineInput(next)) { - return this.constants_.MEDIUM_LARGE_PADDING; - } else if (Types.isExternalInput(next)) { - return this.constants_.MEDIUM_LARGE_PADDING; - } else if (Types.isStatementInput(next)) { + // Between inputs and the end of the row. + if (Types.isInput(prev) && !next) { + if (Types.isExternalInput(prev)) { + return this.constants_.NO_PADDING; + } else if (Types.isInlineInput(prev)) { return this.constants_.LARGE_PADDING; + } else if (Types.isStatementInput(prev)) { + return this.constants_.NO_PADDING; } } - return this.constants_.LARGE_PADDING - 1; - } - // Spacing between an icon and an icon or field. - if (Types.isIcon(prev) && next && !Types.isInput(next)) { - return this.constants_.LARGE_PADDING; - } + // 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) && (/** @type {Field} */ (prev)).isEditable) { + if (Types.isInlineInput(next)) { + return this.constants_.SMALL_PADDING; + } else if (Types.isExternalInput(next)) { + return this.constants_.SMALL_PADDING; + } + } else { + if (Types.isInlineInput(next)) { + return this.constants_.MEDIUM_LARGE_PADDING; + } else if (Types.isExternalInput(next)) { + return this.constants_.MEDIUM_LARGE_PADDING; + } else if (Types.isStatementInput(next)) { + return this.constants_.LARGE_PADDING; + } + } + return this.constants_.LARGE_PADDING - 1; + } - // Spacing between an inline input and a field. - if (Types.isInlineInput(prev) && next && Types.isField(next)) { - // Editable field after inline input. - if ((/** @type {Field} */ (next)).isEditable) { - return this.constants_.MEDIUM_PADDING; - } else { - // Noneditable field after inline input. + // Spacing between an icon and an icon or field. + if (Types.isIcon(prev) && next && !Types.isInput(next)) { return this.constants_.LARGE_PADDING; } - } - if (Types.isLeftSquareCorner(prev) && next) { - // Spacing between a hat and a corner - if (Types.isHat(next)) { - return this.constants_.NO_PADDING; - } - // Spacing between a square corner and a previous or next connection - if (Types.isPreviousConnection(next)) { - return next.notchOffset; - } else if (Types.isNextConnection(next)) { - // Next connections are shifted slightly to the left (in both LTR and RTL) - // to make the dark path under the previous connection show through. - const offset = (this.RTL ? 1 : -1) * this.constants_.DARK_PATH_OFFSET / 2; - return next.notchOffset + offset; + // Spacing between an inline input and a field. + if (Types.isInlineInput(prev) && next && Types.isField(next)) { + // Editable field after inline input. + if ((/** @type {Field} */ (next)).isEditable) { + return this.constants_.MEDIUM_PADDING; + } else { + // Noneditable field after inline input. + return this.constants_.LARGE_PADDING; + } } - } - // Spacing between a rounded corner and a previous or next connection. - if (Types.isLeftRoundedCorner(prev) && next) { - if (Types.isPreviousConnection(next)) { - return next.notchOffset - this.constants_.CORNER_RADIUS; - } else if (Types.isNextConnection(next)) { - // Next connections are shifted slightly to the left (in both LTR and RTL) - // to make the dark path under the previous connection show through. - const offset = (this.RTL ? 1 : -1) * this.constants_.DARK_PATH_OFFSET / 2; - return next.notchOffset - this.constants_.CORNER_RADIUS + offset; + if (Types.isLeftSquareCorner(prev) && next) { + // Spacing between a hat and a corner + if (Types.isHat(next)) { + return this.constants_.NO_PADDING; + } + // Spacing between a square corner and a previous or next connection + if (Types.isPreviousConnection(next)) { + return next.notchOffset; + } else if (Types.isNextConnection(next)) { + // Next connections are shifted slightly to the left (in both LTR and RTL) + // to make the dark path under the previous connection show through. + const offset = (this.RTL ? 1 : -1) * this.constants_.DARK_PATH_OFFSET / 2; + return next.notchOffset + offset; + } } - } - // Spacing between two fields of the same editability. - if (Types.isField(prev) && next && Types.isField(next) && - ((/** @type {Field} */ (prev)).isEditable === - (/** @type {Field} */ (next)).isEditable)) { - return this.constants_.LARGE_PADDING; - } + // Spacing between a rounded corner and a previous or next connection. + if (Types.isLeftRoundedCorner(prev) && next) { + if (Types.isPreviousConnection(next)) { + return next.notchOffset - this.constants_.CORNER_RADIUS; + } else if (Types.isNextConnection(next)) { + // Next connections are shifted slightly to the left (in both LTR and RTL) + // to make the dark path under the previous connection show through. + const offset = (this.RTL ? 1 : -1) * this.constants_.DARK_PATH_OFFSET / 2; + return next.notchOffset - this.constants_.CORNER_RADIUS + offset; + } + } - // Spacing between anything and a jagged edge. - if (next && Types.isJaggedEdge(next)) { - return this.constants_.LARGE_PADDING; - } + // Spacing between two fields of the same editability. + if (Types.isField(prev) && next && Types.isField(next) && + ((/** @type {Field} */ (prev)).isEditable === + (/** @type {Field} */ (next)).isEditable)) { + return this.constants_.LARGE_PADDING; + } - return this.constants_.MEDIUM_PADDING; -}; + // Spacing between anything and a jagged edge. + if (next && Types.isJaggedEdge(next)) { + return this.constants_.LARGE_PADDING; + } -/** - * @override - */ -RenderInfo.prototype.getSpacerRowHeight_ = function(prev, next) { - // If we have an empty block add a spacer to increase the height. - if (Types.isTopRow(prev) && Types.isBottomRow(next)) { - return this.constants_.EMPTY_BLOCK_SPACER_HEIGHT; - } - // Top and bottom rows act as a spacer so we don't need any extra padding. - if (Types.isTopRow(prev) || Types.isBottomRow(next)) { - return this.constants_.NO_PADDING; - } - if (prev.hasExternalInput && next.hasExternalInput) { - return this.constants_.LARGE_PADDING; - } - if (!prev.hasStatement && next.hasStatement) { - return this.constants_.BETWEEN_STATEMENT_PADDING_Y; + return this.constants_.MEDIUM_PADDING; } - if (prev.hasStatement && next.hasStatement) { - return this.constants_.LARGE_PADDING; - } - if (!prev.hasStatement && next.hasDummyInput) { - return this.constants_.LARGE_PADDING; - } - if (prev.hasDummyInput) { - return this.constants_.LARGE_PADDING; - } - return this.constants_.MEDIUM_PADDING; -}; -/** - * @override - */ -RenderInfo.prototype.getElemCenterline_ = function(row, elem) { - if (Types.isSpacer(elem)) { - return row.yPos + elem.height / 2; - } - if (Types.isBottomRow(row)) { - const baseline = row.yPos + row.height - row.descenderHeight; - if (Types.isNextConnection(elem)) { - return baseline + elem.height / 2; + /** + * @override + */ + getSpacerRowHeight_(prev, next) { + // If we have an empty block add a spacer to increase the height. + if (Types.isTopRow(prev) && Types.isBottomRow(next)) { + return this.constants_.EMPTY_BLOCK_SPACER_HEIGHT; } - return baseline - elem.height / 2; - } - if (Types.isTopRow(row)) { - if (Types.isHat(elem)) { - return row.capline - elem.height / 2; + // Top and bottom rows act as a spacer so we don't need any extra padding. + if (Types.isTopRow(prev) || Types.isBottomRow(next)) { + return this.constants_.NO_PADDING; + } + if (prev.hasExternalInput && next.hasExternalInput) { + return this.constants_.LARGE_PADDING; + } + if (!prev.hasStatement && next.hasStatement) { + return this.constants_.BETWEEN_STATEMENT_PADDING_Y; + } + if (prev.hasStatement && next.hasStatement) { + return this.constants_.LARGE_PADDING; + } + if (!prev.hasStatement && next.hasDummyInput) { + return this.constants_.LARGE_PADDING; } - return row.capline + elem.height / 2; + if (prev.hasDummyInput) { + return this.constants_.LARGE_PADDING; + } + return this.constants_.MEDIUM_PADDING; } - let result = row.yPos; - if (Types.isField(elem) || Types.isIcon(elem)) { - result += (elem.height / 2); - if ((row.hasInlineInput || row.hasStatement) && - elem.height + this.constants_.TALL_INPUT_FIELD_OFFSET_Y <= row.height) { - result += this.constants_.TALL_INPUT_FIELD_OFFSET_Y; - } - } else if (Types.isInlineInput(elem)) { - result += elem.height / 2; - } else { - result += (row.height / 2); - } - return result; -}; + /** + * @override + */ + getElemCenterline_(row, elem) { + if (Types.isSpacer(elem)) { + return row.yPos + elem.height / 2; + } + if (Types.isBottomRow(row)) { + 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)) { + if (Types.isHat(elem)) { + return row.capline - elem.height / 2; + } + return row.capline + elem.height / 2; + } -/** - * @override - */ -RenderInfo.prototype.alignRowElements_ = function() { - if (!this.isInline) { - RenderInfo.superClass_.alignRowElements_.call(this); - return; + let result = row.yPos; + if (Types.isField(elem) || Types.isIcon(elem)) { + result += (elem.height / 2); + if ((row.hasInlineInput || row.hasStatement) && + elem.height + this.constants_.TALL_INPUT_FIELD_OFFSET_Y <= row.height) { + result += this.constants_.TALL_INPUT_FIELD_OFFSET_Y; + } + } else if (Types.isInlineInput(elem)) { + result += elem.height / 2; + } else { + result += (row.height / 2); + } + return result; } - // Walk backgrounds through rows on the block, keeping track of the right - // input edge. - let nextRightEdge = 0; - let prevInput = null; - for (let i = this.rows.length - 1, row; (row = this.rows[i]); i--) { - row.nextRightEdge = nextRightEdge; - if (Types.isInputRow(row)) { - if (row.hasStatement) { - this.alignStatementRow_( - /** @type {!InputRow} */ (row)); + /** + * @override + */ + alignRowElements_() { + if (!this.isInline) { + super.alignRowElements_(); + return; + } + + // Walk backgrounds through rows on the block, keeping track of the right + // input edge. + let nextRightEdge = 0; + let prevInput = null; + for (let i = this.rows.length - 1, row; (row = this.rows[i]); i--) { + row.nextRightEdge = nextRightEdge; + if (Types.isInputRow(row)) { + if (row.hasStatement) { + this.alignStatementRow_( + /** @type {!InputRow} */ (row)); + } + if (prevInput && prevInput.hasStatement && row.width < prevInput.width) { + row.nextRightEdge = prevInput.width; + } else { + nextRightEdge = row.width; + } + prevInput = row; } - if (prevInput && prevInput.hasStatement && row.width < prevInput.width) { - row.nextRightEdge = prevInput.width; + } + // Walk down each row from the top, comparing the prev and next right input + // edges and setting the desired width to the max of the two. + let prevRightEdge = 0; + for (let i = 0, row; (row = this.rows[i]); i++) { + if (row.hasStatement) { + prevRightEdge = this.getDesiredRowWidth_(row); + } else if (Types.isSpacer(row)) { + // Set the spacer row to the max of the prev or next input width. + row.width = Math.max(prevRightEdge, row.nextRightEdge); } else { - nextRightEdge = row.width; + const currentWidth = row.width; + const desiredWidth = Math.max(prevRightEdge, row.nextRightEdge); + const missingSpace = desiredWidth - currentWidth; + if (missingSpace > 0) { + this.addAlignmentPadding_(row, missingSpace); + } + prevRightEdge = row.width; } - prevInput = row; } } - // Walk down each row from the top, comparing the prev and next right input - // edges and setting the desired width to the max of the two. - let prevRightEdge = 0; - for (let i = 0, row; (row = this.rows[i]); i++) { - if (row.hasStatement) { - prevRightEdge = this.getDesiredRowWidth_(row); - } else if (Types.isSpacer(row)) { - // Set the spacer row to the max of the prev or next input width. - row.width = Math.max(prevRightEdge, row.nextRightEdge); - } else { - const currentWidth = row.width; - const desiredWidth = Math.max(prevRightEdge, row.nextRightEdge); - const missingSpace = desiredWidth - currentWidth; - if (missingSpace > 0) { - this.addAlignmentPadding_(row, missingSpace); + + /** + * @override + */ + getDesiredRowWidth_(row) { + // Limit the width of a statement row when a block is inline. + if (this.isInline && row.hasStatement) { + return this.statementEdge + this.constants_.MAX_BOTTOM_WIDTH + this.startX; + } + return super.getDesiredRowWidth_(row); + } + + /** + * @override + */ + finalize_() { + // Performance note: this could be combined with the draw pass, if the time + // that this takes is excessive. But it shouldn't be, because it only + // accesses and sets properties that already exist on the objects. + let widestRowWithConnectedBlocks = 0; + let yCursor = 0; + for (let i = 0, row; (row = this.rows[i]); i++) { + row.yPos = yCursor; + row.xPos = this.startX; + yCursor += row.height; + + widestRowWithConnectedBlocks = + Math.max(widestRowWithConnectedBlocks, row.widthWithConnectedBlocks); + // Add padding to the bottom row if block height is less than minimum + const heightWithoutHat = yCursor - this.topRow.ascenderHeight; + if (row === this.bottomRow && + heightWithoutHat < this.constants_.MIN_BLOCK_HEIGHT) { + // But the hat height shouldn't be part of this. + const diff = this.constants_.MIN_BLOCK_HEIGHT - heightWithoutHat; + this.bottomRow.height += diff; + yCursor += diff; } - prevRightEdge = row.width; + this.recordElemPositions_(row); + } + if (this.outputConnection && this.block_.nextConnection && + this.block_.nextConnection.isConnected()) { + // Include width of connected block in value to stack width measurement. + widestRowWithConnectedBlocks = Math.max( + widestRowWithConnectedBlocks, + this.block_.nextConnection.targetBlock().getHeightWidth().width - + this.constants_.DARK_PATH_OFFSET); } - } -}; -/** - * @override - */ -RenderInfo.prototype.getDesiredRowWidth_ = function(row) { - // Limit the width of a statement row when a block is inline. - if (this.isInline && row.hasStatement) { - return this.statementEdge + this.constants_.MAX_BOTTOM_WIDTH + this.startX; - } - return RenderInfo.superClass_.getDesiredRowWidth_.call(this, row); -}; + this.bottomRow.baseline = yCursor - this.bottomRow.descenderHeight; -/** - * @override - */ -RenderInfo.prototype.finalize_ = function() { - // Performance note: this could be combined with the draw pass, if the time - // that this takes is excessive. But it shouldn't be, because it only - // accesses and sets properties that already exist on the objects. - let widestRowWithConnectedBlocks = 0; - let yCursor = 0; - for (let i = 0, row; (row = this.rows[i]); i++) { - row.yPos = yCursor; - row.xPos = this.startX; - yCursor += row.height; - - widestRowWithConnectedBlocks = - Math.max(widestRowWithConnectedBlocks, row.widthWithConnectedBlocks); - // Add padding to the bottom row if block height is less than minimum - const heightWithoutHat = yCursor - this.topRow.ascenderHeight; - if (row === this.bottomRow && - heightWithoutHat < this.constants_.MIN_BLOCK_HEIGHT) { - // But the hat height shouldn't be part of this. - const diff = this.constants_.MIN_BLOCK_HEIGHT - heightWithoutHat; - this.bottomRow.height += diff; - yCursor += diff; - } - this.recordElemPositions_(row); + // The dark (lowlight) adds to the size of the block in both x and y. + this.widthWithChildren = widestRowWithConnectedBlocks + this.startX + + this.constants_.DARK_PATH_OFFSET; + this.width += this.constants_.DARK_PATH_OFFSET; + this.height = yCursor + this.constants_.DARK_PATH_OFFSET; + this.startY = this.topRow.capline; } - if (this.outputConnection && this.block_.nextConnection && - this.block_.nextConnection.isConnected()) { - // Include width of connected block in value to stack width measurement. - widestRowWithConnectedBlocks = Math.max( - widestRowWithConnectedBlocks, - this.block_.nextConnection.targetBlock().getHeightWidth().width - - this.constants_.DARK_PATH_OFFSET); - } - - this.bottomRow.baseline = yCursor - this.bottomRow.descenderHeight; - - // The dark (lowlight) adds to the size of the block in both x and y. - this.widthWithChildren = widestRowWithConnectedBlocks + this.startX + - this.constants_.DARK_PATH_OFFSET; - this.width += this.constants_.DARK_PATH_OFFSET; - this.height = yCursor + this.constants_.DARK_PATH_OFFSET; - this.startY = this.topRow.capline; -}; +} exports.RenderInfo = RenderInfo; diff --git a/core/renderers/geras/measurables/inline_input.js b/core/renderers/geras/measurables/inline_input.js index 5df435181d5..a759be62411 100644 --- a/core/renderers/geras/measurables/inline_input.js +++ b/core/renderers/geras/measurables/inline_input.js @@ -17,7 +17,6 @@ */ goog.module('Blockly.geras.InlineInput'); -const object = goog.require('Blockly.utils.object'); /* eslint-disable-next-line no-unused-vars */ const {ConstantProvider} = goog.requireType('Blockly.blockRendering.ConstantProvider'); const {InlineInput: BaseInlineInput} = goog.require('Blockly.blockRendering.InlineInput'); @@ -27,26 +26,28 @@ const {Input} = goog.requireType('Blockly.Input'); /** * An object containing information about the space an inline input takes up - * during rendering - * @param {!ConstantProvider} constants The rendering - * constants provider. - * @param {!Input} input The inline input to measure and store - * information for. - * @package - * @constructor + * during rendering. * @extends {BaseInlineInput} - * @alias Blockly.geras.InlineInput */ -const InlineInput = function(constants, input) { - InlineInput.superClass_.constructor.call(this, constants, input); +class InlineInput extends BaseInlineInput { + /** + * @param {!ConstantProvider} constants The rendering + * constants provider. + * @param {!Input} input The inline input to measure and store + * information for. + * @package + * @alias Blockly.geras.InlineInput + */ + constructor(constants, input) { + super(constants, input); - if (this.connectedBlock) { - // We allow the dark path to show on the parent block so that the child - // block looks embossed. This takes up an extra pixel in both x and y. - this.width += this.constants_.DARK_PATH_OFFSET; - this.height += this.constants_.DARK_PATH_OFFSET; + if (this.connectedBlock) { + // We allow the dark path to show on the parent block so that the child + // block looks embossed. This takes up an extra pixel in both x and y. + this.width += this.constants_.DARK_PATH_OFFSET; + this.height += this.constants_.DARK_PATH_OFFSET; + } } -}; -object.inherits(InlineInput, BaseInlineInput); +} exports.InlineInput = InlineInput; diff --git a/core/renderers/geras/measurables/statement_input.js b/core/renderers/geras/measurables/statement_input.js index d4636af3c51..ce696b20161 100644 --- a/core/renderers/geras/measurables/statement_input.js +++ b/core/renderers/geras/measurables/statement_input.js @@ -17,7 +17,6 @@ */ goog.module('Blockly.geras.StatementInput'); -const object = goog.require('Blockly.utils.object'); /* eslint-disable-next-line no-unused-vars */ const {ConstantProvider} = goog.requireType('Blockly.blockRendering.ConstantProvider'); /* eslint-disable-next-line no-unused-vars */ @@ -27,25 +26,27 @@ const {StatementInput: BaseStatementInput} = goog.require('Blockly.blockRenderin /** * An object containing information about the space a statement input takes up - * during rendering - * @param {!ConstantProvider} constants The rendering - * constants provider. - * @param {!Input} input The statement input to measure and store - * information for. - * @package - * @constructor + * during rendering. * @extends {BaseStatementInput} - * @alias Blockly.geras.StatementInput */ -const StatementInput = function(constants, input) { - StatementInput.superClass_.constructor.call(this, constants, input); +class StatementInput extends BaseStatementInput { + /** + * @param {!ConstantProvider} constants The rendering + * constants provider. + * @param {!Input} input The statement input to measure and store + * information for. + * @package + * @alias Blockly.geras.StatementInput + */ + constructor(constants, input) { + super(constants, input); - if (this.connectedBlock) { - // We allow the dark path to show on the parent block so that the child - // block looks embossed. This takes up an extra pixel in both x and y. - this.height += this.constants_.DARK_PATH_OFFSET; + if (this.connectedBlock) { + // We allow the dark path to show on the parent block so that the child + // block looks embossed. This takes up an extra pixel in both x and y. + this.height += this.constants_.DARK_PATH_OFFSET; + } } -}; -object.inherits(StatementInput, BaseStatementInput); +} exports.StatementInput = StatementInput; diff --git a/core/renderers/geras/renderer.js b/core/renderers/geras/renderer.js index 25629843c0d..fdd0a8ce4b2 100644 --- a/core/renderers/geras/renderer.js +++ b/core/renderers/geras/renderer.js @@ -16,7 +16,6 @@ goog.module('Blockly.geras.Renderer'); const blockRendering = goog.require('Blockly.blockRendering'); -const object = goog.require('Blockly.utils.object'); /* eslint-disable-next-line no-unused-vars */ const {BlockSvg} = goog.requireType('Blockly.BlockSvg'); /* eslint-disable-next-line no-unused-vars */ @@ -35,116 +34,118 @@ const {Theme} = goog.requireType('Blockly.Theme'); /** * The geras renderer. - * @param {string} name The renderer name. - * @package - * @constructor * @extends {BaseRenderer} - * @alias Blockly.geras.Renderer */ -const Renderer = function(name) { - Renderer.superClass_.constructor.call(this, name); - +class Renderer extends BaseRenderer { /** - * The renderer's highlight constant provider. - * @type {HighlightConstantProvider} - * @private + * @param {string} name The renderer name. + * @package + * @alias Blockly.geras.Renderer */ - this.highlightConstants_ = null; -}; -object.inherits(Renderer, BaseRenderer); + constructor(name) { + super(name); -/** - * Initialize the renderer. Geras has a highlight provider in addition to - * the normal constant provider. - * @package - * @override - */ -Renderer.prototype.init = function(theme, opt_rendererOverrides) { - Renderer.superClass_.init.call(this, theme, opt_rendererOverrides); - this.highlightConstants_ = this.makeHighlightConstants_(); - this.highlightConstants_.init(); -}; + /** + * The renderer's highlight constant provider. + * @type {HighlightConstantProvider} + * @private + */ + this.highlightConstants_ = null; + } -/** - * @override - */ -Renderer.prototype.refreshDom = function(svg, theme) { - Renderer.superClass_.refreshDom.call(this, svg, theme); - this.getHighlightConstants().init(); -}; + /** + * Initialize the renderer. Geras has a highlight provider in addition to + * the normal constant provider. + * @package + * @override + */ + init(theme, opt_rendererOverrides) { + super.init(theme, opt_rendererOverrides); + this.highlightConstants_ = this.makeHighlightConstants_(); + this.highlightConstants_.init(); + } -/** - * @override - */ -Renderer.prototype.makeConstants_ = function() { - return new ConstantProvider(); -}; + /** + * @override + */ + refreshDom(svg, theme) { + super.refreshDom(svg, theme); + this.getHighlightConstants().init(); + } -/** - * Create a new instance of the renderer's render info object. - * @param {!BlockSvg} block The block to measure. - * @return {!RenderInfo} The render info object. - * @protected - * @override - */ -Renderer.prototype.makeRenderInfo_ = function(block) { - return new RenderInfo(this, block); -}; + /** + * @override + */ + makeConstants_() { + return new ConstantProvider(); + } -/** - * Create a new instance of the renderer's drawer. - * @param {!BlockSvg} block The block to render. - * @param {!BaseRenderInfo} info An object containing all - * information needed to render this block. - * @return {!Drawer} The drawer. - * @protected - * @override - */ -Renderer.prototype.makeDrawer_ = function(block, info) { - return new Drawer( - block, - /** @type {!RenderInfo} */ (info)); -}; + /** + * Create a new instance of the renderer's render info object. + * @param {!BlockSvg} block The block to measure. + * @return {!RenderInfo} The render info object. + * @protected + * @override + */ + makeRenderInfo_(block) { + return new RenderInfo(this, block); + } -/** - * Create a new instance of a renderer path object. - * @param {!SVGElement} root The root SVG element. - * @param {!Theme.BlockStyle} style The style object to use for - * colouring. - * @return {!PathObject} The renderer path object. - * @package - * @override - */ -Renderer.prototype.makePathObject = function(root, style) { - return new PathObject( - root, style, - /** @type {!ConstantProvider} */ (this.getConstants())); -}; + /** + * Create a new instance of the renderer's drawer. + * @param {!BlockSvg} block The block to render. + * @param {!BaseRenderInfo} info An object containing all + * information needed to render this block. + * @return {!Drawer} The drawer. + * @protected + * @override + */ + makeDrawer_(block, info) { + return new Drawer( + block, + /** @type {!RenderInfo} */ (info)); + } -/** - * Create a new instance of the renderer's highlight constant provider. - * @return {!HighlightConstantProvider} The highlight constant - * provider. - * @protected - */ -Renderer.prototype.makeHighlightConstants_ = function() { - return new HighlightConstantProvider( - /** @type {!BaseConstantProvider} */ - (this.getConstants())); -}; + /** + * Create a new instance of a renderer path object. + * @param {!SVGElement} root The root SVG element. + * @param {!Theme.BlockStyle} style The style object to use for + * colouring. + * @return {!PathObject} The renderer path object. + * @package + * @override + */ + makePathObject(root, style) { + return new PathObject( + root, style, + /** @type {!ConstantProvider} */ (this.getConstants())); + } -/** - * Get the renderer's highlight constant provider. We assume that when this is - * called, the renderer has already been initialized. - * @return {!HighlightConstantProvider} The highlight constant - * provider. - * @package - */ -Renderer.prototype.getHighlightConstants = function() { - return ( - /** @type {!HighlightConstantProvider} */ - (this.highlightConstants_)); -}; + /** + * Create a new instance of the renderer's highlight constant provider. + * @return {!HighlightConstantProvider} The highlight constant + * provider. + * @protected + */ + makeHighlightConstants_() { + return new HighlightConstantProvider( + /** @type {!BaseConstantProvider} */ + (this.getConstants())); + } + + /** + * Get the renderer's highlight constant provider. We assume that when this is + * called, the renderer has already been initialized. + * @return {!HighlightConstantProvider} The highlight constant + * provider. + * @package + */ + getHighlightConstants() { + return ( + /** @type {!HighlightConstantProvider} */ + (this.highlightConstants_)); + } +} blockRendering.register('geras', Renderer); From 3b0e6565df95525e7161f0f415cda3ca1683dc67 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 13 Jan 2022 12:23:41 -0800 Subject: [PATCH 6/7] chore: rebuild and format --- core/renderers/geras/highlight_constants.js | 13 +++--- core/renderers/geras/highlighter.js | 10 +++-- core/renderers/geras/info.js | 39 ++++++++++------- core/renderers/geras/renderer.js | 4 +- core/renderers/thrasos/info.js | 9 ++-- core/renderers/zelos/constants.js | 43 ++++++++++--------- core/renderers/zelos/drawer.js | 12 +++--- core/renderers/zelos/info.js | 32 +++++++------- core/renderers/zelos/marker_svg.js | 3 +- core/renderers/zelos/path_object.js | 11 ++--- core/renderers/zelos/renderer.js | 6 +-- scripts/gulpfiles/chunks.json | 2 +- tests/deps.js | 46 ++++++++++----------- 13 files changed, 127 insertions(+), 103 deletions(-) diff --git a/core/renderers/geras/highlight_constants.js b/core/renderers/geras/highlight_constants.js index 8ed71679acc..23f26d80837 100644 --- a/core/renderers/geras/highlight_constants.js +++ b/core/renderers/geras/highlight_constants.js @@ -127,7 +127,8 @@ class HighlightConstantProvider { 'a', '0 0,0', radius + offset, svgPaths.point(radius + offset, radius + offset)); - const pathBottomLtr = svgPaths.moveBy(distance45outside, -distance45outside) + + const pathBottomLtr = + svgPaths.moveBy(distance45outside, -distance45outside) + svgPaths.arc( 'a', '0 0,0', radius + offset, svgPaths.point( @@ -205,11 +206,12 @@ class HighlightConstantProvider { const width = this.constantProvider.TAB_WIDTH; const height = this.constantProvider.TAB_HEIGHT; - // This is how much of the vertical block edge is actually drawn by the puzzle - // tab. + // This is how much of the vertical block edge is actually drawn by the + // puzzle tab. const verticalOverlap = 2.5; - const highlightRtlUp = svgPaths.moveBy(-2, -height + verticalOverlap + 0.9) + + const highlightRtlUp = + svgPaths.moveBy(-2, -height + verticalOverlap + 0.9) + svgPaths.lineTo(width * -0.45, -2.1); const highlightRtlDown = svgPaths.lineOnAxis('v', verticalOverlap) + @@ -226,7 +228,8 @@ class HighlightConstantProvider { const highlightLtrUp = svgPaths.lineOnAxis('v', -1.5) + svgPaths.moveBy(width * -0.92, -0.5) + svgPaths.curve( - 'q', [svgPaths.point(width * -0.19, -5.5), svgPaths.point(0, -11)]) + + 'q', + [svgPaths.point(width * -0.19, -5.5), svgPaths.point(0, -11)]) + svgPaths.moveBy(width * 0.92, 1); const highlightLtrDown = diff --git a/core/renderers/geras/highlighter.js b/core/renderers/geras/highlighter.js index c0fbf74b782..c81beb0779b 100644 --- a/core/renderers/geras/highlighter.js +++ b/core/renderers/geras/highlighter.js @@ -115,9 +115,10 @@ class Highlighter { } else if (Types.isHat(elem)) { this.steps_ += this.startPaths_.path(this.RTL_); } else if (Types.isSpacer(elem) && elem.width !== 0) { - // The end point of the spacer needs to be offset by the highlight amount. - // So instead of using the spacer's width for a relative horizontal, use - // its width and position for an absolute horizontal move. + // The end point of the spacer needs to be offset by the highlight + // amount. So instead of using the spacer's width for a relative + // horizontal, use its width and position for an absolute horizontal + // move. this.steps_ += svgPaths.lineOnAxis( 'H', elem.xPos + elem.width - this.highlightOffset_); } @@ -252,7 +253,8 @@ class Highlighter { if (!this.RTL_) { const topRow = this.info_.topRow; if (Types.isLeftRoundedCorner(topRow.elements[0])) { - this.steps_ += svgPaths.lineOnAxis('V', this.outsideCornerPaths_.height); + this.steps_ += + svgPaths.lineOnAxis('V', this.outsideCornerPaths_.height); } else { this.steps_ += svgPaths.lineOnAxis('V', topRow.capline + this.highlightOffset_); diff --git a/core/renderers/geras/info.js b/core/renderers/geras/info.js index d7a2b8f6d5f..d8e45c27194 100644 --- a/core/renderers/geras/info.js +++ b/core/renderers/geras/info.js @@ -74,8 +74,8 @@ class RenderInfo extends BaseRenderInfo { // 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 overwritten - // in the compute pass. + // If one of the row's elements has a greater height this will be + // overwritten in the compute pass. if (!followsStatement) { this.bottomRow.minHeight = this.constants_.MEDIUM_PADDING - this.constants_.DARK_PATH_OFFSET; @@ -97,8 +97,8 @@ class RenderInfo extends BaseRenderInfo { activeRow.elements.push(new ExternalValueInput(this.constants_, input)); activeRow.hasExternalInput = true; } else if (input.type === inputTypes.DUMMY) { - // Dummy inputs have no visual representation, but the information is still - // important. + // Dummy inputs have no visual representation, but the information is + // still important. activeRow.minHeight = Math.max(activeRow.minHeight, this.constants_.DUMMY_INPUT_MIN_HEIGHT); activeRow.hasDummyInput = true; @@ -138,7 +138,8 @@ class RenderInfo extends BaseRenderInfo { } row.elements.push(oldElems[oldElems.length - 1]); if (row.endsWithElemSpacer()) { - let spacing = this.getInRowSpacing_(oldElems[oldElems.length - 1], null); + let spacing = + this.getInRowSpacing_(oldElems[oldElems.length - 1], null); if (hasExternalInputs && row.hasDummyInput) { spacing += this.constants_.TAB_WIDTH; } @@ -182,7 +183,8 @@ class RenderInfo extends BaseRenderInfo { if (Types.isHat(prev)) { return this.constants_.NO_PADDING; } - // Establish a minimum width for a block with a previous or next connection. + // Establish a minimum width for a block with a previous or next + // connection. if (Types.isPreviousOrNextConnection(prev)) { return this.constants_.LARGE_PADDING; } @@ -255,9 +257,11 @@ class RenderInfo extends BaseRenderInfo { if (Types.isPreviousConnection(next)) { return next.notchOffset; } else if (Types.isNextConnection(next)) { - // Next connections are shifted slightly to the left (in both LTR and RTL) - // to make the dark path under the previous connection show through. - const offset = (this.RTL ? 1 : -1) * this.constants_.DARK_PATH_OFFSET / 2; + // Next connections are shifted slightly to the left (in both LTR and + // RTL) to make the dark path under the previous connection show + // through. + const offset = + (this.RTL ? 1 : -1) * this.constants_.DARK_PATH_OFFSET / 2; return next.notchOffset + offset; } } @@ -267,9 +271,11 @@ class RenderInfo extends BaseRenderInfo { if (Types.isPreviousConnection(next)) { return next.notchOffset - this.constants_.CORNER_RADIUS; } else if (Types.isNextConnection(next)) { - // Next connections are shifted slightly to the left (in both LTR and RTL) - // to make the dark path under the previous connection show through. - const offset = (this.RTL ? 1 : -1) * this.constants_.DARK_PATH_OFFSET / 2; + // Next connections are shifted slightly to the left (in both LTR and + // RTL) to make the dark path under the previous connection show + // through. + const offset = + (this.RTL ? 1 : -1) * this.constants_.DARK_PATH_OFFSET / 2; return next.notchOffset - this.constants_.CORNER_RADIUS + offset; } } @@ -344,7 +350,8 @@ class RenderInfo extends BaseRenderInfo { if (Types.isField(elem) || Types.isIcon(elem)) { result += (elem.height / 2); if ((row.hasInlineInput || row.hasStatement) && - elem.height + this.constants_.TALL_INPUT_FIELD_OFFSET_Y <= row.height) { + elem.height + this.constants_.TALL_INPUT_FIELD_OFFSET_Y <= + row.height) { result += this.constants_.TALL_INPUT_FIELD_OFFSET_Y; } } else if (Types.isInlineInput(elem)) { @@ -375,7 +382,8 @@ class RenderInfo extends BaseRenderInfo { this.alignStatementRow_( /** @type {!InputRow} */ (row)); } - if (prevInput && prevInput.hasStatement && row.width < prevInput.width) { + if (prevInput && prevInput.hasStatement && + row.width < prevInput.width) { row.nextRightEdge = prevInput.width; } else { nextRightEdge = row.width; @@ -410,7 +418,8 @@ class RenderInfo extends BaseRenderInfo { getDesiredRowWidth_(row) { // Limit the width of a statement row when a block is inline. if (this.isInline && row.hasStatement) { - return this.statementEdge + this.constants_.MAX_BOTTOM_WIDTH + this.startX; + return this.statementEdge + this.constants_.MAX_BOTTOM_WIDTH + + this.startX; } return super.getDesiredRowWidth_(row); } diff --git a/core/renderers/geras/renderer.js b/core/renderers/geras/renderer.js index fdd0a8ce4b2..a4b8ef0cb96 100644 --- a/core/renderers/geras/renderer.js +++ b/core/renderers/geras/renderer.js @@ -134,8 +134,8 @@ class Renderer extends BaseRenderer { } /** - * Get the renderer's highlight constant provider. We assume that when this is - * called, the renderer has already been initialized. + * Get the renderer's highlight constant provider. We assume that when this + * is called, the renderer has already been initialized. * @return {!HighlightConstantProvider} The highlight constant * provider. * @package diff --git a/core/renderers/thrasos/info.js b/core/renderers/thrasos/info.js index 584fecd9553..cf894842eea 100644 --- a/core/renderers/thrasos/info.js +++ b/core/renderers/thrasos/info.js @@ -85,7 +85,8 @@ class RenderInfo extends BaseRenderInfo { } row.elements.push(oldElems[oldElems.length - 1]); if (row.endsWithElemSpacer()) { - let spacing = this.getInRowSpacing_(oldElems[oldElems.length - 1], null); + let spacing = + this.getInRowSpacing_(oldElems[oldElems.length - 1], null); if (hasExternalInputs && row.hasDummyInput) { spacing += this.constants_.TAB_WIDTH; } @@ -129,7 +130,8 @@ class RenderInfo extends BaseRenderInfo { if (Types.isHat(prev)) { return this.constants_.NO_PADDING; } - // Establish a minimum width for a block with a previous or next connection. + // Establish a minimum width for a block with a previous or next + // connection. if (Types.isPreviousOrNextConnection(prev)) { return this.constants_.LARGE_PADDING; } @@ -274,7 +276,8 @@ class RenderInfo extends BaseRenderInfo { let result = row.yPos; if (Types.isField(elem) && row.hasStatement) { - const offset = this.constants_.TALL_INPUT_FIELD_OFFSET_Y + elem.height / 2; + const offset = + this.constants_.TALL_INPUT_FIELD_OFFSET_Y + elem.height / 2; result += offset; } else { result += (row.height / 2); diff --git a/core/renderers/zelos/constants.js b/core/renderers/zelos/constants.js index 8caec78d802..7eb47068db1 100644 --- a/core/renderers/zelos/constants.js +++ b/core/renderers/zelos/constants.js @@ -372,8 +372,8 @@ class ConstantProvider extends BaseConstantProvider { this.selectedGlowFilter_ = null; /** - * The ID of the replacement glow filter, or the empty string if no filter is - * set. + * The ID of the replacement glow filter, or the empty string if no filter + * is set. * @type {string} * @package */ @@ -440,7 +440,8 @@ class ConstantProvider extends BaseConstantProvider { this.SELECTED_GLOW_COLOUR = theme.getComponentStyle('selectedGlowColour') || this.SELECTED_GLOW_COLOUR; - const selectedGlowSize = Number(theme.getComponentStyle('selectedGlowSize')); + const selectedGlowSize = + Number(theme.getComponentStyle('selectedGlowSize')); this.SELECTED_GLOW_SIZE = selectedGlowSize && !isNaN(selectedGlowSize) ? selectedGlowSize : this.SELECTED_GLOW_SIZE; @@ -494,10 +495,10 @@ class ConstantProvider extends BaseConstantProvider { /** * Make the main path for the hexagonal connection shape out of two lines. - * The lines are defined with relative positions and require the block height. - * The 'up' and 'down' versions of the paths are the same, but the Y sign - * flips. The 'left' and 'right' versions of the path are also the same, but - * the X sign flips. + * The lines are defined with relative positions and require the block + * height. The 'up' and 'down' versions of the paths are the same, but the Y + * sign flips. The 'left' and 'right' versions of the path are also the + * same, but the X sign flips. * @param {number} height The height of the block the connection is on. * @param {boolean} up True if the path should be drawn from bottom to top, * false otherwise. @@ -559,12 +560,11 @@ class ConstantProvider extends BaseConstantProvider { /** * Make the main path for the rounded connection shape out of two arcs and * a line that joins them. The arcs are defined with relative positions. - * Usually, the height of the block is split between the two arcs. In the case - * where the height of the block exceeds the maximum height, a line is drawn - * in between the two arcs. - * The 'up' and 'down' versions of the paths are the same, but the Y sign - * flips. The 'up' and 'right' versions of the path flip the sweep-flag - * which moves the arc at negative angles. + * Usually, the height of the block is split between the two arcs. In the + * case where the height of the block exceeds the maximum height, a line is + * drawn in between the two arcs. The 'up' and 'down' versions of the paths + * are the same, but the Y sign flips. The 'up' and 'right' versions of the + * path flip the sweep-flag which moves the arc at negative angles. * @param {number} blockHeight The height of the block the connection is on. * @param {boolean} up True if the path should be drawn from bottom to top, * false otherwise. @@ -579,7 +579,8 @@ class ConstantProvider extends BaseConstantProvider { const radius = height / 2; return svgPaths.arc( 'a', '0 0,1', radius, - svgPaths.point((up ? -1 : 1) * radius, (up ? -1 : 1) * radius)) + + svgPaths.point( + (up ? -1 : 1) * radius, (up ? -1 : 1) * radius)) + svgPaths.lineOnAxis('v', (right ? 1 : -1) * remainingHeight) + svgPaths.arc( 'a', '0 0,1', radius, @@ -631,8 +632,8 @@ class ConstantProvider extends BaseConstantProvider { * and a single line in-between (a and v). These are defined in relative * positions and require the height of the block. * The 'left' and 'right' versions of the paths are the same, but the Y sign - * flips. The 'up' and 'down' versions of the path determine where the corner - * point is placed and in turn the direction of the corners. + * flips. The 'up' and 'down' versions of the path determine where the + * corner point is placed and in turn the direction of the corners. * @param {number} height The height of the block the connection is on. * @param {boolean} up True if the path should be drawn from bottom to top, * false otherwise. @@ -644,7 +645,8 @@ class ConstantProvider extends BaseConstantProvider { const innerHeight = height - radius * 2; return svgPaths.arc( 'a', '0 0,1', radius, - svgPaths.point((up ? -1 : 1) * radius, (up ? -1 : 1) * radius)) + + svgPaths.point( + (up ? -1 : 1) * radius, (up ? -1 : 1) * radius)) + svgPaths.lineOnAxis('v', (right ? 1 : -1) * innerHeight) + svgPaths.arc( 'a', '0 0,1', radius, @@ -965,13 +967,14 @@ class ConstantProvider extends BaseConstantProvider { selector + ' .blocklyDraggable:not(.blocklyDisabled)', ' .blocklyEditableText:not(.editing):hover>rect,', selector + ' .blocklyDraggable:not(.blocklyDisabled)', - ' .blocklyEditableText:not(.editing):hover>.blocklyPath {', 'stroke: #fff;', - 'stroke-width: 2;', '}', + ' .blocklyEditableText:not(.editing):hover>.blocklyPath {', + 'stroke: #fff;', 'stroke-width: 2;', '}', // Text field input. selector + ' .blocklyHtmlInput {', 'font-family: ' + this.FIELD_TEXT_FONTFAMILY + ';', - 'font-weight: ' + this.FIELD_TEXT_FONTWEIGHT + ';', 'color: #575E75;', '}', + 'font-weight: ' + this.FIELD_TEXT_FONTWEIGHT + ';', 'color: #575E75;', + '}', // Dropdown field. selector + ' .blocklyDropdownText {', 'fill: #fff !important;', '}', diff --git a/core/renderers/zelos/drawer.js b/core/renderers/zelos/drawer.js index 19301997ee9..9c9f7917eb8 100644 --- a/core/renderers/zelos/drawer.js +++ b/core/renderers/zelos/drawer.js @@ -115,9 +115,9 @@ class Drawer extends BaseDrawer { const cornerHeight = this.constants_.INSIDE_CORNERS.rightHeight; const remainingHeight = row.height - (row.precedesStatement ? cornerHeight : 0); - this.outlinePath_ += - (row.followsStatement ? this.constants_.INSIDE_CORNERS.pathBottomRight : - '') + + this.outlinePath_ += (row.followsStatement ? + this.constants_.INSIDE_CORNERS.pathBottomRight : + '') + (remainingHeight > 0 ? svgPaths.lineOnAxis('V', row.yPos + remainingHeight) : '') + @@ -198,7 +198,8 @@ class Drawer extends BaseDrawer { const outlinePath = svgPaths.moveTo(connectionRight, yPos) + svgPaths.lineOnAxis('h', width) + input.shape.pathRightDown(input.height) + - svgPaths.lineOnAxis('h', -width) + input.shape.pathUp(input.height) + 'z'; + svgPaths.lineOnAxis('h', -width) + input.shape.pathUp(input.height) + + 'z'; this.block_.pathObject.setOutlinePath(inputName, outlinePath); } @@ -215,7 +216,8 @@ class Drawer extends BaseDrawer { 'h', -(input.notchOffset - this.constants_.INSIDE_CORNERS.width)) + this.constants_.INSIDE_CORNERS.pathTop; - const innerHeight = row.height - (2 * this.constants_.INSIDE_CORNERS.height); + const innerHeight = + row.height - (2 * this.constants_.INSIDE_CORNERS.height); const innerBottomLeftCorner = this.constants_.INSIDE_CORNERS.pathBottom + svgPaths.lineOnAxis( diff --git a/core/renderers/zelos/info.js b/core/renderers/zelos/info.js index 4ca0666acfc..58802a9090e 100644 --- a/core/renderers/zelos/info.js +++ b/core/renderers/zelos/info.js @@ -76,8 +76,8 @@ class RenderInfo extends BaseRenderInfo { this.isInline = true; /** - * Whether the block should be rendered as a multi-line block, either because - * it's not inline or because it has been collapsed. + * Whether the block should be rendered as a multi-line block, either + * because it's not inline or because it has been collapsed. * @type {boolean} */ this.isMultiRow = !block.getInputsInline() || block.isCollapsed(); @@ -92,8 +92,9 @@ class RenderInfo extends BaseRenderInfo { * An object with rendering information about the right connection shape. * @type {RightConnectionShape} */ - this.rightSide = - this.outputConnection ? new RightConnectionShape(this.constants_) : null; + this.rightSide = this.outputConnection ? + new RightConnectionShape(this.constants_) : + null; } /** @@ -260,9 +261,9 @@ class RenderInfo extends BaseRenderInfo { * @override */ addInput_(input, activeRow) { - // 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 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 && activeRow.align === Align.LEFT && input.align === Align.RIGHT) { activeRow.rightAlignedDummyInput = input; @@ -305,9 +306,9 @@ class RenderInfo extends BaseRenderInfo { } /** - * Adjust the x position of fields to bump all non-label fields in the first row - * past the notch position. This must be called before ``computeBounds`` is - * called. + * Adjust the x position of fields to bump all non-label fields in the first + * row past the notch position. This must be called before ``computeBounds`` + * is called. * @protected */ adjustXPosition_() { @@ -315,8 +316,8 @@ class RenderInfo extends BaseRenderInfo { this.constants_.NOTCH_OFFSET_LEFT + this.constants_.NOTCH_WIDTH; let minXPos = notchTotalWidth; // Run through every input row on the block and only apply bump logic to the - // first input row (if the block has prev connection) and every input row that - // has a prev and next notch. + // first input row (if the block has prev connection) and every input row + // that has a prev and next notch. for (let i = 2; i < this.rows.length - 1; i += 2) { const prevSpacer = this.rows[i - 1]; const row = this.rows[i]; @@ -497,7 +498,8 @@ class RenderInfo extends BaseRenderInfo { elem.shape.type; // Special case for value to stack / value to statement blocks. if (connectedBlock && connectedBlock.outputConnection && - (connectedBlock.statementInputCount || connectedBlock.nextConnection)) { + (connectedBlock.statementInputCount || + connectedBlock.nextConnection)) { return 0; } // Special case for hexagonal output. @@ -531,8 +533,8 @@ class RenderInfo extends BaseRenderInfo { if (this.outputConnection) { return; } - // Run through every input row on the block and only apply tight nesting logic - // to input rows that have a prev and next notch. + // Run through every input row on the block and only apply tight nesting + // logic to input rows that have a prev and next notch. for (let i = 2; i < this.rows.length - 1; i += 2) { const prevSpacer = this.rows[i - 1]; const row = this.rows[i]; diff --git a/core/renderers/zelos/marker_svg.js b/core/renderers/zelos/marker_svg.js index 6e84d6831d5..30019063ce2 100644 --- a/core/renderers/zelos/marker_svg.js +++ b/core/renderers/zelos/marker_svg.js @@ -93,7 +93,8 @@ class MarkerSvg extends BaseMarkerSvg { // 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. + // 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_(); diff --git a/core/renderers/zelos/path_object.js b/core/renderers/zelos/path_object.js index f3c79ec9509..84131e166a1 100644 --- a/core/renderers/zelos/path_object.js +++ b/core/renderers/zelos/path_object.js @@ -73,8 +73,8 @@ class PathObject extends BasePathObject { this.remainingOutlines_ = null; /** - * The type of block's output connection shape. This is set when a block with - * an output connection is drawn. + * The type of block's output connection shape. This is set when a block + * with an output connection is drawn. * @package */ this.outputShapeType = null; @@ -97,7 +97,8 @@ class PathObject extends BasePathObject { super.applyColour(block); // Set shadow stroke colour. if (block.isShadow() && block.getParent()) { - this.svgPath.setAttribute('stroke', block.getParent().style.colourTertiary); + this.svgPath.setAttribute( + 'stroke', block.getParent().style.colourTertiary); } // Apply colour to outlines. @@ -198,8 +199,8 @@ class PathObject extends BasePathObject { } /** - * Set the path generated by the renderer for an outline path on the respective - * outline path SVG element. + * Set the path generated by the renderer for an outline path on the + * respective outline path SVG element. * @param {string} name The input name. * @param {string} pathString The path. * @package diff --git a/core/renderers/zelos/renderer.js b/core/renderers/zelos/renderer.js index 75ff4e24cf6..d18962bb83e 100644 --- a/core/renderers/zelos/renderer.js +++ b/core/renderers/zelos/renderer.js @@ -125,8 +125,7 @@ class Renderer extends BaseRenderer { /** * @override */ - getConnectionPreviewMethod( - closest, local, topBlock) { + getConnectionPreviewMethod(closest, local, topBlock) { if (local.type === ConnectionType.OUTPUT_VALUE) { if (!closest.isConnected()) { return InsertionMarkerManager.PREVIEW_TYPE.INPUT_OUTLINE; @@ -139,8 +138,7 @@ class Renderer extends BaseRenderer { return InsertionMarkerManager.PREVIEW_TYPE.REPLACEMENT_FADE; } - return super.getConnectionPreviewMethod( - closest, local, topBlock); + return super.getConnectionPreviewMethod(closest, local, topBlock); } } diff --git a/scripts/gulpfiles/chunks.json b/scripts/gulpfiles/chunks.json index a7fcc3cbfa8..c3df5c6120a 100644 --- a/scripts/gulpfiles/chunks.json +++ b/scripts/gulpfiles/chunks.json @@ -52,8 +52,8 @@ "./core/renderers/geras/measurables/statement_input.js", "./core/renderers/geras/path_object.js", "./core/renderers/geras/renderer.js", - "./core/renderers/geras/measurables/inline_input.js", "./core/renderers/geras/info.js", + "./core/renderers/geras/measurables/inline_input.js", "./core/renderers/geras/highlight_constants.js", "./core/renderers/geras/highlighter.js", "./core/renderers/geras/drawer.js", diff --git a/tests/deps.js b/tests/deps.js index ab5c3206453..d244eaf4737 100644 --- a/tests/deps.js +++ b/tests/deps.js @@ -150,16 +150,16 @@ goog.addDependency('../../core/renderers/common/info.js', ['Blockly.blockRenderi goog.addDependency('../../core/renderers/common/marker_svg.js', ['Blockly.blockRendering.MarkerSvg'], ['Blockly.ASTNode', 'Blockly.ConnectionType', 'Blockly.Events.MarkerMove', 'Blockly.Events.utils', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/renderers/common/path_object.js', ['Blockly.blockRendering.PathObject'], ['Blockly.blockRendering.IPathObject', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/renderers/common/renderer.js', ['Blockly.blockRendering.Renderer'], ['Blockly.Connection', 'Blockly.ConnectionType', 'Blockly.IRegistrable', 'Blockly.InsertionMarkerManager', 'Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.Drawer', 'Blockly.blockRendering.MarkerSvg', 'Blockly.blockRendering.PathObject', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.debug', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/geras/constants.js', ['Blockly.geras.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/geras/drawer.js', ['Blockly.geras.Drawer'], ['Blockly.blockRendering.Drawer', 'Blockly.blockRendering.debug', 'Blockly.geras.Highlighter', 'Blockly.utils.object', 'Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/geras/constants.js', ['Blockly.geras.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/geras/drawer.js', ['Blockly.geras.Drawer'], ['Blockly.blockRendering.Drawer', 'Blockly.blockRendering.debug', 'Blockly.geras.Highlighter', 'Blockly.geras.InlineInput', 'Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/renderers/geras/geras.js', ['Blockly.geras'], ['Blockly.geras.ConstantProvider', 'Blockly.geras.Drawer', 'Blockly.geras.HighlightConstantProvider', 'Blockly.geras.Highlighter', 'Blockly.geras.InlineInput', 'Blockly.geras.PathObject', 'Blockly.geras.RenderInfo', 'Blockly.geras.Renderer', 'Blockly.geras.StatementInput'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/renderers/geras/highlight_constants.js', ['Blockly.geras.HighlightConstantProvider'], ['Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/geras/highlighter.js', ['Blockly.geras.Highlighter'], ['Blockly.blockRendering.Types', 'Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/geras/info.js', ['Blockly.geras.RenderInfo'], ['Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Types', 'Blockly.geras.InlineInput', 'Blockly.geras.StatementInput', 'Blockly.inputTypes', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/geras/measurables/inline_input.js', ['Blockly.geras.InlineInput'], ['Blockly.blockRendering.InlineInput', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/geras/measurables/statement_input.js', ['Blockly.geras.StatementInput'], ['Blockly.blockRendering.StatementInput', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/geras/highlighter.js', ['Blockly.geras.Highlighter'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.geras.InlineInput', 'Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/geras/info.js', ['Blockly.geras.RenderInfo'], ['Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Types', 'Blockly.geras.InlineInput', 'Blockly.geras.StatementInput', 'Blockly.inputTypes'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/geras/measurables/inline_input.js', ['Blockly.geras.InlineInput'], ['Blockly.blockRendering.InlineInput'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/geras/measurables/statement_input.js', ['Blockly.geras.StatementInput'], ['Blockly.blockRendering.StatementInput'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/renderers/geras/path_object.js', ['Blockly.geras.PathObject'], ['Blockly.blockRendering.PathObject', 'Blockly.utils.Svg', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/geras/renderer.js', ['Blockly.geras.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.geras.ConstantProvider', 'Blockly.geras.Drawer', 'Blockly.geras.HighlightConstantProvider', 'Blockly.geras.PathObject', 'Blockly.geras.RenderInfo', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/geras/renderer.js', ['Blockly.geras.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.geras.ConstantProvider', 'Blockly.geras.Drawer', 'Blockly.geras.HighlightConstantProvider', 'Blockly.geras.PathObject', 'Blockly.geras.RenderInfo'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/renderers/measurables/base.js', ['Blockly.blockRendering.Measurable'], ['Blockly.blockRendering.Types'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/renderers/measurables/bottom_row.js', ['Blockly.blockRendering.BottomRow'], ['Blockly.blockRendering.Row', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/renderers/measurables/connection.js', ['Blockly.blockRendering.Connection'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); @@ -182,24 +182,24 @@ goog.addDependency('../../core/renderers/measurables/square_corner.js', ['Blockl goog.addDependency('../../core/renderers/measurables/statement_input.js', ['Blockly.blockRendering.StatementInput'], ['Blockly.blockRendering.InputConnection', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/renderers/measurables/top_row.js', ['Blockly.blockRendering.TopRow'], ['Blockly.blockRendering.Row', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/renderers/measurables/types.js', ['Blockly.blockRendering.Types'], [], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/minimalist/constants.js', ['Blockly.minimalist.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/minimalist/drawer.js', ['Blockly.minimalist.Drawer'], ['Blockly.blockRendering.Drawer', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/minimalist/info.js', ['Blockly.minimalist.RenderInfo'], ['Blockly.blockRendering.RenderInfo', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/minimalist/constants.js', ['Blockly.minimalist.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/minimalist/drawer.js', ['Blockly.minimalist.Drawer'], ['Blockly.blockRendering.Drawer'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/minimalist/info.js', ['Blockly.minimalist.RenderInfo'], ['Blockly.blockRendering.RenderInfo'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/renderers/minimalist/minimalist.js', ['Blockly.minimalist'], ['Blockly.minimalist.ConstantProvider', 'Blockly.minimalist.Drawer', 'Blockly.minimalist.RenderInfo', 'Blockly.minimalist.Renderer'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/minimalist/renderer.js', ['Blockly.minimalist.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.minimalist.ConstantProvider', 'Blockly.minimalist.Drawer', 'Blockly.minimalist.RenderInfo', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/thrasos/info.js', ['Blockly.thrasos.RenderInfo'], ['Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/thrasos/renderer.js', ['Blockly.thrasos.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.thrasos.RenderInfo', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/minimalist/renderer.js', ['Blockly.minimalist.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.minimalist.ConstantProvider', 'Blockly.minimalist.Drawer', 'Blockly.minimalist.RenderInfo'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/thrasos/info.js', ['Blockly.thrasos.RenderInfo'], ['Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Types'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/thrasos/renderer.js', ['Blockly.thrasos.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.thrasos.RenderInfo'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/renderers/thrasos/thrasos.js', ['Blockly.thrasos'], ['Blockly.thrasos.RenderInfo', 'Blockly.thrasos.Renderer'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/zelos/constants.js', ['Blockly.zelos.ConstantProvider'], ['Blockly.ConnectionType', 'Blockly.blockRendering.ConstantProvider', 'Blockly.utils.Svg', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/zelos/drawer.js', ['Blockly.zelos.Drawer'], ['Blockly.blockRendering.Drawer', 'Blockly.blockRendering.debug', 'Blockly.utils.object', 'Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/zelos/info.js', ['Blockly.zelos.RenderInfo'], ['Blockly.FieldImage', 'Blockly.FieldLabel', 'Blockly.FieldTextInput', 'Blockly.Input', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Types', 'Blockly.inputTypes', 'Blockly.utils.object', 'Blockly.zelos.BottomRow', 'Blockly.zelos.RightConnectionShape', 'Blockly.zelos.StatementInput', 'Blockly.zelos.TopRow'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/zelos/marker_svg.js', ['Blockly.zelos.MarkerSvg'], ['Blockly.blockRendering.MarkerSvg', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/zelos/measurables/bottom_row.js', ['Blockly.zelos.BottomRow'], ['Blockly.blockRendering.BottomRow', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/zelos/measurables/inputs.js', ['Blockly.zelos.StatementInput'], ['Blockly.blockRendering.StatementInput', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/zelos/measurables/row_elements.js', ['Blockly.zelos.RightConnectionShape'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/zelos/measurables/top_row.js', ['Blockly.zelos.TopRow'], ['Blockly.blockRendering.TopRow', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/zelos/path_object.js', ['Blockly.zelos.PathObject'], ['Blockly.blockRendering.PathObject', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/renderers/zelos/renderer.js', ['Blockly.zelos.Renderer'], ['Blockly.ConnectionType', 'Blockly.InsertionMarkerManager', 'Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.utils.object', 'Blockly.zelos.ConstantProvider', 'Blockly.zelos.Drawer', 'Blockly.zelos.MarkerSvg', 'Blockly.zelos.PathObject', 'Blockly.zelos.RenderInfo'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/constants.js', ['Blockly.zelos.ConstantProvider'], ['Blockly.ConnectionType', 'Blockly.blockRendering.ConstantProvider', 'Blockly.utils.Svg', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/drawer.js', ['Blockly.zelos.Drawer'], ['Blockly.blockRendering.Drawer', 'Blockly.blockRendering.debug', 'Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/info.js', ['Blockly.zelos.RenderInfo'], ['Blockly.FieldImage', 'Blockly.FieldLabel', 'Blockly.FieldTextInput', 'Blockly.Input', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Types', 'Blockly.inputTypes', 'Blockly.zelos.BottomRow', 'Blockly.zelos.RightConnectionShape', 'Blockly.zelos.StatementInput', 'Blockly.zelos.TopRow'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/marker_svg.js', ['Blockly.zelos.MarkerSvg'], ['Blockly.blockRendering.MarkerSvg', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/measurables/bottom_row.js', ['Blockly.zelos.BottomRow'], ['Blockly.blockRendering.BottomRow'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/measurables/inputs.js', ['Blockly.zelos.StatementInput'], ['Blockly.blockRendering.StatementInput'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/measurables/row_elements.js', ['Blockly.zelos.RightConnectionShape'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/measurables/top_row.js', ['Blockly.zelos.TopRow'], ['Blockly.blockRendering.TopRow'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/path_object.js', ['Blockly.zelos.PathObject'], ['Blockly.blockRendering.PathObject', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/renderer.js', ['Blockly.zelos.Renderer'], ['Blockly.ConnectionType', 'Blockly.InsertionMarkerManager', 'Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.zelos.ConstantProvider', 'Blockly.zelos.Drawer', 'Blockly.zelos.MarkerSvg', 'Blockly.zelos.PathObject', 'Blockly.zelos.RenderInfo'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/renderers/zelos/zelos.js', ['Blockly.zelos'], ['Blockly.zelos.BottomRow', 'Blockly.zelos.ConstantProvider', 'Blockly.zelos.Drawer', 'Blockly.zelos.MarkerSvg', 'Blockly.zelos.PathObject', 'Blockly.zelos.RenderInfo', 'Blockly.zelos.Renderer', 'Blockly.zelos.RightConnectionShape', 'Blockly.zelos.StatementInput', 'Blockly.zelos.TopRow'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/scrollbar.js', ['Blockly.Scrollbar'], ['Blockly.Touch', 'Blockly.browserEvents', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.svgMath'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/scrollbar_pair.js', ['Blockly.ScrollbarPair'], ['Blockly.Events.utils', 'Blockly.Scrollbar', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); From 0848d01d1b29530b2463e47b14fc22373210a6b0 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 14 Jan 2022 12:39:56 -0800 Subject: [PATCH 7/7] chore: fix indentation in comments --- core/renderers/geras/renderer.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/core/renderers/geras/renderer.js b/core/renderers/geras/renderer.js index a4b8ef0cb96..e71789c01c4 100644 --- a/core/renderers/geras/renderer.js +++ b/core/renderers/geras/renderer.js @@ -94,8 +94,8 @@ class Renderer extends BaseRenderer { /** * Create a new instance of the renderer's drawer. * @param {!BlockSvg} block The block to render. - * @param {!BaseRenderInfo} info An object containing all - * information needed to render this block. + * @param {!BaseRenderInfo} info An object containing all information needed + * to render this block. * @return {!Drawer} The drawer. * @protected * @override @@ -109,8 +109,7 @@ class Renderer extends BaseRenderer { /** * Create a new instance of a renderer path object. * @param {!SVGElement} root The root SVG element. - * @param {!Theme.BlockStyle} style The style object to use for - * colouring. + * @param {!Theme.BlockStyle} style The style object to use for colouring. * @return {!PathObject} The renderer path object. * @package * @override @@ -123,8 +122,7 @@ class Renderer extends BaseRenderer { /** * Create a new instance of the renderer's highlight constant provider. - * @return {!HighlightConstantProvider} The highlight constant - * provider. + * @return {!HighlightConstantProvider} The highlight constant provider. * @protected */ makeHighlightConstants_() { @@ -136,8 +134,7 @@ class Renderer extends BaseRenderer { /** * Get the renderer's highlight constant provider. We assume that when this * is called, the renderer has already been initialized. - * @return {!HighlightConstantProvider} The highlight constant - * provider. + * @return {!HighlightConstantProvider} The highlight constant provider. * @package */ getHighlightConstants() {