diff --git a/blockly_uncompressed.js b/blockly_uncompressed.js index 77e42cafa..07f48f5a4 100644 --- a/blockly_uncompressed.js +++ b/blockly_uncompressed.js @@ -99,14 +99,15 @@ goog.addDependency("../../../" + dir + "/core/names.js", ['Blockly.Names'], []); goog.addDependency("../../../" + dir + "/core/options.js", ['Blockly.Options'], ['Blockly.Themes.Classic', 'Blockly.utils.userAgent', 'Blockly.Xml']); goog.addDependency("../../../" + dir + "/core/procedures.js", ['Blockly.Procedures'], ['Blockly.Blocks', 'Blockly.constants', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.Msg', 'Blockly.Names', 'Blockly.utils.xml', 'Blockly.Workspace', 'Blockly.Xml']); goog.addDependency("../../../" + dir + "/core/rendered_connection.js", ['Blockly.RenderedConnection'], ['Blockly.Connection', 'Blockly.Events', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom']); -goog.addDependency("../../../" + dir + "/core/renderers/block_rendering_rewrite/block_render_draw.js", ['Blockly.blockRendering.Drawer'], ['Blockly.blockRendering.constants', 'Blockly.blockRendering.Debug', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Highlighter', 'Blockly.blockRendering.Measurable']); -goog.addDependency("../../../" + dir + "/core/renderers/block_rendering_rewrite/block_render_draw_debug.js", ['Blockly.blockRendering.Debug'], ['Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Highlighter', 'Blockly.blockRendering.constants', 'Blockly.blockRendering.Measurable']); -goog.addDependency("../../../" + dir + "/core/renderers/block_rendering_rewrite/block_render_draw_highlight.js", ['Blockly.blockRendering.Highlighter'], ['Blockly.blockRendering.highlightConstants', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Measurable']); -goog.addDependency("../../../" + dir + "/core/renderers/block_rendering_rewrite/block_render_info.js", ['Blockly.blockRendering.RenderInfo'], ['Blockly.blockRendering.constants', 'Blockly.blockRendering.Measurable']); +goog.addDependency("../../../" + dir + "/core/renderers/block_rendering_rewrite/block_render_draw.js", ['Blockly.blockRendering.Drawer'], ['Blockly.blockRendering.constants', 'Blockly.blockRendering.Debug', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Highlighter', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Rows']); +goog.addDependency("../../../" + dir + "/core/renderers/block_rendering_rewrite/block_render_draw_debug.js", ['Blockly.blockRendering.Debug'], ['Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Highlighter', 'Blockly.blockRendering.constants', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Rows']); +goog.addDependency("../../../" + dir + "/core/renderers/block_rendering_rewrite/block_render_draw_highlight.js", ['Blockly.blockRendering.Highlighter'], ['Blockly.blockRendering.highlightConstants', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Rows']); +goog.addDependency("../../../" + dir + "/core/renderers/block_rendering_rewrite/block_render_info.js", ['Blockly.blockRendering.RenderInfo'], ['Blockly.blockRendering.constants', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Rows']); goog.addDependency("../../../" + dir + "/core/renderers/block_rendering_rewrite/block_rendering.js", ['Blockly.blockRendering'], ['Blockly.blockRendering.Debug', 'Blockly.blockRendering.Drawer', 'Blockly.blockRendering.RenderInfo']); goog.addDependency("../../../" + dir + "/core/renderers/block_rendering_rewrite/block_rendering_constants.js", ['Blockly.blockRendering.constants'], ['Blockly.utils.svgPaths']); goog.addDependency("../../../" + dir + "/core/renderers/block_rendering_rewrite/highlight_constants.js", ['Blockly.blockRendering.highlightConstants'], ['Blockly.blockRendering.constants', 'Blockly.utils.svgPaths']); -goog.addDependency("../../../" + dir + "/core/renderers/block_rendering_rewrite/measurables.js", ['Blockly.blockRendering.Measurable'], ['Blockly.blockRendering.constants']); +goog.addDependency("../../../" + dir + "/core/renderers/block_rendering_rewrite/measurables.js", ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Input', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.PreviousConnection'], ['Blockly.blockRendering.constants']); +goog.addDependency("../../../" + dir + "/core/renderers/measurables/rows.js", ['Blockly.blockRendering.Rows'], ['Blockly.blockRendering.constants', 'Blockly.blockRendering.Input', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.RenderedConnection']); goog.addDependency("../../../" + dir + "/core/scrollbar.js", ['Blockly.Scrollbar', 'Blockly.ScrollbarPair'], ['Blockly.Touch', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom']); goog.addDependency("../../../" + dir + "/core/theme.js", ['Blockly.Theme'], []); goog.addDependency("../../../" + dir + "/core/theme/classic.js", ['Blockly.Themes.Classic'], ['Blockly.Theme']); @@ -1888,8 +1889,13 @@ goog.require('Blockly.blockRendering'); goog.require('Blockly.blockRendering.Debug'); goog.require('Blockly.blockRendering.Drawer'); goog.require('Blockly.blockRendering.Highlighter'); +goog.require('Blockly.blockRendering.InRowSpacer'); +goog.require('Blockly.blockRendering.Input'); goog.require('Blockly.blockRendering.Measurable'); +goog.require('Blockly.blockRendering.NextConnection'); +goog.require('Blockly.blockRendering.PreviousConnection'); goog.require('Blockly.blockRendering.RenderInfo'); +goog.require('Blockly.blockRendering.Rows'); goog.require('Blockly.blockRendering.constants'); goog.require('Blockly.blockRendering.highlightConstants'); goog.require('Blockly.constants'); diff --git a/core/renderers/block_rendering_rewrite/block_render_draw.js b/core/renderers/block_rendering_rewrite/block_render_draw.js index e38e4e6fb..dd241aace 100644 --- a/core/renderers/block_rendering_rewrite/block_render_draw.js +++ b/core/renderers/block_rendering_rewrite/block_render_draw.js @@ -31,6 +31,7 @@ goog.require('Blockly.blockRendering.Debug'); goog.require('Blockly.blockRendering.RenderInfo'); goog.require('Blockly.blockRendering.Highlighter'); goog.require('Blockly.blockRendering.Measurable'); +goog.require('Blockly.blockRendering.Rows'); /** * An object that draws a block based on the given rendering information. diff --git a/core/renderers/block_rendering_rewrite/block_render_draw_debug.js b/core/renderers/block_rendering_rewrite/block_render_draw_debug.js index 1bb6d80e7..6584995d1 100644 --- a/core/renderers/block_rendering_rewrite/block_render_draw_debug.js +++ b/core/renderers/block_rendering_rewrite/block_render_draw_debug.js @@ -29,6 +29,7 @@ goog.require('Blockly.blockRendering.RenderInfo'); goog.require('Blockly.blockRendering.Highlighter'); goog.require('Blockly.blockRendering.constants'); goog.require('Blockly.blockRendering.Measurable'); +goog.require('Blockly.blockRendering.Rows'); /** * An object that renders rectangles and dots for debugging rendering code. diff --git a/core/renderers/block_rendering_rewrite/block_render_draw_highlight.js b/core/renderers/block_rendering_rewrite/block_render_draw_highlight.js index 289aa1fa7..e9cd66a4e 100644 --- a/core/renderers/block_rendering_rewrite/block_render_draw_highlight.js +++ b/core/renderers/block_rendering_rewrite/block_render_draw_highlight.js @@ -29,6 +29,7 @@ goog.provide('Blockly.blockRendering.Highlighter'); goog.require('Blockly.blockRendering.highlightConstants'); goog.require('Blockly.blockRendering.RenderInfo'); goog.require('Blockly.blockRendering.Measurable'); +goog.require('Blockly.blockRendering.Rows'); /** * An object that adds highlights to a block based on the given rendering diff --git a/core/renderers/block_rendering_rewrite/block_render_info.js b/core/renderers/block_rendering_rewrite/block_render_info.js index 343b754e6..1b233fe12 100644 --- a/core/renderers/block_rendering_rewrite/block_render_info.js +++ b/core/renderers/block_rendering_rewrite/block_render_info.js @@ -29,6 +29,7 @@ goog.provide('Blockly.blockRendering.RenderInfo'); goog.require('Blockly.blockRendering.constants'); goog.require('Blockly.blockRendering.Measurable'); +goog.require('Blockly.blockRendering.Rows'); /** * An object containing all sizing information needed to draw this block. diff --git a/core/renderers/block_rendering_rewrite/measurables.js b/core/renderers/block_rendering_rewrite/measurables.js index dd4014438..1b5064a98 100644 --- a/core/renderers/block_rendering_rewrite/measurables.js +++ b/core/renderers/block_rendering_rewrite/measurables.js @@ -1,4 +1,8 @@ goog.provide('Blockly.blockRendering.Measurable'); +goog.provide('Blockly.blockRendering.Input'); +goog.provide('Blockly.blockRendering.InRowSpacer'); +goog.provide('Blockly.blockRendering.NextConnection'); +goog.provide('Blockly.blockRendering.PreviousConnection'); goog.require('Blockly.blockRendering.constants'); @@ -432,78 +436,14 @@ Blockly.blockRendering.RoundCorner = function() { }; goog.inherits(Blockly.blockRendering.RoundCorner, Blockly.blockRendering.Measurable); - -Blockly.blockRendering.Row = function() { - this.type = 'row'; - this.yPos = 0; - this.elements = []; - this.width = 0; - this.height = 0; - this.widthWithConnectedBlocks = 0; - - this.hasExternalInput = false; - this.hasStatement = false; - this.hasInlineInput = false; - this.hasDummyInput = false; - this.hasJaggedEdge = false; -}; - /** - * The shape object to use when drawing previous and next connections. - * TODO (#2803): Formalize type annotations for these objects. - * @type {Object} + * An object containing information about a spacer between two rows. + * @param {number} height The height of the spacer. + * @param {number} width The width of the spacer. + * @package + * @constructor + * @extends {Blockly.blockRendering.Measurable} */ -Blockly.blockRendering.Row.prototype.notchShape = - Blockly.blockRendering.constants.NOTCH; - -Blockly.blockRendering.Row.prototype.isSpacer = function() { - return false; -}; - -Blockly.blockRendering.Row.prototype.measure = function() { - var connectedBlockWidths = 0; - for (var e = 0; e < this.elements.length; e++) { - var elem = this.elements[e]; - this.width += elem.width; - if (elem.isInput) { - if (elem.type == 'statement input') { - connectedBlockWidths += elem.connectedBlockWidth; - } else if (elem.type == 'external value input' && - elem.connectedBlockWidth != 0) { - connectedBlockWidths += (elem.connectedBlockWidth - elem.connectionWidth); - } - } - if (!(elem.isSpacer())) { - this.height = Math.max(this.height, elem.height); - } - } - this.widthWithConnectedBlocks = this.width + connectedBlockWidths; -}; - -Blockly.blockRendering.Row.prototype.getLastInput = function() { - for (var i = this.elements.length - 1; i >= 0; i--) { - var elem = this.elements[i]; - if (elem.isSpacer()) { - continue; - } - if (elem.isInput) { - return elem; - } else if (elem.isField()) { - return elem.parentInput; - } - } - // Return null if there are no inputs. - return null; -}; - -Blockly.blockRendering.Row.prototype.getFirstSpacer = function() { - return this.elements[0]; -}; - -Blockly.blockRendering.Row.prototype.getLastSpacer = function() { - return this.elements[this.elements.length - 1]; -}; - Blockly.blockRendering.BetweenRowSpacer = function(height, width) { this.type = 'between-row spacer'; this.width = width; @@ -514,6 +454,14 @@ Blockly.blockRendering.BetweenRowSpacer = function(height, width) { goog.inherits(Blockly.blockRendering.BetweenRowSpacer, Blockly.blockRendering.Measurable); +/** + * An object containing information about a spacer between two elements on a + * row. + * @param {number} width The width of the spacer. + * @package + * @constructor + * @extends {Blockly.blockRendering.Measurable} + */ Blockly.blockRendering.InRowSpacer = function(width) { this.type = 'in-row spacer'; this.width = width; @@ -522,100 +470,3 @@ Blockly.blockRendering.InRowSpacer = function(width) { goog.inherits(Blockly.blockRendering.InRowSpacer, Blockly.blockRendering.Measurable); -/** - * An object containing information about what elements are in the top row of a - * block as well as spacing information for the top row. - * Elements in a top row can consist of corners, hats and previous connections. - * @param {[type]} block [description] - * @package - */ -Blockly.blockRendering.TopRow = function(block) { - Blockly.blockRendering.TopRow.superClass_.constructor.call(this); - - this.elements = []; - this.type = 'top row'; - this.startY = 0; - - this.hasPreviousConnection = !!block.previousConnection; - this.connection = block.previousConnection; - - var precedesStatement = block.inputList.length && - block.inputList[0].type == Blockly.NEXT_STATEMENT; - - // This is the minimum height for the row. If one of its elements has a greater - // height it will be overwritten in the compute pass. - if (precedesStatement && !block.isCollapsed()) { - this.height = Blockly.blockRendering.constants.LARGE_PADDING; - } else { - this.height = Blockly.blockRendering.constants.MEDIUM_PADDING; - } -}; -goog.inherits(Blockly.blockRendering.TopRow, Blockly.blockRendering.Row); - - -Blockly.blockRendering.TopRow.prototype.getPreviousConnection = function() { - if (this.hasPreviousConnection) { - return this.elements[2]; - } - return null; -}; - -Blockly.blockRendering.TopRow.prototype.measure = function() { - for (var e = 0; e < this.elements.length; e++) { - var elem = this.elements[e]; - this.width += elem.width; - if (!(elem.isSpacer())) { - if (elem.type == 'hat') { - this.startY = elem.startY; - this.height = this.height + elem.height; - } - this.height = Math.max(this.height, elem.height); - } - } - this.widthWithConnectedBlocks = this.width; -}; - -Blockly.blockRendering.BottomRow = function(block) { - Blockly.blockRendering.BottomRow.superClass_.constructor.call(this); - this.type = 'bottom row'; - this.hasNextConnection = !!block.nextConnection; - this.connection = block.nextConnection; - this.overhangY = 0; - - var followsStatement = - block.inputList.length && - block.inputList[block.inputList.length - 1].type == Blockly.NEXT_STATEMENT; - this.hasFixedWidth = followsStatement && block.getInputsInline(); - - // This is the minimum height for the row. If one of its elements has a greater - // height it will be overwritten in the compute pass. - if (followsStatement) { - this.height = Blockly.blockRendering.constants.LARGE_PADDING; - } else { - this.height = this.notchShape.height; - } -}; -goog.inherits(Blockly.blockRendering.BottomRow, - Blockly.blockRendering.Row); - -Blockly.blockRendering.BottomRow.prototype.getNextConnection = function() { - if (this.hasNextConnection) { - return this.elements[2]; - } - return null; -}; - -Blockly.blockRendering.BottomRow.prototype.measure = function() { - for (var e = 0; e < this.elements.length; e++) { - var elem = this.elements[e]; - this.width += elem.width; - if (!(elem.isSpacer())) { - if (elem.type == 'next connection') { - this.height = this.height + elem.height; - this.overhangY = elem.height; - } - this.height = Math.max(this.height, elem.height); - } - } - this.widthWithConnectedBlocks = this.width; -}; diff --git a/core/renderers/measurables/rows.js b/core/renderers/measurables/rows.js new file mode 100644 index 000000000..01e54be1c --- /dev/null +++ b/core/renderers/measurables/rows.js @@ -0,0 +1,389 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2019 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview An object representing a single row on a rendered block and all + * of its subcomponents. + * @author fenichel@google.com (Rachel Fenichel) + */ + +goog.provide('Blockly.blockRendering.Rows'); + +goog.require('Blockly.blockRendering.constants'); +goog.require('Blockly.blockRendering.Input'); +goog.require('Blockly.blockRendering.InRowSpacer'); +goog.require('Blockly.blockRendering.Measurable'); +goog.require('Blockly.blockRendering.NextConnection'); +goog.require('Blockly.blockRendering.PreviousConnection'); +goog.require('Blockly.RenderedConnection'); + +/** + * An object representing a single row on a rendered block and all of its + * subcomponents. + * @package + * @constructor + */ +Blockly.blockRendering.Row = function() { + /** + * The type of this rendering object. + * @package + * @type {string} + */ + this.type = 'row'; + + /** + * An array of elements contained in this row. + * @package + * @type {!Array.} + */ + this.elements = []; + + // TODO: Should row extend measurable? Some of these properties are shared. + /** + * The height of the row. + * @package + * @type {number} + */ + this.height = 0; + + /** + * The width of the row, from the left edge of the block to the right. + * Does not include child blocks unless they are inline. + * @package + * @type {number} + */ + this.width = 0; + + /** + * The width of the row, from the left edge of the block to the edge of the + * block or any connected child blocks. + * @package + * @type {number} + */ + this.widthWithConnectedBlocks = 0; + + /** + * The Y position of the row relative to the origin of the block's svg group. + * @package + * @type {number} + */ + this.yPos = 0; + + /** + * The X position of the row relative to the origin of the block's svg group. + * @package + * @type {number} + */ + this.xPos = 0; + + /** + * Whether the row has any external inputs. + * @package + * @type {boolean} + */ + this.hasExternalInput = false; + + /** + * Whether the row has any statement inputs. + * @package + * @type {boolean} + */ + this.hasStatement = false; + + /** + * Whether the row has any inline inputs. + * @package + * @type {boolean} + */ + this.hasInlineInput = false; + + /** + * Whether the row has any dummy inputs. + * @package + * @type {boolean} + */ + this.hasDummyInput = false; + + /** + * Whether the row has a jagged edge. + * @package + * @type {boolean} + */ + this.hasJaggedEdge = false; +}; + +/** + * The shape object to use when drawing previous and next connections. + * TODO (#2803): Formalize type annotations for these objects. + * @type {Object} + */ +Blockly.blockRendering.Row.prototype.notchShape = + Blockly.blockRendering.constants.NOTCH; + +/** + * Whether this is a spacer row. + * @return {boolean} Always false. + * @package + */ +Blockly.blockRendering.Row.prototype.isSpacer = function() { + return false; +}; + +/** + * Inspect all subcomponents and populate all size properties on the row. + * @package + */ +Blockly.blockRendering.Row.prototype.measure = function() { + var connectedBlockWidths = 0; + for (var e = 0; e < this.elements.length; e++) { + var elem = this.elements[e]; + this.width += elem.width; + if (elem.isInput) { + if (elem.type == 'statement input') { + connectedBlockWidths += elem.connectedBlockWidth; + } else if (elem.type == 'external value input' && + elem.connectedBlockWidth != 0) { + connectedBlockWidths += (elem.connectedBlockWidth - elem.connectionWidth); + } + } + if (!(elem.isSpacer())) { + this.height = Math.max(this.height, elem.height); + } + } + this.widthWithConnectedBlocks = this.width + connectedBlockWidths; +}; + +/** + * Get the last input on this row, if it has one. + * @return {Blockly.blockRendering.Input} The last input on the row, or null. + * @package + */ +Blockly.blockRendering.Row.prototype.getLastInput = function() { + for (var i = this.elements.length - 1; i >= 0; i--) { + var elem = this.elements[i]; + if (elem.isSpacer()) { + continue; + } + if (elem.isInput) { + return /** @type {Blockly.blockRendering.Input} */ (elem); + } else if (elem.isField()) { + return /** @type {Blockly.blockRendering.Input} */ (elem.parentInput); + } + } + return null; +}; + +/** + * Convenience method to get the first spacer element on this row. + * TODO: I want to say that this is guaranteed to exist, but I'm not sure how. + * I could check if it's null and throw, but that seems like overkill. + * @return {Blockly.blockRendering.InRowSpacer} The first spacer element on + * this row. + * @package + */ +Blockly.blockRendering.Row.prototype.getFirstSpacer = function() { + return /** @type {Blockly.blockRendering.InRowSpacer} */ (this.elements[0]); +}; + +/** + * Convenience method to get the last spacer element on this row. + * TODO: I want to say that this is guaranteed to exist, but I'm not sure how. + * I could check if it's null and throw, but that seems like overkill. + * @return {Blockly.blockRendering.InRowSpacer} The last spacer element on + * this row. + * @package + */ +Blockly.blockRendering.Row.prototype.getLastSpacer = function() { + /* eslint-disable-next-line max-len */ + return /** @type {Blockly.blockRendering.InRowSpacer} */ (this.elements[this.elements.length - 1]); +}; + +/** + * An object containing information about what elements are in the top row of a + * block as well as sizing information for the top row. + * Elements in a top row can consist of corners, hats, spacers, and previous + * connections. + * @param {!Blockly.BlockSvg} block The block for which this represents the top + * row. + * @package + * @constructor + * @extends {Blockly.blockRendering.Row} + */ +Blockly.blockRendering.TopRow = function(block) { + Blockly.blockRendering.TopRow.superClass_.constructor.call(this); + + this.type = 'top row'; + + /** + * The starting point for drawing the row, in the y direction. + * This allows us to draw hats and simliar shapes that don't start at the + * origin. Must be non-negative (see #2820). + * @package + * @type {number} + */ + this.startY = 0; + + /** + * Whether the block has a previous connection. + * @package + * @type {boolean} + */ + this.hasPreviousConnection = !!block.previousConnection; + + /** + * The previous connection on the block, if any. + * TODO: Should this be the connection measurable instead? It would add some + * indirection but would mean we aren't mixing connections and connection + * measurables. + * @type {Blockly.RenderedConnection} + */ + this.connection = block.previousConnection; + + var precedesStatement = block.inputList.length && + block.inputList[0].type == Blockly.NEXT_STATEMENT; + + // This is the minimum height for the row. If one of its elements has a + // greater height it will be overwritten in the compute pass. + if (precedesStatement && !block.isCollapsed()) { + this.height = Blockly.blockRendering.constants.LARGE_PADDING; + } else { + this.height = Blockly.blockRendering.constants.MEDIUM_PADDING; + } +}; +goog.inherits(Blockly.blockRendering.TopRow, Blockly.blockRendering.Row); + +/** + * Convenience method to get the measurable representing the previous + * connection, if one exists. + * @return {Blockly.blockRendering.PreviousConnection} The measurable that + * represents the previous connection on this block, or null. + * @package + */ +Blockly.blockRendering.TopRow.prototype.getPreviousConnection = function() { + if (this.hasPreviousConnection) { + return /** @type {Blockly.blockRendering.PreviousConnection} */ (this.elements[2]); + } + return null; +}; + +/** + * @override + */ +Blockly.blockRendering.TopRow.prototype.measure = function() { + for (var e = 0; e < this.elements.length; e++) { + var elem = this.elements[e]; + this.width += elem.width; + if (!(elem.isSpacer())) { + if (elem.type == 'hat') { + this.startY = elem.startY; + this.height = this.height + elem.height; + } + this.height = Math.max(this.height, elem.height); + } + } + this.widthWithConnectedBlocks = this.width; +}; + +/** + * An object containing information about what elements are in the bottom row of + * 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 {!Blockly.BlockSvg} block The block for which this represents the + * bottom row. + * @package + * @constructor + * @extends {Blockly.blockRendering.Row} + */ +Blockly.blockRendering.BottomRow = function(block) { + Blockly.blockRendering.BottomRow.superClass_.constructor.call(this); + this.type = 'bottom row'; + + /** + * Whether the block has a next connection. + * @package + * @type {boolean} + */ + this.hasNextConnection = !!block.nextConnection; + + /** + * The next connection on the block, if any. + * TODO: Should this be the connection measurable instead? It would add some + * indirection but would mean we aren't mixing connections and connection + * measurables. + * @package + * @type {Blockly.RenderedConnection} + */ + this.connection = block.nextConnection; + + /** + * The amount that the bottom of the block extends below the horizontal edge, + * e.g. because of a next connection. Must be non-negative (see #2820). + * @package + * @type {number} + */ + this.overhangY = 0; + + var followsStatement = + block.inputList.length && + block.inputList[block.inputList.length - 1].type == Blockly.NEXT_STATEMENT; + this.hasFixedWidth = followsStatement && block.getInputsInline(); + + // This is the minimum height for the row. If one of its elements has a greater + // height it will be overwritten in the compute pass. + if (followsStatement) { + this.height = Blockly.blockRendering.constants.LARGE_PADDING; + } else { + this.height = this.notchShape.height; + } +}; +goog.inherits(Blockly.blockRendering.BottomRow, + Blockly.blockRendering.Row); + +/** + * Convenience method to get the measurable representing the next + * connection, if one exists. + * @return {Blockly.blockRendering.NextConnection} The measurable that + * represents the next connection on this block, or null. + * @package + */ +Blockly.blockRendering.BottomRow.prototype.getNextConnection = function() { + if (this.hasNextConnection) { + return /** @type {Blockly.blockRendering.NextConnection} */ (this.elements[2]); + } + return null; +}; + +/** + * @override + */ +Blockly.blockRendering.BottomRow.prototype.measure = function() { + for (var e = 0; e < this.elements.length; e++) { + var elem = this.elements[e]; + this.width += elem.width; + if (!(elem.isSpacer())) { + if (elem.type == 'next connection') { + this.height = this.height + elem.height; + this.overhangY = elem.height; + } + this.height = Math.max(this.height, elem.height); + } + } + this.widthWithConnectedBlocks = this.width; +};