From 178f90dce2543b7695f1c01457acaee25832a26e Mon Sep 17 00:00:00 2001 From: jwklong Date: Fri, 14 Nov 2025 19:02:25 +0000 Subject: [PATCH 1/9] half-done on editor --- blocks_vertical/procedures.js | 68 +++++++++++++++++++++----- tests/custom_procedure_playground.html | 5 ++ 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 039eaf4276..291645be3a 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -298,8 +298,8 @@ Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_ = function() { * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) { - // Split the proc into components, by %n, %b, %o, %a, and %s (ignoring escaped). - var procComponents = this.procCode_.split(/(?=[^\\]%[nboas])/); + // Split the proc into components, by %n, %b, %o, %a, %c, and %s (ignoring escaped). + var procComponents = this.procCode_.split(/(?=[^\\]%[nboasc])/); procComponents = procComponents.map(function(c) { return c.trim(); // Strip whitespace. }); @@ -310,7 +310,7 @@ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) var labelText; if (component.substring(0, 1) == '%') { var argumentType = component.substring(1, 2); - if (!(argumentType == 'n' || argumentType == 'b' || argumentType == 'o' || argumentType == 'a' || argumentType == 's')) { + if (!['n', 'b', 'o', 'a', 's', 'c'].includes(argumentType)) { throw new Error( 'Found an custom procedure with an invalid type: ' + argumentType); } @@ -318,13 +318,18 @@ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) var id = this.argumentIds_[argumentCount]; - var input = this.appendValueInput(id); - if (argumentType == 'b') { - input.setCheck('Boolean'); - } else if (argumentType == 'o') { - input.setCheck('Object'); - } else if (argumentType == 'a') { - input.setCheck('Array'); + var input + if (argumentType === 'c') { + input = this.appendStatementInput(id); + } else { + input = this.appendValueInput(id); + if (argumentType == 'b') { + input.setCheck('Boolean'); + } else if (argumentType == 'o') { + input.setCheck('Object'); + } else if (argumentType == 'a') { + input.setCheck('Array'); + } } this.populateArgument_(argumentType, argumentCount, connectionMap, id, input); @@ -621,7 +626,8 @@ Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnDeclaration_ = function( } // Attach the block. - input.connection.connect(argumentEditor.outputConnection); + if (type == "c") input.connection.connect(argumentEditor.previousConnection) + else input.connection.connect(argumentEditor.outputConnection); }; /** @@ -676,6 +682,8 @@ Blockly.ScratchBlocks.ProcedureUtils.createArgumentEditor_ = function( var newBlock = this.workspace.newBlock('argument_editor_object'); } else if (argumentType == 'a') { var newBlock = this.workspace.newBlock('argument_editor_array'); + } else if (argumentType == 'c') { + var newBlock = this.workspace.newBlock('argument_editor_statement') } newBlock.setFieldValue(displayName, 'TEXT'); newBlock.setShadow(true); @@ -721,6 +729,11 @@ Blockly.ScratchBlocks.ProcedureUtils.updateDeclarationProcCode_ = function() { } else { this.procCode_ += '%s'; } + } else if (input.type == Blockly.NEXT_STATEMENT) { + var target = input.connection.targetBlock(); + this.displayNames_.push(target.getFieldValue('TEXT')); + this.argumentIds_.push(input.name); + this.procCode += '%c'; } else { throw new Error( 'Unexpected input type on a procedure mutator root: ' + input.type); @@ -801,6 +814,21 @@ Blockly.ScratchBlocks.ProcedureUtils.addArrayExternal = function() { this.focusLastEditor_(); }; +/** + * Externally-visible function to add a statement argument to the procedure + * declaration. + * @public + */ +Blockly.ScratchBlocks.ProcedureUtils.addStatementExternal = function() { + Blockly.WidgetDiv.hide(true); + this.procCode_ = this.procCode_ + ' %c'; + this.displayNames_.push('statement'); + this.argumentIds_.push(Blockly.utils.genUid()); + this.argumentDefaults_.push(''); + this.updateDisplay_(); + this.focusLastEditor_(); +}; + /** * Externally-visible function to add a string/number argument to the procedure * declaration. @@ -1079,6 +1107,7 @@ Blockly.Blocks['procedures_declaration'] = { addBooleanExternal: Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal, addObjectExternal: Blockly.ScratchBlocks.ProcedureUtils.addObjectExternal, addArrayExternal: Blockly.ScratchBlocks.ProcedureUtils.addArrayExternal, + addStatementExternal: Blockly.ScratchBlocks.ProcedureUtils.addStatementExternal, addStringNumberExternal: Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal, onChangeFn: Blockly.ScratchBlocks.ProcedureUtils.updateDeclarationProcCode_, // For colour fixing of the fields when on the GUI side look at the GUI!!!. @@ -1227,6 +1256,23 @@ Blockly.Blocks['argument_editor_array'] = { removeFieldCallback: Blockly.ScratchBlocks.ProcedureUtils.removeArgumentCallback_ }; +Blockly.Blocks['argument_editor_statement'] = { + init: function() { + this.jsonInit({ "message0": "%1", + "args0": [ + { + "type": "field_input_removable", + "name": "TEXT", + "text": "foo" + } + ], + "extensions": ["shape_statement", "colours_more"] + }); + }, + // Exist on declaration and arguments editors, with different implementations. + removeFieldCallback: Blockly.ScratchBlocks.ProcedureUtils.removeArgumentCallback_ +}; + Blockly.Blocks['argument_editor_string_number'] = { init: function() { this.jsonInit({ "message0": " %1", diff --git a/tests/custom_procedure_playground.html b/tests/custom_procedure_playground.html index 793c654b4a..a19b90f69c 100644 --- a/tests/custom_procedure_playground.html +++ b/tests/custom_procedure_playground.html @@ -48,6 +48,7 @@ + @@ -211,6 +212,10 @@ mutationRoot.addArrayExternal(); } + function addStatement() { + mutationRoot.addStatementExternal(); + } + function addTextNumber() { mutationRoot.addStringNumberExternal(); } From 23fd93587283d5e537d4c38c133792cb56347c7c Mon Sep 17 00:00:00 2001 From: jwklong Date: Sat, 15 Nov 2025 10:22:52 +0000 Subject: [PATCH 2/9] fix typo --- blocks_vertical/procedures.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 291645be3a..a2d85ed57a 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -733,7 +733,7 @@ Blockly.ScratchBlocks.ProcedureUtils.updateDeclarationProcCode_ = function() { var target = input.connection.targetBlock(); this.displayNames_.push(target.getFieldValue('TEXT')); this.argumentIds_.push(input.name); - this.procCode += '%c'; + this.procCode_ += '%c'; } else { throw new Error( 'Unexpected input type on a procedure mutator root: ' + input.type); From 18bec065096061fdf1745a9fc93d25a5a55b52ed Mon Sep 17 00:00:00 2001 From: jwklong Date: Sat, 15 Nov 2025 10:31:54 +0000 Subject: [PATCH 3/9] finished --- blocks_vertical/procedures.js | 35 ++++++++++++++++++++++++++++++----- core/scratch_blocks_utils.js | 6 +----- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index a2d85ed57a..264e488c9d 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -493,6 +493,8 @@ Blockly.ScratchBlocks.ProcedureUtils.createArgumentReporter_ = function( var blockType = 'argument_reporter_object'; } else if (argumentType == 'a') { var blockType = 'argument_reporter_array'; + } else if (argumentType == 'c') { + var blockType = 'argument_reporter_statement' } Blockly.Events.disable(); try { @@ -537,7 +539,8 @@ Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnCaller_ = function(type, if (connectionMap && oldBlock) { // Reattach the old block and shadow DOM. connectionMap[input.name] = null; - oldBlock.outputConnection.connect(input.connection); + if (type == 'c') oldBlock.previousConnection.connect(input.connection); + else oldBlock.outputConnection.connect(input.connection); if ((['s', 'n', 'b'].includes(type)) && this.generateShadows_) { var shadowDom = oldShadow || this.buildShadowDom_(type); input.connection.setShadowDom(shadowDom); @@ -584,7 +587,8 @@ Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnPrototype_ = function( } // Attach the block. - input.connection.connect(argumentReporter.outputConnection); + if (type == 'c') input.connection.connect(argumentReporter.previousConnection); + else input.connection.connect(argumentReporter.outputConnection); }; /** @@ -626,7 +630,7 @@ Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnDeclaration_ = function( } // Attach the block. - if (type == "c") input.connection.connect(argumentEditor.previousConnection) + if (type == 'c') input.connection.connect(argumentEditor.previousConnection) else input.connection.connect(argumentEditor.outputConnection); }; @@ -655,6 +659,9 @@ Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_ = function(oldBlock, if (type == 'a' && oldBlock.type == 'argument_reporter_array') { return true; } + if (type == 'c' && oldBlock.type == 'argument_reporter_statement') { + return true; + } return false; }; @@ -942,7 +949,8 @@ Blockly.ScratchBlocks.ProcedureUtils.updateArgumentReporterNames_ = function(pre if ((block.type === 'argument_reporter_string_number' || block.type === 'argument_reporter_boolean' || block.type === 'argument_reporter_object' || - block.type === 'argument_reporter_array' + block.type === 'argument_reporter_array' || + block.type === 'argument_reporter_statement' ) && !block.isShadow()) { // Exclude arg reporters in the prototype block, which are shadows. argReporters.push(block); @@ -1193,6 +1201,23 @@ Blockly.Blocks['argument_reporter_string_number'] = { domToMutation: argumentReporterDomToMutation }; +Blockly.Blocks['argument_reporter_statement'] = { + init: function() { + this.jsonInit({ "message0": " %1", + "args0": [ + { + "type": "field_label_serializable", + "name": "VALUE", + "text": "" + } + ], + "extensions": ["colours_more", "shape_statement"] + }); + }, + mutationToDom: argumentReporterMutationToDom, + domToMutation: argumentReporterDomToMutation +}; + Blockly.Blocks['argument_editor_boolean'] = { init: function() { this.jsonInit({ "message0": " %1", @@ -1258,7 +1283,7 @@ Blockly.Blocks['argument_editor_array'] = { Blockly.Blocks['argument_editor_statement'] = { init: function() { - this.jsonInit({ "message0": "%1", + this.jsonInit({ "message0": " %1", "args0": [ { "type": "field_input_removable", diff --git a/core/scratch_blocks_utils.js b/core/scratch_blocks_utils.js index cec3fe91e5..6ab2ded53f 100644 --- a/core/scratch_blocks_utils.js +++ b/core/scratch_blocks_utils.js @@ -83,11 +83,7 @@ Blockly.scratchBlocksUtils.changeObscuredShadowIds = function(block) { * @package */ Blockly.scratchBlocksUtils.isShadowArgumentReporter = function(block) { - return (block.isShadow() && (block.type == 'argument_reporter_boolean' || - block.type == 'argument_reporter_object' || - block.type == 'argument_reporter_array' || - block.type == 'argument_reporter_string_number' - )); + return (block.isShadow() && block.type.startsWith("argument_reporter_")); }; /** From 75bf0d77dc1ef11d8b660a3ee06790179ee8b66a Mon Sep 17 00:00:00 2001 From: jwklong Date: Sat, 15 Nov 2025 15:19:02 +0000 Subject: [PATCH 4/9] fix the evil block inside the prototype branch thing --- blocks_vertical/procedures.js | 10 +++++++--- blocks_vertical/vertical_extensions.js | 8 ++++---- core/block.js | 8 +++++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 264e488c9d..49b9223824 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -214,8 +214,8 @@ Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_ = function() { if (!wasRendered && this.getReturn) { this.setInputsInline(true); if (this.getReturn() === Blockly.PROCEDURES_CALL_TYPE_STATEMENT) { - this.setPreviousStatement(true, null); - this.setNextStatement(true, null); + this.setPreviousStatement(true, "normal"); + this.setNextStatement(true, "normal"); } else { if (this.getReturn() === Blockly.PROCEDURES_CALL_TYPE_BOOLEAN) { this.setOutput(true, null); @@ -320,7 +320,7 @@ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) var input if (argumentType === 'c') { - input = this.appendStatementInput(id); + input = this.appendStatementInput(id).setCheck(this.type == 'procedures_prototype' ? "argumentReporterCommand" : "normal"); } else { input = this.appendValueInput(id); if (argumentType == 'b') { @@ -505,6 +505,10 @@ Blockly.ScratchBlocks.ProcedureUtils.createArgumentReporter_ = function( newBlock.initSvg(); newBlock.render(false); } + if (argumentType === 'c') { + newBlock.setPreviousStatement(true, 'argumentReporterCommand') + newBlock.setNextStatement(true, 'argumentReporterCommand') + } } finally { Blockly.Events.enable(); } diff --git a/blocks_vertical/vertical_extensions.js b/blocks_vertical/vertical_extensions.js index 3666a84868..f421cfb371 100644 --- a/blocks_vertical/vertical_extensions.js +++ b/blocks_vertical/vertical_extensions.js @@ -76,8 +76,8 @@ Blockly.ScratchBlocks.VerticalExtensions.COLOUR_TEXTFIELD = function() { */ Blockly.ScratchBlocks.VerticalExtensions.SHAPE_STATEMENT = function() { this.setInputsInline(true); - this.setPreviousStatement(true, null); - this.setNextStatement(true, null); + this.setPreviousStatement(true, "normal"); + this.setNextStatement(true, "normal"); }; /** @@ -89,7 +89,7 @@ Blockly.ScratchBlocks.VerticalExtensions.SHAPE_STATEMENT = function() { */ Blockly.ScratchBlocks.VerticalExtensions.SHAPE_HAT = function() { this.setInputsInline(true); - this.setNextStatement(true, null); + this.setNextStatement(true, "normal"); }; /** @@ -101,7 +101,7 @@ Blockly.ScratchBlocks.VerticalExtensions.SHAPE_HAT = function() { */ Blockly.ScratchBlocks.VerticalExtensions.SHAPE_END = function() { this.setInputsInline(true); - this.setPreviousStatement(true, null); + this.setPreviousStatement(true, "normal"); }; /** diff --git a/core/block.js b/core/block.js index ae49a86015..a2f8755788 100644 --- a/core/block.js +++ b/core/block.js @@ -1037,7 +1037,7 @@ Blockly.Block.prototype.setFieldValue = function(newValue, name) { Blockly.Block.prototype.setPreviousStatement = function(newBoolean, opt_check) { if (newBoolean) { if (opt_check === undefined) { - opt_check = null; + opt_check = "normal"; } if (!this.previousConnection) { goog.asserts.assert(!this.outputConnection, @@ -1065,7 +1065,7 @@ Blockly.Block.prototype.setPreviousStatement = function(newBoolean, opt_check) { Blockly.Block.prototype.setNextStatement = function(newBoolean, opt_check) { if (newBoolean) { if (opt_check === undefined) { - opt_check = null; + opt_check = "normal"; } if (!this.nextConnection) { this.nextConnection = this.makeConnection_(Blockly.NEXT_STATEMENT); @@ -1255,7 +1255,9 @@ Blockly.Block.prototype.appendValueInput = function(name) { * @return {!Blockly.Input} The input object created. */ Blockly.Block.prototype.appendStatementInput = function(name) { - return this.appendInput_(Blockly.NEXT_STATEMENT, name); + let output = this.appendInput_(Blockly.NEXT_STATEMENT, name); + output.setCheck("normal") + return output }; /** From 093d8ceb7f1b5cddbdcd715326755125d65b0301 Mon Sep 17 00:00:00 2001 From: jwklong Date: Sat, 15 Nov 2025 17:13:51 +0000 Subject: [PATCH 5/9] add my branched reporter fixes so i can get my custom role --- blocks_vertical/procedures.js | 3 +++ core/block_render_svg_vertical.js | 36 ++++++++++++++++++------------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 49b9223824..193eacafbb 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -216,6 +216,9 @@ Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_ = function() { if (this.getReturn() === Blockly.PROCEDURES_CALL_TYPE_STATEMENT) { this.setPreviousStatement(true, "normal"); this.setNextStatement(true, "normal"); + } else if (/*this.inputList.find(v => v.type == Blockly.NEXT_STATEMENT)*/false) { + this.setOutput(true, null); + this.setOutputShape(Blockly.OUTPUT_SHAPE_SQUARE) } else { if (this.getReturn() === Blockly.PROCEDURES_CALL_TYPE_BOOLEAN) { this.setOutput(true, null); diff --git a/core/block_render_svg_vertical.js b/core/block_render_svg_vertical.js index 6aa3d1eb8e..ec525ece51 100644 --- a/core/block_render_svg_vertical.js +++ b/core/block_render_svg_vertical.js @@ -729,6 +729,7 @@ Blockly.BlockSvg.prototype.render = function(opt_bubble) { */ Blockly.BlockSvg.prototype.renderFields_ = function(fieldList, cursorX, cursorY) { + if (this.edgeShape_ && this.inputList.find(v => v.type == Blockly.NEXT_STATEMENT)) cursorX += this.edgeShapeWidth_ + Blockly.BlockSvg.CORNER_RADIUS * 2 if (this.RTL) { cursorX = -cursorX; } @@ -1180,8 +1181,8 @@ Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) { if (this.outputConnection) { // Width of the curve/pointy-curve var shape = this.getOutputShape(); - if (shape === Blockly.OUTPUT_SHAPE_HEXAGONAL || shape === Blockly.OUTPUT_SHAPE_ROUND || shape === Blockly.OUTPUT_SHAPE_OBJECT) { - this.edgeShapeWidth_ = inputRows.bottomEdge / 2; + if (shape !== Blockly.OUTPUT_SHAPE_SQUARE) { + this.edgeShapeWidth_ = (inputRows.bottomEdge + this.inputList.filter(v => v.type == Blockly.NEXT_STATEMENT).length * Blockly.BlockSvg.NOTCH_WIDTH) / 2; this.edgeShape_ = shape; this.squareTopLeftCorner_ = true; } @@ -1340,6 +1341,9 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, if (this.previousConnection) { cursorX = Math.max(cursorX, Blockly.BlockSvg.INPUT_AND_FIELD_MIN_X); } + if (this.outputConnection && (input.connection.targetConnection ? input.connection.targetConnection.getSourceBlock().getOutputShape() : input.connection.getOutputShape()) === Blockly.OUTPUT_SHAPE_SQUARE && this.getOutputShape() !== Blockly.OUTPUT_SHAPE_SQUARE) { + cursorX = Math.max(cursorX, this.edgeShapeWidth_) + } connectionX = this.RTL ? -cursorX : cursorX; // Attempt to center the connection vertically. var connectionYOffset = row.height / 2; @@ -1354,13 +1358,13 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, cursorX += row.paddingEnd; // Update right edge for all inputs, such that all rows // stretch to be at least the size of all previous rows. - inputRows.rightEdge = Math.max(cursorX, inputRows.rightEdge); + inputRows.rightEdge = Math.max(cursorX, inputRows.rightEdge, this.inputList.find(v => v.type == Blockly.NEXT_STATEMENT) ? Blockly.BlockSvg.MIN_BLOCK_X_WITH_STATEMENT + this.edgeShapeWidth_ : 0); // Move to the right edge cursorX = Math.max(cursorX, inputRows.rightEdge); this.width = Math.max(this.width, cursorX); - if (!this.edgeShape_) { + if (!this.edgeShape_ || this.inputList.find(v => v.type == Blockly.NEXT_STATEMENT)) { // Include corner radius in drawing the horizontal line. - steps.push('H', cursorX - Blockly.BlockSvg.CORNER_RADIUS - this.edgeShapeWidth_); + steps.push('H', cursorX - Blockly.BlockSvg.CORNER_RADIUS); steps.push(Blockly.BlockSvg.TOP_RIGHT_CORNER); } else { // Don't include corner radius - no corner (edge shape drawn). @@ -1368,7 +1372,7 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, } // Subtract CORNER_RADIUS * 2 to account for the top right corner // and also the bottom right corner. Only move vertically the non-corner length. - if (!this.edgeShape_) { + if (!this.edgeShape_ || this.inputList.find(v => v.type == Blockly.NEXT_STATEMENT)) { steps.push('v', row.height - Blockly.BlockSvg.CORNER_RADIUS * 2); } } else if (row.type == Blockly.NEXT_STATEMENT) { @@ -1387,7 +1391,7 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, this.renderDefineBlock_(steps, inputRows, input, row, cursorY); } else { Blockly.BlockSvg.drawStatementInputFromTopRight_(steps, cursorX, - inputRows.rightEdge, row); + inputRows.rightEdge, row, this); } // Create statement connection. @@ -1395,7 +1399,7 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, input.connection.setOffsetInBlock(connectionX, cursorY); if (input.connection.isConnected()) { this.width = Math.max(this.width, inputRows.statementEdge + - input.connection.targetBlock().getHeightWidth().width); + input.connection.targetBlock().getHeightWidth().width + (this.inputList.find(v => v.type == Blockly.NEXT_STATEMENT) ? this.edgeShapeWidth_ : 0)); } if (this.type != Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE && (y == inputRows.length - 1 || @@ -1414,6 +1418,7 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, cursorY = Blockly.BlockSvg.MIN_BLOCK_Y; steps.push('V', cursorY); } + if (this.edgeShape_ && this.inputList.find(v => v.type == Blockly.NEXT_STATEMENT)) steps[1] = `m ${cursorY / 2} 0` return cursorY; }; @@ -1483,7 +1488,7 @@ Blockly.BlockSvg.prototype.renderInputShape_ = function(input, x, y) { */ Blockly.BlockSvg.prototype.renderDrawBottom_ = function(steps, cursorY) { this.height = cursorY; - if (!this.edgeShape_) { + if (!this.edgeShape_ || this.inputList.find(v => v.type == Blockly.NEXT_STATEMENT)) { steps.push(Blockly.BlockSvg.BOTTOM_RIGHT_CORNER); } if (this.nextConnection) { @@ -1549,7 +1554,7 @@ Blockly.BlockSvg.prototype.renderDrawLeft_ = function(steps) { * @private */ Blockly.BlockSvg.prototype.drawEdgeShapeRight_ = function(steps) { - if (this.edgeShape_) { + if (this.edgeShape_ && !this.inputList.find(v => v.type == Blockly.NEXT_STATEMENT) ) { // Draw the right-side edge shape. if (this.edgeShape_ === Blockly.OUTPUT_SHAPE_ROUND) { // Draw a rounded arc. @@ -1605,8 +1610,8 @@ Blockly.BlockSvg.prototype.positionNewBlock = function(newBlock, newConnection, * @private */ Blockly.BlockSvg.drawStatementInputFromTopRight_ = function(steps, cursorX, - rightEdge, row) { - Blockly.BlockSvg.drawStatementInputTop_(steps, cursorX); + rightEdge, row, block) { + Blockly.BlockSvg.drawStatementInputTop_(steps, cursorX, block); steps.push('v', row.height - 2 * Blockly.BlockSvg.CORNER_RADIUS); Blockly.BlockSvg.drawStatementInputBottom_(steps, rightEdge, row); }; @@ -1619,10 +1624,10 @@ Blockly.BlockSvg.drawStatementInputFromTopRight_ = function(steps, cursorX, * of the input. * @private */ -Blockly.BlockSvg.drawStatementInputTop_ = function(steps, cursorX) { +Blockly.BlockSvg.drawStatementInputTop_ = function(steps, cursorX, block) { steps.push(Blockly.BlockSvg.BOTTOM_RIGHT_CORNER); steps.push('H', cursorX + Blockly.BlockSvg.STATEMENT_INPUT_INNER_SPACE + - 2 * Blockly.BlockSvg.CORNER_RADIUS); + 2 * Blockly.BlockSvg.CORNER_RADIUS + block.edgeShapeWidth_); steps.push(Blockly.BlockSvg.NOTCH_PATH_RIGHT); steps.push('h', '-' + Blockly.BlockSvg.STATEMENT_INPUT_INNER_SPACE); steps.push(Blockly.BlockSvg.INNER_TOP_LEFT_CORNER); @@ -1781,6 +1786,7 @@ Blockly.BlockSvg.getAlignedCursor_ = function(cursorX, input, rightEdge) { */ Blockly.BlockSvg.prototype.renderMoveConnections_ = function() { var blockTL = this.getRelativeToSurfaceXY(); + var branchedReporterTL = blockTL.clone().translate(this.edgeShapeWidth_, 0); // Don't tighten previous or output connections because they are inferior. if (this.previousConnection) { this.previousConnection.moveToOffset(blockTL); @@ -1793,7 +1799,7 @@ Blockly.BlockSvg.prototype.renderMoveConnections_ = function() { var inp = this.inputList[i]; var conn = inp.connection; if (conn) { - conn.moveToOffset(blockTL); + conn.moveToOffset(this.inputList[i].type == Blockly.NEXT_STATEMENT ? branchedReporterTL : blockTL); if (conn.isConnected()) { conn.tighten_(); } From 7f4e5667ee7f71d1be40d9504dd81a7441fb8312 Mon Sep 17 00:00:00 2001 From: jwklong Date: Sun, 16 Nov 2025 17:22:44 +0000 Subject: [PATCH 6/9] make proc inputs be red --- blocks_vertical/procedures.js | 26 +++++--------------------- core/css.js | 4 ++++ core/field_textinput.js | 2 +- core/field_textinput_removable.js | 6 ++++++ 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 193eacafbb..157d1910a0 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -1235,11 +1235,7 @@ Blockly.Blocks['argument_editor_boolean'] = { "text": "foo" } ], - "colour": Blockly.Colours.textField, - "colourSecondary": Blockly.Colours.textField, - "colourTertiary": Blockly.Colours.textField, - "colourQuaternary": Blockly.Colours.textField, - "extensions": ["output_boolean"] + "extensions": ["colours_more", "output_boolean"] }); }, // Exist on declaration and arguments editors, with different implementations. @@ -1256,11 +1252,7 @@ Blockly.Blocks['argument_editor_object'] = { "text": "foo" } ], - "colour": Blockly.Colours.textField, - "colourSecondary": Blockly.Colours.textField, - "colourTertiary": Blockly.Colours.textField, - "colourQuaternary": Blockly.Colours.textField, - "extensions": ["output_object"] + "extensions": ["colours_more", "output_object"] }); }, // Exist on declaration and arguments editors, with different implementations. @@ -1277,11 +1269,7 @@ Blockly.Blocks['argument_editor_array'] = { "text": "foo" } ], - "colour": Blockly.Colours.textField, - "colourSecondary": Blockly.Colours.textField, - "colourTertiary": Blockly.Colours.textField, - "colourQuaternary": Blockly.Colours.textField, - "extensions": ["output_array"] + "extensions": ["colours_more", "output_array"] }); }, // Exist on declaration and arguments editors, with different implementations. @@ -1298,7 +1286,7 @@ Blockly.Blocks['argument_editor_statement'] = { "text": "foo" } ], - "extensions": ["shape_statement", "colours_more"] + "extensions": ["colours_more", "shape_statement"] }); }, // Exist on declaration and arguments editors, with different implementations. @@ -1315,11 +1303,7 @@ Blockly.Blocks['argument_editor_string_number'] = { "text": "foo" } ], - "colour": Blockly.Colours.textField, - "colourSecondary": Blockly.Colours.textField, - "colourTertiary": Blockly.Colours.textField, - "colourQuaternary": Blockly.Colours.textField, - "extensions": ["output_number", "output_string"] + "extensions": ["colours_more", "output_number", "output_string"] }); }, // Exist on declaration and arguments editors, with different implementations. diff --git a/core/css.js b/core/css.js index b076d03829..9aa7b69708 100644 --- a/core/css.js +++ b/core/css.js @@ -472,6 +472,10 @@ Blockly.Css.CONTENT = [ 'font-weight: 500;', '}', + '.blocklyText.removableTextInput {', + 'fill: #fff;', + '}', + '.blocklyCheckbox {', 'fill: $colour_textFieldText;', '}', diff --git a/core/field_textinput.js b/core/field_textinput.js index 8978100a78..03276cf9b1 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -132,7 +132,7 @@ Blockly.FieldTextInput.prototype.init = function() { 'y': 0, 'width': this.size_.width, 'height': this.size_.height, - 'fill': this.sourceBlock_.getColourTertiary() + 'fill': this.sourceBlock_.getColourSecondary() } ); this.fieldGroup_.insertBefore(this.box_, this.textElement_); diff --git a/core/field_textinput_removable.js b/core/field_textinput_removable.js index a37e071845..1a399e553d 100644 --- a/core/field_textinput_removable.js +++ b/core/field_textinput_removable.js @@ -53,6 +53,12 @@ Blockly.FieldTextInputRemovable = function(text, opt_validator, opt_restrictor) }; goog.inherits(Blockly.FieldTextInputRemovable, Blockly.FieldTextInput); +Blockly.FieldTextInputRemovable.prototype.init = function() { + Blockly.FieldTextInputRemovable.superClass_.init.call(this); + + this.textElement_.classList.add('removableTextInput'); +} + /** * Show the inline free-text editor on top of the text with the remove button. * @private From 4e23ba3efb5c8d6c711351a2904e4146d02dc7b5 Mon Sep 17 00:00:00 2001 From: jwklong <72522395+jwklong@users.noreply.github.com> Date: Sun, 16 Nov 2025 18:16:48 +0000 Subject: [PATCH 7/9] do the fix --- blocks_vertical/procedures.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 157d1910a0..20ea7c0d80 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -895,18 +895,19 @@ Blockly.ScratchBlocks.ProcedureUtils.removeFieldCallback = function(field) { return; } var inputNameToRemove = null; + const cannotRemove = (i) => i == 0 && this.inputList[1].type == Blockly.NEXT_STATEMENT for (var n = 0; n < this.inputList.length; n++) { var input = this.inputList[n]; if (input.connection) { var target = input.connection.targetBlock(); if (target.getField(field.name) == field) { + if (cannotRemove(n)) return inputNameToRemove = input.name; } } else { - for (var j = 0; j < input.fieldRow.length; j++) { - if (input.fieldRow[j] == field) { - inputNameToRemove = input.name; - } + if (input.fieldRow[0] == field) { + if (cannotRemove(n)) return + inputNameToRemove = input.name; } } } From 9e3e25d235cbbe8d0008d51329c2cd5bdd70f2af Mon Sep 17 00:00:00 2001 From: jwklong Date: Mon, 17 Nov 2025 17:02:53 +0000 Subject: [PATCH 8/9] fix buggy branched reporter fields --- core/block_render_svg_vertical.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/block_render_svg_vertical.js b/core/block_render_svg_vertical.js index ec525ece51..c2a6e260ab 100644 --- a/core/block_render_svg_vertical.js +++ b/core/block_render_svg_vertical.js @@ -729,7 +729,6 @@ Blockly.BlockSvg.prototype.render = function(opt_bubble) { */ Blockly.BlockSvg.prototype.renderFields_ = function(fieldList, cursorX, cursorY) { - if (this.edgeShape_ && this.inputList.find(v => v.type == Blockly.NEXT_STATEMENT)) cursorX += this.edgeShapeWidth_ + Blockly.BlockSvg.CORNER_RADIUS * 2 if (this.RTL) { cursorX = -cursorX; } @@ -1317,6 +1316,7 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, var connectionX, connectionY; for (var y = 0, row; row = inputRows[y]; y++) { cursorX = row.paddingStart; + if (this.edgeShape_ && this.inputList.find(v => v.type == Blockly.NEXT_STATEMENT)) cursorX += this.edgeShapeWidth_ + Blockly.BlockSvg.CORNER_RADIUS * 2 if (y == 0) { cursorX += this.RTL ? -iconWidth : iconWidth; } From 0ec78e19c2999fa7ee6227728a4f7a7a3428add5 Mon Sep 17 00:00:00 2001 From: jwklong <72522395+jwklong@users.noreply.github.com> Date: Fri, 5 Dec 2025 16:39:15 +0000 Subject: [PATCH 9/9] add substack to branch ids --- blocks_vertical/procedures.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 20ea7c0d80..b92275c44d 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -837,7 +837,7 @@ Blockly.ScratchBlocks.ProcedureUtils.addStatementExternal = function() { Blockly.WidgetDiv.hide(true); this.procCode_ = this.procCode_ + ' %c'; this.displayNames_.push('statement'); - this.argumentIds_.push(Blockly.utils.genUid()); + this.argumentIds_.push('SUBSTACK' + Blockly.utils.genUid()); this.argumentDefaults_.push(''); this.updateDisplay_(); this.focusLastEditor_();