diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 373e3e42f9..19fe551290 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -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. @@ -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} prevArgIds The previous ordering of argument ids. + * @param {!Array} 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. @@ -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'] = { diff --git a/core/block_transformer.js b/core/block_transformer.js index 74036e676e..a76e60ab21 100644 --- a/core/block_transformer.js +++ b/core/block_transformer.js @@ -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) @@ -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); @@ -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 }; @@ -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; @@ -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 \ No newline at end of file diff --git a/core/css.js b/core/css.js index a630879938..95e5eaa80c 100644 --- a/core/css.js +++ b/core/css.js @@ -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... */ '}', diff --git a/core/gesture.js b/core/gesture.js index 6fde8771e7..daf1d640e6 100644 --- a/core/gesture.js +++ b/core/gesture.js @@ -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_()) { diff --git a/core/inject.js b/core/inject.js index 01f9d9c700..96ea8de8f3 100644 --- a/core/inject.js +++ b/core/inject.js @@ -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', diff --git a/core/utils.js b/core/utils.js index 8b47816d20..1c28de01db 100644 --- a/core/utils.js +++ b/core/utils.js @@ -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(); @@ -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 */ diff --git a/core/workspace.js b/core/workspace.js index bb514653c8..26785c7f44 100644 --- a/core/workspace.js +++ b/core/workspace.js @@ -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'] = diff --git a/core/workspace_hint.js b/core/workspace_hint.js index 0923270952..27f432d683 100644 --- a/core/workspace_hint.js +++ b/core/workspace_hint.js @@ -9,6 +9,7 @@ goog.require('Blockly.Icon'); Blockly.WorkspaceHint = function (workspace) { this.workspace_ = workspace; + this.activeFieldValue = null; }; Blockly.WorkspaceHint.prototype.WIDTH_ = 20; @@ -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; } \ No newline at end of file diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 39ab1eb5d7..a7784bec3d 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -995,10 +995,29 @@ Blockly.WorkspaceSvg.prototype.glowMessyBlock = function(ids, isMessyGlowingBloc * @param {?string} start id ID of block to find. * @param {?string} (optional) end id ID of block to find. */ -Blockly.WorkspaceSvg.prototype.drawHighlightBox = function(id, id2=null) { +Blockly.WorkspaceSvg.prototype.drawHighlightBox = function (id, id2 = null, options) { + var svg = null; var block1 = null, block2 = null; + if (id) { block1 = this.getBlockById(id); + if (block1.getFirstStatementConnection()&&!block2) { + svg = Blockly.utils.createSvgElement('path', + { + 'd': Blockly.utils.getBoundingPathForControlBlock(block1), + 'class': 'blocklyBlockBackground', + 'fill': 'black', + 'fill-opacity': '0', + 'stroke': options?options.color:'lightgreen', + 'stroke-width': options?'7px':'1px' + }, + block1.getSvgRoot()); + if(!options){ + svg.setAttribute('filter', 'url(#' + 'blocklyFocusBlocksGlowFilter' + ')'); + } + this.highlightBoxs_.push(svg); + return; + } if (!block1) { throw 'Tried to highlight block that does not exist.'; } @@ -1009,27 +1028,63 @@ Blockly.WorkspaceSvg.prototype.drawHighlightBox = function(id, id2=null) { throw 'Tried to highlight block that does not exist.'; } } - this.highlightBoxs_.push(Blockly.utils.createSvgElement('path', - { - 'd': Blockly.utils.getBoundingPath(block1,block2), - 'class': 'blocklyPath blocklyBlockBackground', - 'stroke': 'black', - 'style': 'stroke-width: 5px', - 'fill-opacity': '0', - 'fill': 'black' - }, - block1.getSvgRoot())); + + svg = Blockly.utils.createSvgElement('path', + { + 'd': Blockly.utils.getBoundingPath(block1, block2), + 'class': 'blocklyBlockBackground', + 'fill': 'black', + 'fill-opacity': '0', + 'stroke': 'lightgreen', + 'stroke-width': options?'7px':'1px' + }, + block1.getSvgRoot()); + if(!options){ + svg.setAttribute('filter', 'url(#' + 'blocklyFocusBlocksGlowFilter' + ')'); + } + + this.highlightBoxs_.push(svg); + }; + /** * Remove all highlighting boxes for given block/blocks in the workspace. */ -Blockly.WorkspaceSvg.prototype.removeHighlightBox = function() { - for ( var i=0; i900036810361610"; + fromXml(); + } + + function highlightBlocksSetup(){ + var input = document.getElementById('importExport'); + input.value = + // "1090101500" + //"101010_mouse_1010" + // "1010" + // "10_mouse_" + "15101010" + ; fromXml(); - Blockly.selected = workspace.getBlockById('*`#p/~R4kNb@uT|}iU@u'); - if (Blockly.selected) { - Blockly.selected.setHintText("abc"); - Blockly.selected.hint.setVisible(true); - } - - } +// Blockly.selected = workspace.getBlockById('*`#p/~R4kNb@uT|}iU@u'); +// if (Blockly.selected) { +// Blockly.selected.setHintText("abc"); +// Blockly.selected.hint.setVisible(true); +// } + // workspace.drawHighlightBox('`!ttdW7uQ~+?pJ#xb@n`','1TQo0bqgwh%R+%QOA50o'); + workspace.drawHighlightBox('Dy;gx(37]2W3a!e[wJQY','|e^K(I@{c_CD)8fhn%+`') + // workspace.drawHighlightBox('target',null,{color:'#3C91E6'}) + } + + function highlighFieldSetup(){ + var input = document.getElementById('importExport'); + input.value = + "COLOR185" + ; + fromXml(); + const shadowId = "qPI5(v*?$FK4-a6xxFY2"; + var block = workspace.getBlockById("N$(lwZyb{)yHn|!6*sDM"); + window.block = block; + + // highlight + workspace.highlightField('qPI5(v*?$FK4-a6xxFY2'); + // workspace.unHighlightField('qPI5(v*?$FK4-a6xxFY2'); + + } function setLocale(locale) { workspace.getFlyout().setRecyclingEnabled(false); @@ -600,5 +646,6 @@

Vertical Blocks

+