Skip to content
65 changes: 64 additions & 1 deletion blocks_vertical/procedures.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,17 @@ Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation = function(xmlEleme
this.procCode_ = xmlElement.getAttribute('proccode');
this.warp_ = JSON.parse(xmlElement.getAttribute('warp'));

var prevArgIds = this.argumentIds_;
var prevDisplayNames = this.displayNames_;

this.argumentIds_ = JSON.parse(xmlElement.getAttribute('argumentids'));
this.displayNames_ = JSON.parse(xmlElement.getAttribute('argumentnames'));
this.argumentDefaults_ = JSON.parse(
xmlElement.getAttribute('argumentdefaults'));
this.updateDisplay_();
if (this.updateArgumentReporterNames_) {
this.updateArgumentReporterNames_(prevArgIds, prevDisplayNames);
}
};

// End of serialization and deserialization.
Expand Down Expand Up @@ -708,6 +714,62 @@ Blockly.ScratchBlocks.ProcedureUtils.removeArgumentCallback_ = function(
}
};

/**
* Update argument reporter field values after an edit to the prototype mutation
* using previous argument ids and names.
* Because the argument reporters only store names and not which argument ids they
* are linked to, it would not be safe to update all argument reporters on the workspace
* since they may be argument reporters with the same name from a different procedure.
* Until there is a more explicit way of identifying argument reporter blocks using ids,
* be conservative and only update argument reporters that are used in the
* stack below the prototype, ie the definition.
* @param {!Array<string>} prevArgIds The previous ordering of argument ids.
* @param {!Array<string>} prevDisplayNames The previous argument names.
* @this Blockly.Block
*/
Blockly.ScratchBlocks.ProcedureUtils.updateArgumentReporterNames_ = function(prevArgIds, prevDisplayNames) {
var nameChanges = [];
var argReporters = [];
var definitionBlock = this.getParent();
if (!definitionBlock) return;

// Create a list of argument reporters that are descendants of the definition stack (see above comment)
var allBlocks = definitionBlock.getDescendants(false);
for (var i = 0; i < allBlocks.length; i++) {
var block = allBlocks[i];
if ((block.type === 'argument_reporter_string_number' ||
block.type === 'argument_reporter_boolean') &&
!block.isShadow()) { // Exclude arg reporters in the prototype block, which are shadows.
argReporters.push(block);
}
}

// Create a list of "name changes", including the new name and blocks matching the old name
// Only search over the current set of argument ids, ignore args that have been removed
for (var i = 0, id; id = this.argumentIds_[i]; i++) {
// Find the previous index of this argument id. Could be -1 if it is newly added.
var prevIndex = prevArgIds.indexOf(id);
if (prevIndex == -1) continue; // Newly added argument, no corresponding previous argument to update.
var prevName = prevDisplayNames[prevIndex];
if (prevName != this.displayNames_[i]) {
nameChanges.push({
newName: this.displayNames_[i],
blocks: argReporters.filter(function(block) {
return block.getFieldValue('VALUE') == prevName;
})
});
}
}

// Finally update the blocks for each name change.
// Do this after creating the lists to avoid cycles of renaming.
for (var j = 0, nameChange; nameChange = nameChanges[j]; j++) {
for (var k = 0, block; block = nameChange.blocks[k]; k++) {
block.setFieldValue(nameChange.newName, 'VALUE');
}
}
};

