From 95aadbffb1f0f098877e0e31d4ae4a0e7d60f5ea Mon Sep 17 00:00:00 2001 From: Sam El-Husseini Date: Fri, 7 Feb 2020 15:13:51 -0800 Subject: [PATCH] [zelos] Set a maximum width for dynamic connection shapes. (#3685) * Set a maximum width for dynamic connection shapes. * Only render the left side connection if a block has a statement input --- core/renderers/common/info.js | 10 +++--- core/renderers/zelos/constants.js | 41 ++++++++++++++++++------ core/renderers/zelos/drawer.js | 15 ++++++++- core/renderers/zelos/info.js | 31 ++++++++++++------ core/renderers/zelos/measurables/rows.js | 8 ++--- 5 files changed, 76 insertions(+), 29 deletions(-) diff --git a/core/renderers/common/info.js b/core/renderers/common/info.js index 0e20b9b24..f034da0ce 100644 --- a/core/renderers/common/info.js +++ b/core/renderers/common/info.js @@ -139,11 +139,10 @@ Blockly.blockRendering.RenderInfo = function(renderer, block) { this.rows = []; /** - * The total number of input rows added onto the block. - * @type {number} - * @protected + * An array of input rows on the block. + * @type {!Array.} */ - this.inputRowNum_ = 1; + this.inputRows = []; /** * An array of measurable objects containing hidden icons. @@ -206,6 +205,7 @@ Blockly.blockRendering.RenderInfo.prototype.createRows_ = function() { this.populateTopRow_(); this.rows.push(this.topRow); var activeRow = new Blockly.blockRendering.InputRow(this.constants_); + this.inputRows.push(activeRow); // Icons always go on the first row, before anything else. var icons = this.block_.getIcons(); @@ -231,7 +231,7 @@ Blockly.blockRendering.RenderInfo.prototype.createRows_ = function() { // Finish this row and create a new one. this.rows.push(activeRow); activeRow = new Blockly.blockRendering.InputRow(this.constants_); - this.inputRowNum_ ++; + this.inputRows.push(activeRow); } // All of the fields in an input go on the same row. diff --git a/core/renderers/zelos/constants.js b/core/renderers/zelos/constants.js index 72458852e..d3c485286 100644 --- a/core/renderers/zelos/constants.js +++ b/core/renderers/zelos/constants.js @@ -358,6 +358,13 @@ Blockly.zelos.ConstantProvider = function() { */ this.FIELD_CHECKBOX_DEFAULT_WIDTH = 6 * this.GRID_UNIT; + + /** + * The maximum width of a dynamic connection shape. + * @type {number} + */ + this.MAX_DYNAMIC_CONNECTION_SHAPE_WIDTH = 12 * this.GRID_UNIT; + /** * The ID of the selected glow filter, or the empty string if no filter is * set. @@ -441,13 +448,16 @@ Blockly.zelos.ConstantProvider.prototype.makeStartHat = function() { * @package */ Blockly.zelos.ConstantProvider.prototype.makeHexagonal = function() { + var maxWidth = this.MAX_DYNAMIC_CONNECTION_SHAPE_WIDTH; + // The main path for the hexagonal connection shape is made out of two lines. // The lines are defined with relative positons 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. function makeMainPath(height, up, right) { - var width = height / 2; + var halfHeight = height / 2; + var width = halfHeight > maxWidth ? maxWidth : halfHeight; var forward = up ? -1 : 1; var direction = right ? -1 : 1; var dy = forward * height / 2; @@ -459,7 +469,8 @@ Blockly.zelos.ConstantProvider.prototype.makeHexagonal = function() { type: this.SHAPES.HEXAGONAL, isDynamic: true, width: function(height) { - return height / 2; + var halfHeight = height / 2; + return halfHeight > maxWidth ? maxWidth : halfHeight; }, height: function(height) { return height; @@ -492,22 +503,34 @@ Blockly.zelos.ConstantProvider.prototype.makeHexagonal = function() { * @package */ Blockly.zelos.ConstantProvider.prototype.makeRounded = function() { - // The main path for the rounded connection shape is made out of a single arc. - // The arc is defined with relative positions and requires the block height. + var maxWidth = this.MAX_DYNAMIC_CONNECTION_SHAPE_WIDTH; + var maxHeight = maxWidth * 2; + + // The main path for the rounded connection shape is made 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. - function makeMainPath(height, up, right) { - var edgeWidth = height / 2; - return Blockly.utils.svgPaths.arc('a', '0 0 ' + (up || right ? 1 : 0), edgeWidth, - Blockly.utils.svgPaths.point(0, (up ? -1 : 1) * edgeWidth * 2)); + function makeMainPath(blockHeight, up, right) { + var remainingHeight = blockHeight > maxHeight ? blockHeight - maxHeight : 0; + var height = blockHeight > maxHeight ? maxHeight : blockHeight; + var radius = height / 2; + return Blockly.utils.svgPaths.arc('a', '0 0,1', radius, + Blockly.utils.svgPaths.point((up ? -1 : 1) * radius, (up ? -1 : 1) * radius)) + + Blockly.utils.svgPaths.lineOnAxis('v', (right ? 1 : -1) * remainingHeight) + + Blockly.utils.svgPaths.arc('a', '0 0,1', radius, + Blockly.utils.svgPaths.point((up ? 1 : -1) * radius, (up ? -1 : 1) * radius)); } return { type: this.SHAPES.ROUND, isDynamic: true, width: function(height) { - return height / 2; + var halfHeight = height / 2; + return halfHeight > maxWidth ? maxWidth : halfHeight; }, height: function(height) { return height; diff --git a/core/renderers/zelos/drawer.js b/core/renderers/zelos/drawer.js index 22580d233..7377595f4 100644 --- a/core/renderers/zelos/drawer.js +++ b/core/renderers/zelos/drawer.js @@ -80,7 +80,8 @@ Blockly.zelos.Drawer.prototype.draw = function() { */ Blockly.zelos.Drawer.prototype.drawOutline_ = function() { if (this.info_.outputConnection && - this.info_.outputConnection.isDynamicShape) { + this.info_.outputConnection.isDynamicShape && + !this.info_.hasStatementInput) { this.drawFlatTop_(); this.drawRightDynamicConnection_(); this.drawFlatBottom_(); @@ -90,6 +91,18 @@ Blockly.zelos.Drawer.prototype.drawOutline_ = function() { } }; +/** + * @override + */ +Blockly.zelos.Drawer.prototype.drawLeft_ = function() { + if (this.info_.outputConnection && + this.info_.outputConnection.isDynamicShape) { + this.drawLeftDynamicConnection_(); + } else { + Blockly.zelos.Drawer.superClass_.drawLeft_.call(this); + } +}; + /** * Add steps for the right side of a row that does not have value or * statement input connections. diff --git a/core/renderers/zelos/info.js b/core/renderers/zelos/info.js index c80c6b5f3..90f606c6c 100644 --- a/core/renderers/zelos/info.js +++ b/core/renderers/zelos/info.js @@ -89,6 +89,12 @@ Blockly.zelos.RenderInfo = function(renderer, block) { */ this.isMultiRow = !block.getInputsInline() || block.isCollapsed(); + /** + * Whether or not the block has a statement input in one of its rows. + * @type {boolean} + */ + this.hasStatementInput = false; + /** * An object with rendering information about the right connection shape. * @type {Blockly.zelos.RightConnectionShape} @@ -164,7 +170,8 @@ Blockly.zelos.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) { + if (this.outputConnection && this.outputConnection.isDynamicShape && + !this.hasStatementInput) { return this.constants_.NO_PADDING; } } @@ -270,6 +277,7 @@ Blockly.zelos.RenderInfo.prototype.addInput_ = function(input, activeRow) { activeRow.elements.push( new Blockly.zelos.StatementInput(this.constants_, input)); activeRow.hasStatement = true; + this.hasStatementInput = true; } else if (input.type == Blockly.INPUT_VALUE) { activeRow.elements.push( new Blockly.blockRendering.ExternalValueInput(this.constants_, input)); @@ -397,10 +405,12 @@ Blockly.zelos.RenderInfo.prototype.finalizeOutputConnection_ = function() { this.outputConnection.shape.connectionOffsetX(connectionWidth); // Adjust right side measurable. - this.rightSide.height = connectionHeight; - this.rightSide.width = connectionWidth; - this.rightSide.centerline = connectionHeight / 2; - this.rightSide.xPos = this.width + connectionWidth; + if (!this.hasStatementInput) { + this.rightSide.height = connectionHeight; + this.rightSide.width = connectionWidth; + this.rightSide.centerline = connectionHeight / 2; + this.rightSide.xPos = this.width + connectionWidth; + } this.startX = connectionWidth; this.width += connectionWidth * 2; @@ -415,7 +425,7 @@ Blockly.zelos.RenderInfo.prototype.finalizeOutputConnection_ = function() { * @protected */ Blockly.zelos.RenderInfo.prototype.finalizeHorizontalAlignment_ = function() { - if (!this.outputConnection) { + if (!this.outputConnection || this.hasStatementInput) { return; } var totalNegativeSpacing = 0; @@ -474,14 +484,15 @@ Blockly.zelos.RenderInfo.prototype.getNegativeSpacing_ = function(elem) { var outerShape = this.outputConnection.shape.type; var constants = /** @type {!Blockly.zelos.ConstantProvider} */ (this.constants_); - if (this.isMultiRow && this.inputRowNum_ > 1) { + if (this.isMultiRow && this.inputRows.length > 1) { switch (outerShape) { case constants.SHAPES.ROUND: // Special case for multi-row round reporter blocks. - var radius = this.height / 2; + var maxWidth = this.constants_.MAX_DYNAMIC_CONNECTION_SHAPE_WIDTH; + var width = this.height / 2 > maxWidth ? maxWidth : this.height / 2; var topPadding = this.constants_.SMALL_PADDING; - var roundPadding = radius * - (1 - Math.sin(Math.acos((radius - topPadding) / radius))); + var roundPadding = width * + (1 - Math.sin(Math.acos((width - topPadding) / width))); return connectionWidth - roundPadding; default: return 0; diff --git a/core/renderers/zelos/measurables/rows.js b/core/renderers/zelos/measurables/rows.js index 5b025eda2..a42703ee1 100644 --- a/core/renderers/zelos/measurables/rows.js +++ b/core/renderers/zelos/measurables/rows.js @@ -72,8 +72,8 @@ Blockly.zelos.TopRow.prototype.hasLeftSquareCorner = function(block) { * Render a round corner unless the block has an output connection. * @override */ -Blockly.zelos.TopRow.prototype.hasRightSquareCorner = function(block) { - return !!block.outputConnection; +Blockly.zelos.TopRow.prototype.hasRightSquareCorner = function(_block) { + return false; }; /** @@ -112,6 +112,6 @@ Blockly.zelos.BottomRow.prototype.hasLeftSquareCorner = function(block) { * Render a round corner unless the block has an output connection. * @override */ -Blockly.zelos.BottomRow.prototype.hasRightSquareCorner = function(block) { - return !!block.outputConnection; +Blockly.zelos.BottomRow.prototype.hasRightSquareCorner = function(_block) { + return false; };