diff --git a/core/renderers/block_rendering_rewrite/block_render_draw.js b/core/renderers/block_rendering_rewrite/block_render_draw.js index c7a423b0d..c9b683632 100644 --- a/core/renderers/block_rendering_rewrite/block_render_draw.js +++ b/core/renderers/block_rendering_rewrite/block_render_draw.js @@ -60,6 +60,7 @@ Blockly.blockRendering.Drawer = function(block, info) { * @private */ Blockly.blockRendering.Drawer.prototype.draw_ = function() { + this.hideHiddenIcons_(); this.drawOutline_(); this.drawInternals_(); this.block_.setPaths_(this.pathObject_); @@ -83,6 +84,17 @@ Blockly.blockRendering.Drawer.prototype.recordSizeOnBlock_ = function() { this.block_.startHat_ = this.info_.topRow.startHat; }; +/** + * Hide icons that were marked as hidden. + * @private + */ +Blockly.blockRendering.Drawer.prototype.hideHiddenIcons_ = function() { + for (var i = 0; i < this.info_.hiddenIcons.length; i++) { + var iconInfo = this.info_.hiddenIcons[i]; + iconInfo.icon.iconGroup_.setAttribute('display', 'none'); + } +}; + /** * Create the outline of the block. This is a single continuous path. * @private @@ -91,7 +103,9 @@ Blockly.blockRendering.Drawer.prototype.drawOutline_ = function() { this.drawTop_(); for (var r = 1; r < this.info_.rows.length - 1; r++) { var row = this.info_.rows[r]; - if (row.hasStatement) { + if (row.hasJaggedEdge) { + this.drawJaggedEdge_(row); + } else if (row.hasStatement) { this.drawStatementInput_(row); } else if (row.hasExternalInput) { this.drawValueInput_(row); @@ -136,6 +150,20 @@ Blockly.blockRendering.Drawer.prototype.drawTop_ = function() { this.steps_.push('v', topRow.height); }; +/** + * Add steps for the jagged edge of a row on a collapsed block. + * @param {!Blockly.blockRendering.Row} row The row to draw the side of. + * @private + */ +Blockly.blockRendering.Drawer.prototype.drawJaggedEdge_ = function(row) { + if (this.highlighter_) { + this.highlighter_.drawJaggedEdge_(row); + } + this.steps_.push(Blockly.blockRendering.constants.JAGGED_TEETH.path); + var remainder = + row.height - Blockly.blockRendering.constants.JAGGED_TEETH.height; + this.steps_.push('v', remainder); +}; /** * Add steps for an external value input, rendered as a notch in the side 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 323e13b83..234244450 100644 --- a/core/renderers/block_rendering_rewrite/block_render_draw_highlight.js +++ b/core/renderers/block_rendering_rewrite/block_render_draw_highlight.js @@ -67,6 +67,8 @@ Blockly.blockRendering.Highlighter = function(info, pathObject) { this.puzzleTabPaths_ = Blockly.blockRendering.highlightConstants.PUZZLE_TAB; this.notchPaths_ = Blockly.blockRendering.highlightConstants.NOTCH; this.startPaths_ = Blockly.blockRendering.highlightConstants.START_HAT; + this.jaggedTeethPaths_ = + Blockly.blockRendering.highlightConstants.JAGGED_TEETH; }; Blockly.blockRendering.Highlighter.prototype.drawTopCorner = function(row) { @@ -91,6 +93,16 @@ Blockly.blockRendering.Highlighter.prototype.drawTopCorner = function(row) { this.steps_.push('H', row.width - this.highlightOffset_); }; +Blockly.blockRendering.Highlighter.prototype.drawJaggedEdge_ = function(row) { + if (this.info_.RTL) { + this.steps_.push('H', row.width - this.highlightOffset_); + this.steps_.push(this.jaggedTeethPaths_.pathLeft); + var remainder = + row.height - this.jaggedTeethPaths_.height - this.highlightOffset_; + this.steps_.push(Blockly.utils.svgPaths.lineOnAxis('v', remainder)); + } +}; + Blockly.blockRendering.Highlighter.prototype.drawValueInput = function(row) { var input = row.getLastInput(); var steps = ''; diff --git a/core/renderers/block_rendering_rewrite/block_render_info.js b/core/renderers/block_rendering_rewrite/block_render_info.js index 1cd9aefc4..9859e2e62 100644 --- a/core/renderers/block_rendering_rewrite/block_render_info.js +++ b/core/renderers/block_rendering_rewrite/block_render_info.js @@ -59,6 +59,12 @@ Blockly.blockRendering.RenderInfo = function(block) { */ this.isInline = block.getInputsInline() && !block.isCollapsed(); + /** + * Whether the block is collapsed. + * @type {boolean} + */ + this.isCollapsed = block.isCollapsed(); + /** * Whether the block is an insertion marker. Insertion markers are the same * shape as normal blocks, but don't show fields. @@ -103,6 +109,12 @@ Blockly.blockRendering.RenderInfo = function(block) { */ this.rows = []; + /** + * An array of measureable objects containing hidden icons. + * @type {!Array.} + */ + this.hiddenIcons = []; + this.topRow = null; this.bottomRow = null; @@ -145,16 +157,25 @@ Blockly.blockRendering.RenderInfo.prototype.createRows_ = function() { var icons = this.block_.getIcons(); if (icons.length) { for (var i = 0; i < icons.length; i++) { - activeRow.elements.push( - new Blockly.blockRendering.Icon(icons[i])); + var icon = icons[i]; + var iconInfo = new Blockly.blockRendering.Icon(icon); + if (this.isCollapsed && icon.collapseHidden) { + this.hiddenIcons.push(iconInfo); + } else { + activeRow.elements.push(iconInfo); + } } } + var lastInput = undefined; // Loop across all of the inputs on the block, creating objects for anything // that needs to be rendered and breaking the block up into visual rows. for (var i = 0; i < this.block_.inputList.length; i++) { var input = this.block_.inputList[i]; - if (this.shouldStartNewRow_(input, this.block_.inputList[i - 1])) { + if (!input.isVisible()) { + continue; + } + if (this.shouldStartNewRow_(input, lastInput)) { // Finish this row and create a new one. this.rows.push(activeRow); activeRow = new Blockly.blockRendering.Row(); @@ -166,6 +187,12 @@ Blockly.blockRendering.RenderInfo.prototype.createRows_ = function() { activeRow.elements.push(new Blockly.blockRendering.Field(field, input)); } this.addInput_(input, activeRow); + lastInput = input; + } + + if (this.isCollapsed) { + activeRow.hasJaggedEdge = true; + activeRow.elements.push(new Blockly.blockRendering.JaggedEdge()); } if (activeRow.elements.length) { @@ -285,7 +312,6 @@ Blockly.blockRendering.RenderInfo.prototype.addElemSpacing_ = function() { row.elements.push(new Blockly.blockRendering.InRowSpacer( this.getInRowSpacing_(null, oldElems[0]))); } - for (var e = 0; e < oldElems.length; e++) { row.elements.push(oldElems[e]); var spacing = this.getInRowSpacing_(oldElems[e], oldElems[e + 1]); @@ -321,7 +347,7 @@ Blockly.blockRendering.RenderInfo.prototype.getInRowSpacing_ = function(prev, ne return Blockly.blockRendering.constants.LARGE_PADDING; } - // Spacing between a field or icon and the end of the row. + // Spacing between a non-input and the end of the row. if (!prev.isInput && !next) { // Between an editable field and the end of the row. if (prev.isField() && prev.isEditable) { @@ -342,6 +368,10 @@ Blockly.blockRendering.RenderInfo.prototype.getInRowSpacing_ = function(prev, ne if (prev.isRoundedCorner()) { return Blockly.blockRendering.constants.MIN_BLOCK_WIDTH; } + // Between a jagged edge and the end of the row. + if (prev.isJaggedEdge()) { + return Blockly.blockRendering.constants.NO_PADDING; + } // Between noneditable fields and icons and the end of the row. return Blockly.blockRendering.constants.LARGE_PADDING; } @@ -357,7 +387,7 @@ Blockly.blockRendering.RenderInfo.prototype.getInRowSpacing_ = function(prev, ne } } - // Spacing between a field or icon and an input. + // Spacing between a non-input and an input. if (!prev.isInput && next.isInput) { // Between an editable field and an input. if (prev.isEditable) { @@ -411,7 +441,7 @@ Blockly.blockRendering.RenderInfo.prototype.getInRowSpacing_ = function(prev, ne } } - // Spacing between a rounded corner and a previous or next connection + // Spacing between a rounded corner and a previous or next connection. if (prev.isRoundedCorner()) { if (next.isPreviousConnection()) { return Blockly.blockRendering.constants.NOTCH_OFFSET_ROUNDED_CORNER_PREV; @@ -429,6 +459,11 @@ Blockly.blockRendering.RenderInfo.prototype.getInRowSpacing_ = function(prev, ne return Blockly.blockRendering.constants.LARGE_PADDING; } + // Spacing between anything and a jagged edge. + if (next.isJaggedEdge()) { + return Blockly.blockRendering.constants.LARGE_PADDING; + } + return Blockly.blockRendering.constants.MEDIUM_PADDING; }; diff --git a/core/renderers/block_rendering_rewrite/block_rendering_constants.js b/core/renderers/block_rendering_rewrite/block_rendering_constants.js index 1439d3fbf..e82ab67c7 100644 --- a/core/renderers/block_rendering_rewrite/block_rendering_constants.js +++ b/core/renderers/block_rendering_rewrite/block_rendering_constants.js @@ -141,6 +141,39 @@ Blockly.blockRendering.constants.START_POINT = Blockly.utils.svgPaths.moveBy(0, Blockly.blockRendering.constants.TOP_LEFT_CORNER_START = 'm 0,' + Blockly.blockRendering.constants.CORNER_RADIUS; +/** + * Height of SVG path for jagged teeth at the end of collapsed blocks. + * @const + */ +Blockly.blockRendering.constants.JAGGED_TEETH_HEIGHT = 12; +/** + * Width of SVG path for jagged teeth at the end of collapsed blocks. + * @const + */ +Blockly.blockRendering.constants.JAGGED_TEETH_WIDTH = 6; + +/** + * SVG path for drawing jagged teeth at the end of collapsed blocks. + * @const + */ +Blockly.blockRendering.constants.JAGGED_TEETH = (function() { + var height = Blockly.blockRendering.constants.JAGGED_TEETH_HEIGHT; + var width = Blockly.blockRendering.constants.JAGGED_TEETH_WIDTH; + + var mainPath = + Blockly.utils.svgPaths.line( + [ + Blockly.utils.svgPaths.point(6, 3), + Blockly.utils.svgPaths.point(-12, 6), + Blockly.utils.svgPaths.point(6, 3) + ]); + return { + height: height, + width: width, + path: mainPath + }; +})(); + /** * Information about the hat on a start block. */ @@ -180,7 +213,7 @@ Blockly.blockRendering.constants.PUZZLE_TAB = (function() { var halfHeight = height / 2; var control1Y = halfHeight + overlap; var control2Y = halfHeight + 0.5; - var control3Y = overlap; //2.5 + var control3Y = overlap; // 2.5 var endPoint1 = Blockly.utils.svgPaths.point(-width, forward * halfHeight); var endPoint2 = Blockly.utils.svgPaths.point(width, forward * halfHeight); diff --git a/core/renderers/block_rendering_rewrite/highlight_constants.js b/core/renderers/block_rendering_rewrite/highlight_constants.js index 340aadc2d..bd8ac7f32 100644 --- a/core/renderers/block_rendering_rewrite/highlight_constants.js +++ b/core/renderers/block_rendering_rewrite/highlight_constants.js @@ -205,6 +205,16 @@ Blockly.blockRendering.highlightConstants.NOTCH = (function() { }; })(); +Blockly.blockRendering.highlightConstants.JAGGED_TEETH = (function() { + var pathLeft = + Blockly.utils.svgPaths.lineTo(5.1, 2.6) + + Blockly.utils.svgPaths.moveBy(-10.2, 6.8) + + Blockly.utils.svgPaths.lineTo(5.1, 2.6); + return { + pathLeft: pathLeft + }; +})(); + Blockly.blockRendering.highlightConstants.START_HAT = (function() { var pathRtl = Blockly.utils.svgPaths.moveBy(25, -8.7) + diff --git a/core/renderers/block_rendering_rewrite/measurables.js b/core/renderers/block_rendering_rewrite/measurables.js index be247d114..f2c81fefe 100644 --- a/core/renderers/block_rendering_rewrite/measurables.js +++ b/core/renderers/block_rendering_rewrite/measurables.js @@ -127,6 +127,16 @@ Blockly.blockRendering.Measurable.prototype.isRoundedCorner = function() { Blockly.blockRendering.Measurable.prototype.isSquareCorner = function() { return this.type == 'square corner'; }; + +/** + * Whether this stores information about a jagged edge. + * @return {boolean} True if this object stores information about a jagged edge. + * @package + */ +Blockly.blockRendering.Measurable.prototype.isJaggedEdge = function() { + return this.type == 'jagged edge'; +}; + /** * The base class to represent an input that takes up space on a block * during rendering @@ -178,6 +188,21 @@ Blockly.blockRendering.Icon = function(icon) { }; goog.inherits(Blockly.blockRendering.Icon, Blockly.blockRendering.Measurable); +/** + * An object containing information about the jagged edge of a collapsed block + * takes up during rendering + * @package + * @constructor + */ +Blockly.blockRendering.JaggedEdge = function() { + Blockly.blockRendering.JaggedEdge.superClass_.constructor.call(this); + this.type = 'jagged edge'; + this.height = Blockly.blockRendering.constants.JAGGED_TEETH.height; + this.width = Blockly.blockRendering.constants.JAGGED_TEETH.width; +}; +goog.inherits(Blockly.blockRendering.JaggedEdge, Blockly.blockRendering.Measurable); + + /** * An object containing information about the space a field takes up during * rendering @@ -386,6 +411,7 @@ Blockly.blockRendering.Row = function() { this.hasStatement = false; this.hasInlineInput = false; this.hasDummyInput = false; + this.hasJaggedEdge = false; }; Blockly.blockRendering.Row.prototype.isSpacer = function() { @@ -409,9 +435,11 @@ Blockly.blockRendering.Row.prototype.measure = function() { }; Blockly.blockRendering.Row.prototype.getLastInput = function() { - // There's always a spacer after the last input, unless there are no inputs. - if (this.elements.length > 1) { - var elem = this.elements[this.elements.length - 2]; + 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()) { @@ -468,7 +496,7 @@ Blockly.blockRendering.TopRow = function(block) { // 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) { + if (precedesStatement && !block.isCollapsed()) { this.height = Blockly.blockRendering.constants.LARGE_PADDING; } else { this.height = Blockly.blockRendering.constants.MEDIUM_PADDING;