Blockly.Blocks['procedures_definition'] = {
/**
* Block for defining a procedure with no return value.
Expand Down Expand Up @@ -792,7 +854,8 @@ Blockly.Blocks['procedures_prototype'] = {
addProcedureLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelField_,

// Only exists on procedures_prototype.
createArgumentReporter_: Blockly.ScratchBlocks.ProcedureUtils.createArgumentReporter_
createArgumentReporter_: Blockly.ScratchBlocks.ProcedureUtils.createArgumentReporter_,
updateArgumentReporterNames_: Blockly.ScratchBlocks.ProcedureUtils.updateArgumentReporterNames_
};

Blockly.Blocks['procedures_declaration'] = {
Expand Down
26 changes: 22 additions & 4 deletions core/block_transformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Blockly.BlockTransformer.prototype.executeAction = function (action) {
let result = true;
try {
result = this.apply(action);
Blockly.Events.fireNow_();
} catch (err) {
throw "failed to apply transformation:" +
JSON.stringify(action)
Expand All @@ -40,7 +41,8 @@ Blockly.BlockTransformer.prototype.VarDeclareAction = function (action) {
type: "var_create",
varType: "",
varName: action.name,
varId: action.id
varId: action.id,
isLocal: action.target==='_stage_'?false:true
};

let varCreateEvent = new Blockly.Events.VarCreate(null);
Expand All @@ -50,10 +52,10 @@ Blockly.BlockTransformer.prototype.VarDeclareAction = function (action) {
return true;
}

Blockly.BlockTransformer.prototype.VarRename = function (action) {
Blockly.BlockTransformer.prototype.VarRenameAction = function (action) {
let varRenameJson = {
type: "var_rename",
varId: action.id,
varId: action.var_id,
oldName: action.oldName,
newName: action.newName
};
Expand All @@ -80,7 +82,7 @@ Blockly.BlockTransformer.prototype.InsertBlockAction = function (action) {
try {
let targetBlock = this.workspace.getBlockById(action.target_block);
let insertedBlock = this.workspace.getBlockById(action.inserted_block);
let previousBlock = targetBlock.previousConnection.targetBlock();
let previousBlock = targetBlock.previousConnection?targetBlock.previousConnection.targetBlock():null;

let moveEventJsonSpec = null;

Expand Down Expand Up @@ -211,5 +213,21 @@ Blockly.BlockTransformer.prototype.ReplaceAction = function (action) {

};

Blockly.BlockTransformer.prototype.VarDeleteAction = function (action) {
let varDeleteJson = {
type: "var_delete",
varId: action.var_id,
// var_name: action.var_name,
// target: action.target
};

let varDeleteEvent = new Blockly.Events.VarDelete(null);
varDeleteEvent.id = action.var_id;
varDeleteEvent.fromJson(varDeleteJson);
varDeleteEvent.workspaceId = this.workspace.id;
varDeleteEvent.run(true);
return true;
}

// Blockly.Workspace.prototype.createVariable
// Blockly.Workspace.prototype.deleteVariableById
2 changes: 1 addition & 1 deletion core/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -1033,7 +1033,7 @@ Blockly.Css.CONTENT = [
'outline: none;',
'padding: 4px 0;',
'position: absolute;',
'overflow-y: auto;',
'overflow-y: hidden;',
'overflow-x: hidden;',
'z-index: 20000;', /* Arbitrary, but some apps depend on it... */
'}',
Expand Down
1 change: 1 addition & 0 deletions core/gesture.js
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@ Blockly.Gesture.prototype.handleUp = function(e) {
this.doBubbleClick_();
} else if (this.isFieldClick_()) {
this.doFieldClick_();
this.startWorkspace_.setActiveFieldValue(this.startField_.sourceBlock_); //for contextualized hints
} else if (this.isBlockClick_()) {
this.doBlockClick_();
} else if (this.isWorkspaceClick_()) {
Expand Down
74 changes: 74 additions & 0 deletions core/inject.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,80 @@ Blockly.createDom_ = function(container, options) {

// copy ends

// another copy
var focusBlocksGlowFilter = Blockly.utils.createSvgElement('filter',
{'id': 'blocklyFocusBlocksGlowFilter',
'height': '160%',
'width': '180%',
y: '-30%',
x: '-40%'
},
defs);

options.focusBlocksGlowBlur = Blockly.utils.createSvgElement('feGaussianBlur',
{'in': 'SourceGraphic',
'stdDeviation': 1.5},
focusBlocksGlowFilter);
// Set all gaussian blur pixels to 1 opacity before applying flood
var componentTransfer2 = Blockly.utils.createSvgElement('feComponentTransfer', {'result': 'outBlur'}, focusBlocksGlowFilter);
Blockly.utils.createSvgElement('feFuncA',
{'type': 'table',
'tableValues': '0' + goog.string.repeat(' 1', 16)},
componentTransfer2);
// Color the highlight
Blockly.utils.createSvgElement('feFlood',
{'flood-color': 'lightgreen',
'flood-opacity': Blockly.Colours.stackGlowOpacity,
'result': 'outColor'},
focusBlocksGlowFilter);
Blockly.utils.createSvgElement('feComposite',
{'in': 'outColor',
'in2': 'outBlur',
'operator': 'in',
'result': 'outGlow'},
focusBlocksGlowFilter);
Blockly.utils.createSvgElement('feComposite',
{'in': 'SourceGraphic', 'in2': 'outGlow', 'operator': 'over'}, focusBlocksGlowFilter);
// copy end

// another copy
var focusBlocksStackGlowFilter = Blockly.utils.createSvgElement('filter',
{'id': 'focusBlocksStackGlowFilter',
'height': '160%',
'width': '180%',
y: '-30%',
x: '-40%'
},
defs);

options.focusBlocksStackGlowBlur = Blockly.utils.createSvgElement('feGaussianBlur',
{'in': 'SourceGraphic',
'stdDeviation': 4},
focusBlocksStackGlowFilter);
// Set all gaussian blur pixels to 1 opacity before applying flood
var componentTransfer2 = Blockly.utils.createSvgElement('feComponentTransfer', {'result': 'outBlur'}, focusBlocksStackGlowFilter);
Blockly.utils.createSvgElement('feFuncA',
{'type': 'table',
'tableValues': '0' + goog.string.repeat(' 1', 16)},
componentTransfer2);
// Color the highlight
Blockly.utils.createSvgElement('feFlood',
{'flood-color': 'lightgreen',
'flood-opacity': Blockly.Colours.stackGlowOpacity,
'result': 'outColor'},
focusBlocksStackGlowFilter);
Blockly.utils.createSvgElement('feComposite',
{'in': 'outColor',
'in2': 'outBlur',
'operator': 'in',
'result': 'outGlow'},
focusBlocksStackGlowFilter);
Blockly.utils.createSvgElement('feComposite',
{'in': 'SourceGraphic', 'in2': 'outGlow', 'operator': 'over'}, focusBlocksStackGlowFilter);


// copy end


// Filter for replacement marker
var replacementGlowFilter = Blockly.utils.createSvgElement('filter',
Expand Down
73 changes: 72 additions & 1 deletion core/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -976,7 +976,7 @@ Blockly.utils.getBoundingPath = function(block1, block2=null){
var substack = block2.inputList[1].connection ? block2.inputList[1].connection.targetConnection.getSourceBlock() : block2.inputList[2].connection.targetConnection.getSourceBlock();
var newTranslate = parseInt(substack.getSvgRoot().getAttribute("transform").replace("translate(","").split(",")[0]);
d = d + " H" + pd2[2] + Blockly.utils.pathBetween(substack, null, newTranslate)
+ " H" + pd2[4];
+ " H" + pd2[5];
d = d + " H" + pd2[6] + " H" + pd2[7];
} else if(block2Type == "if_else"){
var substack1 = block2.inputList[2].connection.targetConnection.getSourceBlock();
Expand All @@ -994,6 +994,77 @@ Blockly.utils.getBoundingPath = function(block1, block2=null){
return d;
};


Blockly.utils.getBoundingPathForControlBlock = function(block){
var d = "";
var pd1 = block.svgPath_.getAttribute("d").split(" H");

d = d + pd1[0] + " H" + pd1[1];
var blockType = block.type.replace("control_","");
d = d + Blockly.utils.pathBetweenForControlBlock(block,null, true);
if(["if","repeat","repeat_until","forever"].includes(blockType)){
if(["if"].includes(blockType)){
d = d + " H" + pd1[5] + " H" + pd1[6];
}else if(["forever"].includes(blockType)){
d = d + " H" + pd1[6];
}else if(["repeat"].includes(blockType)){
d = d + " H" + pd1[6] + " H"+ pd1[7];
}
else{
d = d + " H" + pd1[6] + " H" + pd1[7];
}
} else if(blockType == "if_else"){
d = d + " H" + pd1[8] + " H" + pd1[9];
} else {

}
return d;
};

Blockly.utils.pathBetweenForControlBlock = function(block1, translate=0, topMost){
var d = "";
var block = block1;
while (block != null){
var pd = block.svgPath_.getAttribute("d").split(" H");
pd[2] = pd[2].replace(pd[2].split(" ")[1],parseInt(pd[2].split(" ")[1]) + translate);
var blockType = block.type.replace("control_","");
if(["repeat","repeat_until","forever"].includes(blockType)){
pd[5] = pd[5].replace(pd[5].split(" ")[1],parseInt(pd[5].split(" ")[1]) + translate )
var substack = block.inputList[1].connection ? block.inputList[1].connection.targetConnection.getSourceBlock() : block.inputList[2].connection.targetConnection.getSourceBlock();
var newTranslate = parseInt(substack.getSvgRoot().getAttribute("transform").replace("translate(","").split(",")[0]) + translate;
d = d + " H" + pd[2] + Blockly.utils.pathBetweenForControlBlock(substack, newTranslate, false)
+ " H" + pd[5];
} else if(blockType == "if"){
pd[4] = pd[4].replace(pd[4].split(" ")[1],parseInt(pd[4].split(" ")[1]) + translate )
var substack = block.inputList[1].connection ? block.inputList[1].connection.targetConnection.getSourceBlock() : block.inputList[2].connection.targetConnection.getSourceBlock();
var newTranslate = parseInt(substack.getSvgRoot().getAttribute("transform").replace("translate(","").split(",")[0]) + translate;
d = d + " H" + pd[2] + Blockly.utils.pathBetweenForControlBlock(substack, newTranslate, false)
+ " H" + pd[4];
} else if(blockType == "if_else"){
pd[5] = pd[5].replace(pd[5].split(" ")[1],parseInt(pd[5].split(" ")[1]) + translate )
pd[7] = pd[7].replace(pd[7].split(" ")[1],parseInt(pd[7].split(" ")[1]) + translate )
var substack1 = block.inputList[2].connection.targetConnection.getSourceBlock();
var substack2 = block.inputList[4].connection.targetConnection.getSourceBlock();
var newTranslate1 = parseInt(substack1.getSvgRoot().getAttribute("transform").replace("translate(","").split(",")[0]) + translate;
var newTranslate2 = parseInt(substack2.getSvgRoot().getAttribute("transform").replace("translate(","").split(",")[0]) + translate;
d = d + " H" + pd[2] + Blockly.utils.pathBetweenForControlBlock(substack1, newTranslate1, false)
+ " H" + pd[5] + Blockly.utils.pathBetweenForControlBlock(substack2, newTranslate2, false)
+ " H" + pd[7];
} else {
d = d + " H" + pd[2];
}

if(!topMost){
block = block.getNextBlock();
}else{
block = null;
}
}

return d;
};


/**
*Returns path string for nested blocks
*/
Expand Down
4 changes: 4 additions & 0 deletions core/workspace.js
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,10 @@ Blockly.Workspace.prototype.showHint = function(){
}
}

Blockly.Workspace.prototype.setActiveFieldValue = function(shadowBlock){
this.workspaceHint.setActiveFieldValue(shadowBlock);
}

// Export symbols that would otherwise be renamed by Closure compiler.
Blockly.Workspace.prototype['clear'] = Blockly.Workspace.prototype.clear;
Blockly.Workspace.prototype['clearUndo'] =
Expand Down
6 changes: 6 additions & 0 deletions core/workspace_hint.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ goog.require('Blockly.Icon');

Blockly.WorkspaceHint = function (workspace) {
this.workspace_ = workspace;
this.activeFieldValue = null;
};

Blockly.WorkspaceHint.prototype.WIDTH_ = 20;
Expand Down Expand Up @@ -131,4 +132,9 @@ Blockly.WorkspaceHint.prototype.showContextMenu_ = function (e) {

Blockly.ContextMenu.show(e, menuOptions, this.workspace_.RTL);

}

Blockly.WorkspaceHint.prototype.setActiveFieldValue = function (shadowBlock) {
// set activeField (shadowId) attribute in workspaceHint: shadowBlock.id
this.activeFieldValue = shadowBlock;
}
Loading