diff --git a/blocks/procedures.js b/blocks/procedures.js index 761e4877d6e..52746e19aa8 100644 --- a/blocks/procedures.js +++ b/blocks/procedures.js @@ -466,6 +466,58 @@ Blockly.Blocks['procedures_defreturn'] = { callType_: 'procedures_callreturn' }; +Blockly.Blocks['procedures_defreturnexpr'] = { + init: function() { + var nameField = new Blockly.FieldTextInput('', Blockly.Procedures.rename); + nameField.setSpellcheck(false); + this.appendDummyInput() + .appendField(Blockly.Msg['PROCEDURES_DEFRETURN_TITLE']) + .appendField(nameField, 'NAME') + .appendField('', 'PARAMS'); + this.appendValueInput('RETURN', {style: Blockly.INDENTED_VALUE}) + .setAlign(Blockly.ALIGN_RIGHT) + .appendField(Blockly.Msg['PROCEDURES_DEFRETURN_RETURN']); + this.setMutator(new Blockly.Mutator(['procedures_mutatorarg'])); + if ((this.workspace.options.comments || + (this.workspace.options.parentWorkspace && + this.workspace.options.parentWorkspace.options.comments)) && + Blockly.Msg['PROCEDURES_DEFRETURN_COMMENT']) { + this.setCommentText(Blockly.Msg['PROCEDURES_DEFRETURN_COMMENT']); + } + this.setStyle('procedure_blocks'); + this.setTooltip(Blockly.Msg['PROCEDURES_DEFRETURN_TOOLTIP']); + this.setHelpUrl(Blockly.Msg['PROCEDURES_DEFRETURN_HELPURL']); + this.arguments_ = []; + this.argumentVarModels_ = []; + this.setStatements_(false); + this.statementConnection_ = null; + }, + setStatements_: Blockly.Blocks['procedures_defnoreturn'].setStatements_, + updateParams_: Blockly.Blocks['procedures_defnoreturn'].updateParams_, + mutationToDom: Blockly.Blocks['procedures_defnoreturn'].mutationToDom, + domToMutation: Blockly.Blocks['procedures_defnoreturn'].domToMutation, + decompose: Blockly.Blocks['procedures_defnoreturn'].decompose, + compose: Blockly.Blocks['procedures_defnoreturn'].compose, + /** + * Return the signature of this procedure definition. + * @return {!Array} Tuple containing three elements: + * - the name of the defined procedure, + * - a list of all its arguments, + * - that it DOES have a return value. + * @this {Blockly.Block} + */ + getProcedureDef: function() { + return [this.getFieldValue('NAME'), this.arguments_, true]; + }, + getVars: Blockly.Blocks['procedures_defnoreturn'].getVars, + getVarModels: Blockly.Blocks['procedures_defnoreturn'].getVarModels, + renameVarById: Blockly.Blocks['procedures_defnoreturn'].renameVarById, + updateVarName: Blockly.Blocks['procedures_defnoreturn'].updateVarName, + displayRenamedVar_: Blockly.Blocks['procedures_defnoreturn'].displayRenamedVar_, + customContextMenu: Blockly.Blocks['procedures_defnoreturn'].customContextMenu, + callType_: 'procedures_callreturn' +}; + Blockly.Blocks['procedures_mutatorcontainer'] = { /** * Mutator block for procedure container. diff --git a/core/block.js b/core/block.js index 1d4b5016343..8b1d6dcc9d1 100644 --- a/core/block.js +++ b/core/block.js @@ -1385,10 +1385,12 @@ Blockly.Block.prototype.toString = function(opt_maxLength, opt_emptyToken) { * Shortcut for appending a value input row. * @param {string} name Language-neutral identifier which may used to find this * input again. Should be unique to this block. + * @param {?Object.=} opt_extras Extra information about the input. + * - style: Rendering style hint (undefined or {@see Blockly.INDENTED_VALUE}) * @return {!Blockly.Input} The input object created. */ -Blockly.Block.prototype.appendValueInput = function(name) { - return this.appendInput_(Blockly.INPUT_VALUE, name); +Blockly.Block.prototype.appendValueInput = function(name, opt_extras) { + return this.appendInput_(Blockly.INPUT_VALUE, name, opt_extras); }; /** @@ -1681,15 +1683,17 @@ Blockly.Block.prototype.interpolate_ = function(message, args, lastDummyAlign) { * Blockly.DUMMY_INPUT. * @param {string} name Language-neutral identifier which may used to find this * input again. Should be unique to this block. + * @param {?Object.=} opt_extras Extra information about the input. + * - style: Rendering style hint (undefined or {@see Blockly.INDENTED_VALUE}) * @return {!Blockly.Input} The input object created. * @protected */ -Blockly.Block.prototype.appendInput_ = function(type, name) { +Blockly.Block.prototype.appendInput_ = function(type, name, opt_extras) { var connection = null; if (type == Blockly.INPUT_VALUE || type == Blockly.NEXT_STATEMENT) { connection = this.makeConnection_(type); } - var input = new Blockly.Input(type, name, this, connection); + var input = new Blockly.Input(type, name, this, connection, opt_extras); // Append input to list. this.inputList.push(input); return input; diff --git a/core/block_svg.js b/core/block_svg.js index 1ebaa5388a6..e5e0c41f4b4 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -1487,11 +1487,14 @@ Blockly.BlockSvg.prototype.moveNumberedInputBefore = function( * Blockly.DUMMY_INPUT. * @param {string} name Language-neutral identifier which may used to find this * input again. Should be unique to this block. + * @param {?Object.=} opt_extras Extra information describing the + * input. * @return {!Blockly.Input} The input object created. * @private */ -Blockly.BlockSvg.prototype.appendInput_ = function(type, name) { - var input = Blockly.BlockSvg.superClass_.appendInput_.call(this, type, name); +Blockly.BlockSvg.prototype.appendInput_ = function(type, name, opt_extras) { + var input = Blockly.BlockSvg.superClass_.appendInput_.call(this, type, name, + opt_extras); if (this.rendered) { this.render(); diff --git a/core/constants.js b/core/constants.js index 06cb1fafaa9..18ab6908655 100644 --- a/core/constants.js +++ b/core/constants.js @@ -174,6 +174,13 @@ Blockly.ALIGN_CENTRE = 0; */ Blockly.ALIGN_RIGHT = 1; +/*** + * ENUM to indicate that a value input should be indented like + * a statement input. + * @const + */ +Blockly.INDENTED_VALUE = 1; + /** * ENUM for no drag operation. * @const diff --git a/core/input.js b/core/input.js index 22278605bce..737602e270b 100644 --- a/core/input.js +++ b/core/input.js @@ -34,9 +34,11 @@ goog.require('Blockly.FieldLabel'); * input again. * @param {!Blockly.Block} block The block containing this input. * @param {Blockly.Connection} connection Optional connection for this input. + * @param {?Object.=} opt_extras Extra information about the input. + * - style: Rendering style hint (undefined or {@see Blockly.INDENTED_VALUE}) * @constructor */ -Blockly.Input = function(type, name, block, connection) { +Blockly.Input = function(type, name, block, connection, opt_extras) { if (type != Blockly.DUMMY_INPUT && !name) { throw Error('Value inputs and statement inputs must have non-empty name.'); } @@ -53,6 +55,8 @@ Blockly.Input = function(type, name, block, connection) { this.connection = connection; /** @type {!Array.} */ this.fieldRow = []; + /** @type {!Object.} */ + this.extraInfo = opt_extras || {}; }; /** diff --git a/core/procedures.js b/core/procedures.js index 5388bf9435e..b8547f7d7fb 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -211,6 +211,20 @@ Blockly.Procedures.flyoutCategory = function(workspace) { block.appendChild(nameField); xmlList.push(block); } + if (Blockly.Blocks['procedures_defreturnexpr']) { + // + // do something + // + var block = Blockly.utils.xml.createElement('block'); + block.setAttribute('type', 'procedures_defreturnexpr'); + block.setAttribute('gap', 16); + var nameField = Blockly.utils.xml.createElement('field'); + nameField.setAttribute('name', 'NAME'); + nameField.appendChild(Blockly.utils.xml.createTextNode( + Blockly.Msg['PROCEDURES_DEFRETURN_PROCEDURE'])); + block.appendChild(nameField); + xmlList.push(block); + } if (Blockly.Blocks['procedures_ifreturn']) { // var block = Blockly.utils.xml.createElement('block'); diff --git a/core/renderers/common/drawer.js b/core/renderers/common/drawer.js index 5887fa76788..c20c74938b2 100644 --- a/core/renderers/common/drawer.js +++ b/core/renderers/common/drawer.js @@ -117,6 +117,8 @@ Blockly.blockRendering.Drawer.prototype.drawOutline_ = function() { this.drawJaggedEdge_(row); } else if (row.hasStatement) { this.drawStatementInput_(row); + } else if (row.hasIndentedInput) { + this.drawIndentedInput_(row); } else if (row.hasExternalInput) { this.drawValueInput_(row); } else { @@ -190,6 +192,30 @@ Blockly.blockRendering.Drawer.prototype.drawValueInput_ = function(row) { }; +/** + * Add steps for an indented value input, rendered as a notch in the side + * of the block but at the same X value as any statement inputs. + * @param {!Blockly.blockRendering.Row} row The row that this input + * belongs to. + * @protected + */ +Blockly.blockRendering.Drawer.prototype.drawIndentedInput_ = function(row) { + var input = row.getLastInput(); + this.positionIndentedValueConnection_(row); + + var pathDown = (typeof input.shape.pathDown == "function") ? + input.shape.pathDown(input.height) : + input.shape.pathDown; + + this.outlinePath_ += + Blockly.utils.svgPaths.lineOnAxis('H', input.xPos + input.connectionWidth) + + Blockly.utils.svgPaths.lineOnAxis('v', input.connectionOffsetY) + + pathDown + + Blockly.utils.svgPaths.lineOnAxis('v', row.height - input.connectionHeight - input.connectionOffsetY) + + Blockly.utils.svgPaths.lineOnAxis('H', input.xPos + input.width); +}; + + /** * Add steps for a statement input. * @param {!Blockly.blockRendering.Row} row The row that this input @@ -427,6 +453,24 @@ Blockly.blockRendering.Drawer.prototype.positionExternalValueConnection_ = funct } }; +/** + * Position the connection on an indented value input, taking into account + * RTL and the small gap between the parent block and child block which lets the + * parent block's dark path show through. + * @param {!Blockly.blockRendering.Row} row The row that the connection is on. + * @protected + */ +Blockly.blockRendering.Drawer.prototype.positionIndentedValueConnection_ = function(row) { + var input = row.getLastInput(); + if (input.connection) { + var connX = row.xPos + row.statementEdge + input.connectionWidth; + if (this.info_.RTL) { + connX *= -1; + } + input.connection.setOffsetInBlock(connX, row.yPos + input.connectionOffsetY); + } +}; + /** * Position the previous connection on a block. * @protected diff --git a/core/renderers/common/info.js b/core/renderers/common/info.js index ce8ca94189e..f0664400117 100644 --- a/core/renderers/common/info.js +++ b/core/renderers/common/info.js @@ -347,6 +347,11 @@ Blockly.blockRendering.RenderInfo.prototype.addInput_ = function(input, activeRo activeRow.elements.push( new Blockly.blockRendering.StatementInput(this.constants_, input)); activeRow.hasStatement = true; + } else if (input.type == Blockly.INPUT_VALUE && + input.extraInfo.style == Blockly.INDENTED_VALUE) { + activeRow.elements.push( + new Blockly.blockRendering.IndentedValueInput(this.constants_, input)); + activeRow.hasIndentedInput = true; } else if (input.type == Blockly.INPUT_VALUE) { activeRow.elements.push( new Blockly.blockRendering.ExternalValueInput(this.constants_, input)); @@ -466,7 +471,7 @@ Blockly.blockRendering.RenderInfo.prototype.computeBounds_ = function() { for (var i = 0, row; (row = this.rows[i]); i++) { row.measure(); blockWidth = Math.max(blockWidth, row.width); - if (row.hasStatement) { + if (row.isStatementLike()) { var statementInput = row.getLastInput(); var innerWidth = row.width - statementInput.width; widestStatementRowFields = Math.max(widestStatementRowFields, innerWidth); @@ -479,7 +484,7 @@ Blockly.blockRendering.RenderInfo.prototype.computeBounds_ = function() { this.width = blockWidth; for (var i = 0, row; (row = this.rows[i]); i++) { - if (row.hasStatement) { + if (row.isStatementLike()) { row.statementEdge = this.statementEdge; } } @@ -501,7 +506,7 @@ Blockly.blockRendering.RenderInfo.prototype.computeBounds_ = function() { */ Blockly.blockRendering.RenderInfo.prototype.alignRowElements_ = function() { for (var i = 0, row; (row = this.rows[i]); i++) { - if (row.hasStatement) { + if (row.isStatementLike()) { this.alignStatementRow_( /** @type {!Blockly.blockRendering.InputRow} */ (row)); } else { diff --git a/core/renderers/geras/drawer.js b/core/renderers/geras/drawer.js index 61b38826b25..532c62595d0 100644 --- a/core/renderers/geras/drawer.js +++ b/core/renderers/geras/drawer.js @@ -96,6 +96,15 @@ Blockly.geras.Drawer.prototype.drawValueInput_ = function(row) { Blockly.geras.Drawer.superClass_.drawValueInput_.call(this, row); }; +/** + * @override + */ +Blockly.geras.Drawer.prototype.drawIndentedInput_ = function(row) { + this.highlighter_.drawIndentedInput(row); + + Blockly.geras.Drawer.superClass_.drawIndentedInput_.call(this, row); +}; + /** * @override */ diff --git a/core/renderers/geras/highlighter.js b/core/renderers/geras/highlighter.js index a740fe1cfda..b57ffa52524 100644 --- a/core/renderers/geras/highlighter.js +++ b/core/renderers/geras/highlighter.js @@ -146,6 +146,25 @@ Blockly.geras.Highlighter.prototype.drawValueInput = function(row) { } }; +Blockly.geras.Highlighter.prototype.drawIndentedInput = function(row) { + var input = row.getLastInput(); + if (this.RTL_) { + var belowTabHeight = row.height - input.connectionHeight - input.connectionOffsetY; + + this.steps_ += + Blockly.utils.svgPaths.moveTo( + input.xPos + this.highlightOffset_ + input.connectionWidth - 1, row.yPos) + + Blockly.utils.svgPaths.lineOnAxis('v', input.connectionOffsetY) + + this.puzzleTabPaths_.pathDown(this.RTL_) + + Blockly.utils.svgPaths.lineOnAxis('v', belowTabHeight); + } else { + this.steps_ += + Blockly.utils.svgPaths.moveTo(input.xPos + input.connectionWidth, + row.yPos + input.connectionOffsetY) + + this.puzzleTabPaths_.pathDown(this.RTL_); + } +}; + Blockly.geras.Highlighter.prototype.drawStatementInput = function(row) { var input = row.getLastInput(); if (this.RTL_) { diff --git a/core/renderers/geras/info.js b/core/renderers/geras/info.js index 90f1d82388f..dd55ac8adf3 100644 --- a/core/renderers/geras/info.js +++ b/core/renderers/geras/info.js @@ -86,6 +86,11 @@ Blockly.geras.RenderInfo.prototype.addInput_ = function(input, activeRow) { activeRow.elements.push( new Blockly.geras.StatementInput(this.constants_, input)); activeRow.hasStatement = true; + } else if (input.type == Blockly.INPUT_VALUE && + input.extraInfo.style == Blockly.INDENTED_VALUE) { + activeRow.elements.push( + new Blockly.blockRendering.IndentedValueInput(this.constants_, input)); + activeRow.hasIndentedInput = true; } else if (input.type == Blockly.INPUT_VALUE) { activeRow.elements.push( new Blockly.blockRendering.ExternalValueInput(this.constants_, input)); @@ -248,7 +253,7 @@ Blockly.geras.RenderInfo.prototype.getInRowSpacing_ = function(prev, next) { Blockly.geras.RenderInfo.prototype.addAlignmentPadding_ = function(row, missingSpace) { var firstSpacer = row.getFirstSpacer(); var lastSpacer = row.getLastSpacer(); - if (row.hasExternalInput || row.hasStatement) { + if (row.hasExternalInput || row.hasStatement || row.hasIndentedInput) { row.widthWithConnectedBlocks += missingSpace; } @@ -327,7 +332,7 @@ Blockly.geras.RenderInfo.prototype.getElemCenterline_ = function(row, elem) { if (Blockly.blockRendering.Types.isField(elem) || Blockly.blockRendering.Types.isIcon(elem)) { result += (elem.height / 2); - if ((row.hasInlineInput || row.hasStatement) && + if ((row.hasInlineInput || row.hasStatement || row.hasIndentedInput) && elem.height + this.constants_.TALL_INPUT_FIELD_OFFSET_Y <= row.height) { result += this.constants_.TALL_INPUT_FIELD_OFFSET_Y; } diff --git a/core/renderers/measurables/inputs.js b/core/renderers/measurables/inputs.js index 2d44fd02bcf..6ee5cc02573 100644 --- a/core/renderers/measurables/inputs.js +++ b/core/renderers/measurables/inputs.js @@ -165,3 +165,24 @@ Blockly.blockRendering.ExternalValueInput = function(constants, input) { }; Blockly.utils.object.inherits(Blockly.blockRendering.ExternalValueInput, Blockly.blockRendering.InputConnection); + +/** + * An object containing information about the space an indented value input + * takes up during rendering + * @param {!Blockly.blockRendering.ConstantProvider} constants The rendering + * constants provider. + * @param {!Blockly.Input} input The indented value input to measure and store + * information for. + * @constructor + */ +Blockly.blockRendering.IndentedValueInput = function(constants, input) { + Blockly.blockRendering.IndentedValueInput.superClass_.constructor.call(this, + constants, input); + if (!this.connectedBlock) { + this.height = this.constants_.EMPTY_STATEMENT_INPUT_HEIGHT; + } else { + this.height = this.connectedBlockHeight; + } +}; +Blockly.utils.object.inherits(Blockly.blockRendering.IndentedValueInput, + Blockly.blockRendering.ExternalValueInput); diff --git a/core/renderers/measurables/rows.js b/core/renderers/measurables/rows.js index 141ceb08a61..2001d52d330 100644 --- a/core/renderers/measurables/rows.js +++ b/core/renderers/measurables/rows.js @@ -118,6 +118,13 @@ Blockly.blockRendering.Row = function(constants) { */ this.hasExternalInput = false; + /** + * Whether the row has any indented inputs. + * @package + * @type {boolean} + */ + this.hasIndentedInput = false; + /** * Whether the row has any statement inputs. * @package @@ -156,6 +163,16 @@ Blockly.blockRendering.Row = function(constants) { this.notchOffset = this.constants_.NOTCH_OFFSET_LEFT; }; +/** + * Returns whether the row should be treated like a statement during the + * measuring pass. + * @return {boolean} true if the row should measure like a statement input, + * otherwise false. + */ +Blockly.blockRendering.Row.prototype.isStatementLike = function() { + return this.hasStatement || this.hasIndentedInput; +}; + /** * Inspect all subcomponents and populate all size properties on the row. * @package