From 23832a18b9d3a98fe57b8518bf143aabf3afb595 Mon Sep 17 00:00:00 2001 From: Prapti Khawas Date: Mon, 16 Jul 2018 13:13:58 -0400 Subject: [PATCH 01/23] Adding methods for improvables --- core/block_svg.js | 58 +++++++++++- core/colours.js | 1 + core/flyout_base.js | 44 +++++++++ core/inject.js | 40 ++++++++ core/workspace_svg.js | 207 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 348 insertions(+), 2 deletions(-) diff --git a/core/block_svg.js b/core/block_svg.js index b6fe0812ef..19a7277122 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -215,9 +215,34 @@ Blockly.BlockSvg.prototype.unselect = function() { * Glow only this particular block, to highlight it visually as if it's running. * @param {boolean} isGlowingBlock Whether the block should glow. */ -Blockly.BlockSvg.prototype.setGlowBlock = function(isGlowingBlock) { +Blockly.BlockSvg.prototype.setGlowBlock = function(isGlowingBlock, refactor=false) { this.isGlowingBlock_ = isGlowingBlock; - this.updateColour(); + + if( refactor === false ){ + this.updateColour(); + } + else{ + // Update the applied SVG filter if the property has changed + var svg = this.getSvgRoot(); + if (this.isGlowingBlock_ && !svg.hasAttribute('filter')) { + svg.setAttribute('filter', 'url(#blocklyMessyBlockGlowFilter)'); + } else if (!this.isGlowingBlock_ && svg.hasAttribute('filter')) { + svg.removeAttribute('filter'); + } + } +} + +/** + * Set whether the block is highlighted or not. + * @param {boolean} highlighted True if highlighted. + */ +Blockly.BlockSvg.prototype.setHighlighted = function(highlighted) { + var svg = this.getSvgRoot(); + if (highlighted && !svg.hasAttribute('filter')) { + svg.setAttribute('filter', 'url(#blocklyMessyBlockGlowFilter)'); + } else if (svg.hasAttribute('filter')) { + svg.removeAttribute('filter'); + } }; /** @@ -378,6 +403,35 @@ Blockly.BlockSvg.prototype.moveBy = function(dx, dy) { this.workspace.resizeContents(); }; +/** + * Move a block along with its connected blocks and attach it to given parent + * @param {Blockly.BlockSvg} parent + */ +Blockly.BlockSvg.prototype.attachToParent = function(parent) { + goog.asserts.assert(parent, 'Parent not provided'); + this.parentBlock_=null; + this.workspace.addTopBlock(this); + this.setParent(parent); +}; + + +/** + * Connect block to given block + * Note: any block below this block will be disconnected + * @param {Blockly.BlockSvg} block + */ +Blockly.BlockSvg.prototype.connectToBlock = function(block) { + var previousConn = this.nextConnection; + if(previousConn.targetConnection) previousConn.disconnect(); + var nextConn = block.previousConnection; + if(nextConn && nextConn.targetConnection) nextConn.disconnect(); + if (previousConn.checkType_(nextConn)) { + // Attach the given block to this block. + previousConn.connect(nextConn); + } +}; + + /** * Transforms a block by setting the translation on the transform attribute * of the block's SVG. diff --git a/core/colours.js b/core/colours.js index 4936ce6b59..3d97e7b5f8 100644 --- a/core/colours.js +++ b/core/colours.js @@ -99,6 +99,7 @@ Blockly.Colours = { "stackGlowSize": 4, "stackGlowOpacity": 1, "replacementGlow": "#FFFFFF", + "messyBlockGlow": "#ff0000", "replacementGlowSize": 2, "replacementGlowOpacity": 1, "colourPickerStroke": "#FFFFFF", diff --git a/core/flyout_base.js b/core/flyout_base.js index c297620259..92ee15e643 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -142,6 +142,14 @@ Blockly.Flyout = function(workspaceOptions) { */ this.recycleBlocks_ = []; + /** + * List of current highlight boxes drawn in flyout. Highlight boxes are often used to + * visually mark certain group of variables. + * @type !Array. + * @private + */ + this.highlightBoxs_ = []; + }; /** @@ -884,3 +892,39 @@ Blockly.Flyout.prototype.recycleBlock_ = function(block) { block.moveBy(-xy.x, -xy.y); this.recycleBlocks_.push(block); }; + +/** + * Display highlighting box for given variables in the workspace. + * @param {array} ids IDs of variables to find. + */ +Blockly.Flyout.prototype.drawHighlightBox = function(ids) { + for ( var i=0; i< ids.length; i++ ) { + var id = ids[i]; + var block = null; + var height = 0; + if (id) { + block = this.workspace_.getBlockById(id); + if (!block) { + throw 'Tried to highlight variable that does not exist.'; + } + } + height = block.getHeightWidth().height; + this.highlightBoxs_.push(Blockly.utils.createSvgElement('rect', + {'height': height * this.workspace_.scale, + 'width' : (block.getHeightWidth().width+10) * this.workspace_.scale, + 'style' : "fill: none;stroke-width:5;stroke:rgb(255,0,0);", + 'x' : (block.getBoundingRectangle().topLeft.x-5) * this.workspace_.scale, + 'y' : (block.getBoundingRectangle().topLeft.y-this.getScrollPos()) * this.workspace_.scale + }, + this.svgGroup_)); + } +}; + +/** + * Remove all highlighting boxes for given variables in the workspace. + */ +Blockly.Flyout.prototype.removeHighlightBox = function() { + for ( var i=0; i + * @private + */ + this.highlightBoxs_ = []; + /** * Object in charge of loading, storing, and playing audio for a workspace. * @type {Blockly.WorkspaceAudio} @@ -909,6 +917,76 @@ Blockly.WorkspaceSvg.prototype.glowBlock = function(id, isGlowingBlock) { block.setGlowBlock(isGlowingBlock); }; +/** ++ * Glow/unglow a messy block in the workspace. ++ * @param {array} ids IDs of block to find. ++ * @param {boolean} isGlowingBlock Whether to glow the block. ++ */ +Blockly.WorkspaceSvg.prototype.glowMessyBlock = function(ids, isMessyGlowingBlock) { + + for ( var i=0; i< ids.length; i++ ) { + var id = ids[i]; + var block = null; + if (id) { + block = this.getBlockById(id); + if (!block) { + throw 'Tried to glow block that does not exist.'; + } + } + if(!isMessyGlowingBlock){ + Blockly.DropDownDiv.hideWithoutAnimation(); + } + block.setGlowBlock(isMessyGlowingBlock, true); + } +}; + + +/** + * Display highlighting box for given block/blocks in the workspace. + * @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) { + var block = null; + var height = 0; + if (id) { + block = this.getBlockById(id); + if (!block) { + throw 'Tried to highlight block that does not exist.'; + } + } + var lastBlock = null; + var block2 = null; + if(id2==null){ + lastBlock = block.getNextBlock(); + } else { + block2 = this.getBlockById(id2); + lastBlock = block2.getNextBlock(); + } + if(lastBlock==null){ + height = block.getHeightWidth().height; + } else { + height = block.getHeightWidth().height-lastBlock.getHeightWidth().height; + } + this.highlightBoxs_.push(Blockly.utils.createSvgElement('rect', + {'height': height, + 'width' : block.getHeightWidth().width+10, + 'style' : "fill: none;stroke-width:5;stroke:rgb(255,0,0);", + 'x' : block.getBoundingRectangle().topLeft.x-5, + 'y' : block.getBoundingRectangle().topLeft.y + }, + this.svgBlockCanvas_)); +}; + +/** + * Remove all highlighting boxes for given block/blocks in the workspace. + */ +Blockly.WorkspaceSvg.prototype.removeHighlightBox = function() { + for ( var i=0; i + * + * + * + * + */ +Blockly.WorkspaceSvg.prototype.explainHighlightBox = function(value, toPointVariables=0, noPath=0) { + + var d = 'M 0 0 l -20 -20 l 0 40 z'; + var transform = 'translate(735,300)'; + var str = ""; + if(toPointVariables==1){ + d = 'M 0 0 l 20 -20 l 0 40 z'; + transform = 'translate(295,600)'; + } + + this.drawBox(500,400); + + + this.highlightBoxs_.push(Blockly.utils.createSvgElement('image', + {'href':'http://research.cs.vt.edu/quality4blocks/img/quality-hound.png', + 'height': 180, + 'width' : 180, + 'x' : 575, + 'y' : 10 + }, + this.highlightBoxs_[this.highlightBoxs_.length-4])); + + var y = 250; + var width = 350; + var lineHeight = 10; + + + if(noPath==0){ + this.highlightBoxs_.push(Blockly.utils.createSvgElement('path', + {'d': d, + 'transform':transform + }, + this.highlightBoxs_[this.highlightBoxs_.length-5])); + } + + /** + this.highlightBoxs_.push(Blockly.utils.createSvgElement('text', + {'style' : "fill: black;font-family: 'Helvetica Neue', Helvetica, sans-serif;font-weight: bold;font-size:2rem;", + 'x' : 340, + 'y' : 200 + }, + this.svgGroup_)); + var textNode1 = document.createTextNode("**sniff** **sniff**"); + this.highlightBoxs_[this.highlightBoxs_.length - 1].appendChild(textNode1); + **/ + + /* split the words into array */ + var words = value.split(' '); + var line = ''; + + for (var i = 0; i < words.length; i++) { + var testLine = line + words[i] + ' '; + var isNewLine = (words[i]=="\n"); + var isLastWord = (i==words.length-1); + if(testLine.length>=45 || isNewLine || isLastWord){ + this.highlightBoxs_.push(Blockly.utils.createSvgElement('text', + {'style' : "fill: black;font-family: 'Helvetica Neue', Helvetica, sans-serif;font-weight: bold;font-size:0.95rem;", + 'x' : 340, + 'y' : y + }, + this.svgGroup_)); + if(isLastWord) line = line + words[i]; + var textNode = document.createTextNode(line); + this.highlightBoxs_[this.highlightBoxs_.length - 1].appendChild(textNode); + line = (isNewLine)? '' : words[i] + ' '; + y = (isNewLine)? y+40 : y+20; + } else { + line = line + words[i] + ' '; + } + } + +}; + +/** + * To draw basic box for pop up with options g for adding buttons etc. + * Will always display to the left + * @param {?string} value String value to visually report. + * + * + * + * + */ +Blockly.WorkspaceSvg.prototype.drawBox = function(height, width){ + this.highlightBoxs_.push(Blockly.utils.createSvgElement('g', + { + }, + this.svgGroup_)); + + this.highlightBoxs_.push(Blockly.utils.createSvgElement('rect', + {'height': height, + 'width' : width, + 'rx' : 15, + 'ry' : 15, + 'style' : "fill: rgb(240,248,255);stroke-width:5;stroke:rgb(0,0,0);", + 'x' : 315, + 'y' : 150 + }, + this.highlightBoxs_[this.highlightBoxs_.length-1])); + + this.highlightBoxs_.push(Blockly.utils.createSvgElement('g', + {'id': 'options', + 'transform': 'translate(350,400)' + }, + this.highlightBoxs_[this.highlightBoxs_.length-2])); + + this.highlightBoxs_.push(Blockly.utils.createSvgElement('g', + {'id': 'options2', + 'transform': 'translate(320,200)' + }, + this.highlightBoxs_[this.highlightBoxs_.length-3])); +} + + /** * Paste the provided block onto the workspace. * @param {!Element} xmlBlock XML block element. From 1f35e25ac7d32af0c3758b51ee56bcafab6d51be Mon Sep 17 00:00:00 2001 From: Peeratham Techapalokul Date: Fri, 1 Feb 2019 21:57:46 -0500 Subject: [PATCH 02/23] handle hint mouseover --- core/block_events.js | 3 ++- core/hint.js | 9 ++++++++- core/hint_bubble.js | 11 +++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/core/block_events.js b/core/block_events.js index 9f7e0b4073..bd5155c22c 100644 --- a/core/block_events.js +++ b/core/block_events.js @@ -538,13 +538,14 @@ Blockly.Events.Move.prototype.run = function(forward) { * @extends {Blockly.Events.BlockBase} * @constructor */ -Blockly.Events.HintClick = function(hint) { +Blockly.Events.HintClick = function(hint, interactionType) { if (!hint) { return; // Blank event to be populated by fromJson. } Blockly.Events.HintClick.superClass_.constructor.call(this); this.hintId = hint.getText(); this.recordUndo = false; + this.interactionType = interactionType; }; goog.inherits(Blockly.Events.HintClick, Blockly.Events.Abstract); diff --git a/core/hint.js b/core/hint.js index acb80f6b7f..fc888d6ef8 100644 --- a/core/hint.js +++ b/core/hint.js @@ -145,6 +145,7 @@ Blockly.Hint.prototype.setVisible = function(visible) { content, this.block_.svgPath_, this.iconXY_, null, null); // specific for hint this.bubble_.registerContextMenuCallback(this.showContextMenu_.bind(this)); + this.bubble_.registerMouseOverCallback(this.showCodeHint_.bind(this)); if (this.block_.RTL) { // Right-align the paragraph. @@ -181,6 +182,12 @@ Blockly.Hint.prototype.showContextMenu_ = function(e) { Blockly.ContextMenu.show(e, menuOptions, this.block_.RTL); }; +Blockly.Hint.prototype.showCodeHint_ = function(e) { + var event = new Blockly.Events.HintClick(this,"mouseover"); + event.workspaceId = this.block_.workspace.id; + Blockly.Events.fire(event); +} + /** * Make a context menu option for action resolving the hint * @param {!Blockly.Hint} hint The hint where the @@ -195,7 +202,7 @@ Blockly.Hint.hintImproveOption = function(hint) { enabled: true, callback: function() { console.log("Hint callback to improve get invoked"); - var event = new Blockly.Events.HintClick(hint); + var event = new Blockly.Events.HintClick(hint, "improve_option_click"); event.workspaceId = wsId; Blockly.Events.fire(event); } diff --git a/core/hint_bubble.js b/core/hint_bubble.js index f015e610f2..fbc85dc053 100644 --- a/core/hint_bubble.js +++ b/core/hint_bubble.js @@ -73,6 +73,7 @@ Blockly.HintBubble = function(workspace, content, shape, anchorXY, this.rendered_ = true; if (!workspace.options.readOnly) { + Blockly.bindEvent_(this.content_, 'mouseover', this, this.bubbleMouseOver_); Blockly.bindEventWithChecks_( this.content_, 'mousedown', this, this.bubbleMouseDown_); } @@ -87,6 +88,10 @@ Blockly.HintBubble.prototype.registerContextMenuCallback = function(callback) { this.contextMenuCallback_ = callback; }; +Blockly.HintBubble.prototype.registerMouseOverCallback = function(callback) { + this.mouseoverCallback_ = callback; +}; + /** * Width of the border around the bubble. */ @@ -281,6 +286,12 @@ Blockly.HintBubble.prototype.showContextMenu_ = function(_e) { } }; +Blockly.HintBubble.prototype.bubbleMouseOver_ = function(_e){ + if(this.mouseoverCallback_){ + this.mouseoverCallback_(_e); + } +} + /** * Get whether this bubble is deletable or not. * @return {boolean} True if deletable. From 0d711ec2e11ce6e66245ad5d18cd13ec883d6f51 Mon Sep 17 00:00:00 2001 From: thesourabh Date: Tue, 5 Feb 2019 13:37:09 -0500 Subject: [PATCH 03/23] Code Hint: Added mouseover and mouseout handling --- core/hint.js | 12 +++++++++++- core/hint_bubble.js | 15 +++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/core/hint.js b/core/hint.js index fc888d6ef8..dacd2b584a 100644 --- a/core/hint.js +++ b/core/hint.js @@ -146,6 +146,7 @@ Blockly.Hint.prototype.setVisible = function(visible) { // specific for hint this.bubble_.registerContextMenuCallback(this.showContextMenu_.bind(this)); this.bubble_.registerMouseOverCallback(this.showCodeHint_.bind(this)); + this.bubble_.registerMouseOutCallback(this.hideCodeHint_.bind(this)); if (this.block_.RTL) { // Right-align the paragraph. @@ -186,7 +187,16 @@ Blockly.Hint.prototype.showCodeHint_ = function(e) { var event = new Blockly.Events.HintClick(this,"mouseover"); event.workspaceId = this.block_.workspace.id; Blockly.Events.fire(event); -} + // this.block_.workspace.highlightBlock(this.block_.id, true); + +}; +Blockly.Hint.prototype.hideCodeHint_ = function(e) { + var event = new Blockly.Events.HintClick(this,"mouseout"); + event.workspaceId = this.block_.workspace.id; + Blockly.Events.fire(event); + // this.block_.workspace.highlightBlock(this.block_.id, false); + +}; /** * Make a context menu option for action resolving the hint diff --git a/core/hint_bubble.js b/core/hint_bubble.js index fbc85dc053..e4939b8b8e 100644 --- a/core/hint_bubble.js +++ b/core/hint_bubble.js @@ -74,6 +74,7 @@ Blockly.HintBubble = function(workspace, content, shape, anchorXY, if (!workspace.options.readOnly) { Blockly.bindEvent_(this.content_, 'mouseover', this, this.bubbleMouseOver_); + Blockly.bindEvent_(this.content_, 'mouseout', this, this.bubbleMouseOut_); Blockly.bindEventWithChecks_( this.content_, 'mousedown', this, this.bubbleMouseDown_); } @@ -92,6 +93,10 @@ Blockly.HintBubble.prototype.registerMouseOverCallback = function(callback) { this.mouseoverCallback_ = callback; }; +Blockly.HintBubble.prototype.registerMouseOutCallback = function(callback) { + this.mouseoutCallback_ = callback; +}; + /** * Width of the border around the bubble. */ @@ -232,7 +237,7 @@ Blockly.HintBubble.prototype.createDom_ = function(content, hasResize) { */ this.bubbleGroup_ = Blockly.utils.createSvgElement('g', {}, null); this.resizeGroup_ = null; - + this.bubbleGroup_.appendChild(content); return this.bubbleGroup_; }; @@ -290,7 +295,13 @@ Blockly.HintBubble.prototype.bubbleMouseOver_ = function(_e){ if(this.mouseoverCallback_){ this.mouseoverCallback_(_e); } -} +}; + +Blockly.HintBubble.prototype.bubbleMouseOut_ = function(_e){ + if(this.mouseoutCallback_){ + this.mouseoutCallback_(_e); + } +}; /** * Get whether this bubble is deletable or not. From da561e12060cfa7f8e64562731bf7c5ed7f74deb Mon Sep 17 00:00:00 2001 From: thesourabh Date: Fri, 22 Feb 2019 17:36:35 -0500 Subject: [PATCH 04/23] Added two-step edit procedure option for extracted procedure --- core/block_transformer.js | 13 +++++++++---- core/hint.js | 32 ++++++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/core/block_transformer.js b/core/block_transformer.js index dfc0b186ff..74036e676e 100644 --- a/core/block_transformer.js +++ b/core/block_transformer.js @@ -19,19 +19,20 @@ Blockly.BlockTransformer.prototype.doTransform = function (refactorable) { }; Blockly.BlockTransformer.prototype.executeAction = function (action) { + let result = true; try { - this.apply(action); + result = this.apply(action); } catch (err) { throw "failed to apply transformation:" + JSON.stringify(action) + "\n" + err.message; } - return true; + return result; }; Blockly.BlockTransformer.prototype.apply = function (action) { const actionType = action.type; - this[actionType](action); + return this[actionType](action); }; Blockly.BlockTransformer.prototype.VarDeclareAction = function (action) { @@ -67,7 +68,11 @@ Blockly.BlockTransformer.prototype.VarRename = function (action) { Blockly.BlockTransformer.prototype.BlockCreateAction = function (action) { let dom = Blockly.Xml.textToDom(action.block_xml).firstChild; let block = Blockly.Xml.domToBlock(dom, this.workspace); - return true; + if (block.type === "procedures_definition") { + return block; + // Blockly.Procedures.editProcedureCallback_(block); + } + return true }; Blockly.BlockTransformer.prototype.InsertBlockAction = function (action) { diff --git a/core/hint.js b/core/hint.js index dacd2b584a..a2a61eb606 100644 --- a/core/hint.js +++ b/core/hint.js @@ -129,7 +129,7 @@ Blockly.Hint.prototype.setBubbleSize = function(width, height) { * Show or hide the hint bubble. * @param {boolean} visible True if the bubble should be visible. */ -Blockly.Hint.prototype.setVisible = function(visible) { +Blockly.Hint.prototype.setVisible = function(visible, hintType) { if (visible == this.isVisible()) { // No change. return; @@ -144,9 +144,13 @@ Blockly.Hint.prototype.setVisible = function(visible) { /** @type {!Blockly.WorkspaceSvg} */ (this.block_.workspace), content, this.block_.svgPath_, this.iconXY_, null, null); // specific for hint - this.bubble_.registerContextMenuCallback(this.showContextMenu_.bind(this)); - this.bubble_.registerMouseOverCallback(this.showCodeHint_.bind(this)); - this.bubble_.registerMouseOutCallback(this.hideCodeHint_.bind(this)); + if (hintType === "edit_procedure") { + this.bubble_.registerContextMenuCallback(this.showEditContextMenu_.bind(this)); + } else { + this.bubble_.registerContextMenuCallback(this.showContextMenu_.bind(this)); + this.bubble_.registerMouseOverCallback(this.showCodeHint_.bind(this)); + this.bubble_.registerMouseOutCallback(this.hideCodeHint_.bind(this)); + } if (this.block_.RTL) { // Right-align the paragraph. @@ -169,6 +173,26 @@ Blockly.Hint.prototype.setVisible = function(visible) { } }; +/** + * Show the context menu for this comment's bubble. + * @param {!Event} e The mouse event + * @private + */ +Blockly.Hint.prototype.showEditContextMenu_ = function(e) { + var menuOptions = []; + var block = this.block_; + var _this = this; + menuOptions.push({ + enabled: true, + text: Blockly.Msg.EDIT_PROCEDURE, + callback: function() { + Blockly.Procedures.editProcedureCallback_(block); + _this.setVisible(false, "edit_procedure"); + } + }); + Blockly.ContextMenu.show(e, menuOptions, block.RTL); +}; + /** * Show the context menu for this comment's bubble. * @param {!Event} e The mouse event From 8476a169e127d34bd5e6b868598dc16036358588 Mon Sep 17 00:00:00 2001 From: Peeratham Techapalokul Date: Mon, 4 Mar 2019 13:41:42 -0500 Subject: [PATCH 05/23] compact xml export --- tests/vertical_playground_compact_xml.html | 613 +++++++++++++++++++++ 1 file changed, 613 insertions(+) create mode 100644 tests/vertical_playground_compact_xml.html diff --git a/tests/vertical_playground_compact_xml.html b/tests/vertical_playground_compact_xml.html new file mode 100644 index 0000000000..e2f2261a43 --- /dev/null +++ b/tests/vertical_playground_compact_xml.html @@ -0,0 +1,613 @@ + + + + + + + Vertical Playground + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +

Vertical Blocks

+

+ Show + - Hide +

+ +
+ + + + +
+ +

+ +   + +
+ +
+ +

+ +
+ +

+ Log events:   + +

+ +

+ Stress test:   + + +

+ +

+ Glows:   + + + + +

+ +

+ Log flyout events:   + +

+ +

+ Enable sounds (after refresh):   + +

+ +

+ + +

+ +

+ Report: + + +

+ + From bb88bfdab45aeda7b6605fd572582707cab88b85 Mon Sep 17 00:00:00 2001 From: thesourabh Date: Mon, 4 Mar 2019 19:27:51 -0500 Subject: [PATCH 06/23] Added Prapti's code for highlighting a block with a fixed end block. --- core/utils.js | 77 +++++++++++++++++++++++++++++++++++++++++++ core/workspace_svg.js | 38 +++++++++------------ 2 files changed, 93 insertions(+), 22 deletions(-) diff --git a/core/utils.js b/core/utils.js index 6106df17a4..8b47816d20 100644 --- a/core/utils.js +++ b/core/utils.js @@ -958,3 +958,80 @@ Blockly.utils.startsWith = function(str, prefix) { Blockly.utils.toRadians = function(angleDegrees) { return angleDegrees * Math.PI / 180; }; + +/** + *Returns path string which bounds the blocks between block1 and block2 (inclusive) + */ +Blockly.utils.getBoundingPath = function(block1, block2=null){ + if(block2 == null){ + return block1.svgPath_.getAttribute("d"); + } + var d = ""; + var pd1 = block1.svgPath_.getAttribute("d").split(" H"); + var pd2 = block2.svgPath_.getAttribute("d").split(" H"); + d = d + pd1[0] + " H" + pd1[1]; + var block2Type = block2.type.replace("control_",""); + d = d + Blockly.utils.pathBetween(block1,block2); + if(["if","repeat","repeat_until","forever"].includes(block2Type)){ + 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]; + d = d + " H" + pd2[6] + " H" + pd2[7]; + } else if(block2Type == "if_else"){ + var substack1 = block2.inputList[2].connection.targetConnection.getSourceBlock(); + var substack2 = block2.inputList[4].connection.targetConnection.getSourceBlock(); + var newTranslate1 = parseInt(substack1.getSvgRoot().getAttribute("transform").replace("translate(","").split(",")[0]); + var newTranslate2 = parseInt(substack2.getSvgRoot().getAttribute("transform").replace("translate(","").split(",")[0]); + d = d + " H" + pd2[2] + Blockly.utils.pathBetween(substack1, null, newTranslate1) + + " H" + pd2[5] + Blockly.utils.pathBetween(substack2, null, newTranslate2) + + " H" + pd2[7]; + d = d + " H" + pd2[8] + " H" + pd2[9]; + } else { + d = d + " H" + pd2[2]; + d = d + " H" + pd2[3] + " H" + pd2[4]; + } + return d; +}; + +/** + *Returns path string for nested blocks + */ +Blockly.utils.pathBetween = function(block1, block2, translate=0){ + var d = ""; + var block = block1; + while (block != null && block != block2){ + 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.pathBetween(substack, null, newTranslate) + + " 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.pathBetween(substack, null, newTranslate) + + " 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.pathBetween(substack1, null, newTranslate1) + + " H" + pd[5] + Blockly.utils.pathBetween(substack2, null, newTranslate2) + + " H" + pd[7]; + } else if (block.type.includes("event_when")){ + d = d ; + } else { + d = d + " H" + pd[2]; + } + block = block.getNextBlock(); + } + return d; +}; diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 2704235c58..1433682d76 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -992,35 +992,29 @@ Blockly.WorkspaceSvg.prototype.glowMessyBlock = function(ids, isMessyGlowingBloc * @param {?string} (optional) end id ID of block to find. */ Blockly.WorkspaceSvg.prototype.drawHighlightBox = function(id, id2=null) { - var block = null; - var height = 0; + var block1 = null, block2 = null; if (id) { - block = this.getBlockById(id); - if (!block) { + block1 = this.getBlockById(id); + if (!block1) { throw 'Tried to highlight block that does not exist.'; } } - var lastBlock = null; - var block2 = null; - if(id2==null){ - lastBlock = block.getNextBlock(); - } else { + if (id2) { block2 = this.getBlockById(id2); - lastBlock = block2.getNextBlock(); - } - if(lastBlock==null){ - height = block.getHeightWidth().height; - } else { - height = block.getHeightWidth().height-lastBlock.getHeightWidth().height; + if (!block2) { + throw 'Tried to highlight block that does not exist.'; + } } - this.highlightBoxs_.push(Blockly.utils.createSvgElement('rect', - {'height': height, - 'width' : block.getHeightWidth().width+10, - 'style' : "fill: none;stroke-width:5;stroke:rgb(255,0,0);", - 'x' : block.getBoundingRectangle().topLeft.x-5, - 'y' : block.getBoundingRectangle().topLeft.y + 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' }, - this.svgBlockCanvas_)); + block1.getSvgRoot())); }; /** From 4cd574f1ac7720dcf43c59769d62425592eeeef3 Mon Sep 17 00:00:00 2001 From: Peeratham Techapalokul Date: Tue, 5 Mar 2019 21:15:02 -0500 Subject: [PATCH 07/23] add workspace hint and positioning --- core/workspace.js | 8 +++- core/workspace_hint.js | 85 ++++++++++++++++++++++++++++++++++++++++++ core/workspace_svg.js | 4 ++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 core/workspace_hint.js diff --git a/core/workspace.js b/core/workspace.js index 3bc5f6bfcd..4e2a456d4b 100644 --- a/core/workspace.js +++ b/core/workspace.js @@ -32,7 +32,7 @@ goog.require('goog.array'); goog.require('goog.math'); goog.require('Blockly.BlockTransformer'); - +goog.require('Blockly.WorkspaceHint'); /** * Class for a workspace. This is a data structure that contains blocks. * There is no UI, and can be created headlessly. @@ -669,6 +669,11 @@ Blockly.Workspace.getById = function(id) { return Blockly.Workspace.WorkspaceDB_[id] || null; }; +Blockly.Workspace.prototype.createHint = function() { + this.workspaceHint_ = new Blockly.WorkspaceHint(this); +} + + // Export symbols that would otherwise be renamed by Closure compiler. Blockly.Workspace.prototype['clear'] = Blockly.Workspace.prototype.clear; Blockly.Workspace.prototype['clearUndo'] = @@ -677,3 +682,4 @@ Blockly.Workspace.prototype['addChangeListener'] = Blockly.Workspace.prototype.addChangeListener; Blockly.Workspace.prototype['removeChangeListener'] = Blockly.Workspace.prototype.removeChangeListener; + diff --git a/core/workspace_hint.js b/core/workspace_hint.js new file mode 100644 index 0000000000..befeaebf91 --- /dev/null +++ b/core/workspace_hint.js @@ -0,0 +1,85 @@ +'use strict'; + +goog.provide('Blockly.WorkspaceHint'); + +goog.require('Blockly.Events.Ui'); +goog.require('Blockly.Icon'); + + + +Blockly.WorkspaceHint = function (workspace) { + this.workspace_ = workspace; + this.createHint(); +}; + +Blockly.WorkspaceHint.prototype.WIDTH_ = 20; +Blockly.WorkspaceHint.prototype.HEIGHT_ = 20; +Blockly.WorkspaceHint.prototype.MARGIN_TOP_ = 30; +Blockly.WorkspaceHint.prototype.MARGIN_BOTTOM_ = 12; +Blockly.WorkspaceHint.prototype.MARGIN_SIDE_ = 12; +Blockly.WorkspaceHint.prototype.HEIGHT_ = 124; + +Blockly.WorkspaceHint.prototype.createHint = function () { + console.log("todo: create hint"); + this.iconGroup_ = this.createHintIcon_(); + this.workspace_.svgGroup_.appendChild(this.iconGroup_); + this.position(); + Blockly.bindEventWithChecks_( + this.iconGroup_, 'mouseup', this, + function () { + console.log("clicked"); + } + // this.iconClick_ + ); +}; + +Blockly.WorkspaceHint.prototype.position = function () { + this.bottom_ = this.MARGIN_BOTTOM_; + var metrics = this.workspace_.getMetrics(); + if (!metrics) { + // There are no metrics available (workspace is probably not visible). + return; + } + if (this.workspace_.RTL) { + this.left_ = this.MARGIN_SIDE_ + Blockly.Scrollbar.scrollbarThickness; + if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { + this.left_ += metrics.flyoutWidth; + if (this.workspace_.toolbox_) { + this.left_ += metrics.absoluteLeft; + } + } + } else { + this.left_ = metrics.viewWidth + metrics.absoluteLeft - + this.WIDTH_ - this.MARGIN_SIDE_ - Blockly.Scrollbar.scrollbarThickness; + + if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { + this.left_ -= metrics.flyoutWidth; + } + } + + this.top_ = metrics.absoluteTop + this.MARGIN_TOP_; + + + this.iconGroup_.setAttribute('transform', + 'translate(' + this.left_ + ',' + this.top_ + ')'); + +} + +Blockly.WorkspaceHint.prototype.createHintIcon_ = function () { + let iconGroup_ = Blockly.utils.createSvgElement('g', { 'class': 'blocklyIconGroup' }, null); + let WIDTH_ = 8 * Blockly.BlockSvg.GRID_UNIT; + let HEIGHT_ = 8 * Blockly.BlockSvg.GRID_UNIT; + let lightbulbSvgPath = '/icons/set-led_yellow.svg'; + var lightbulbSvg = Blockly.utils.createSvgElement( + 'image', + { + 'width': WIDTH_, + 'height': HEIGHT_ + }, + iconGroup_ + ); + lightbulbSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', + this.workspace_.options.pathToMedia + lightbulbSvgPath); + + return iconGroup_; +} diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 1433682d76..39ab1eb5d7 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -708,6 +708,10 @@ Blockly.WorkspaceSvg.prototype.resize = function() { if (this.scrollbar) { this.scrollbar.resize(); } + if (this.workspaceHint_) { + this.workspaceHint_.position(); + } + this.updateScreenCalculations_(); }; From 4e30f3b6df32c91b87e997e5e9c25b48ec749aea Mon Sep 17 00:00:00 2001 From: Peeratham Techapalokul Date: Tue, 5 Mar 2019 22:07:09 -0500 Subject: [PATCH 08/23] add context menu --- core/block_events.js | 2 +- core/gesture.js | 16 ++++++++++++++ core/workspace_hint.js | 47 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/core/block_events.js b/core/block_events.js index bd5155c22c..31b8b8a38b 100644 --- a/core/block_events.js +++ b/core/block_events.js @@ -543,7 +543,7 @@ Blockly.Events.HintClick = function(hint, interactionType) { return; // Blank event to be populated by fromJson. } Blockly.Events.HintClick.superClass_.constructor.call(this); - this.hintId = hint.getText(); + this.hintId = hint.getText()||hint.getId(); this.recordUndo = false; this.interactionType = interactionType; }; diff --git a/core/gesture.js b/core/gesture.js index 1e9ec9a49d..6fde8771e7 100644 --- a/core/gesture.js +++ b/core/gesture.js @@ -640,6 +640,8 @@ Blockly.Gesture.prototype.handleRightClick = function(e) { this.targetBlock_.showContextMenu_(e); } else if (this.startBubble_) { this.startBubble_.showContextMenu_(e); + } else if (this.startWorkspaceHint_) { + this.startWorkspaceHint_.showContextMenu_(e); } else if (this.startWorkspace_ && !this.flyout_) { Blockly.hideChaff(); this.startWorkspace_.showContextMenu_(e); @@ -709,6 +711,14 @@ Blockly.Gesture.prototype.handleBubbleStart = function(e, bubble) { this.mostRecentEvent_ = e; }; +Blockly.Gesture.prototype.handleWorkspaceHintStart = function(e, hint) { + goog.asserts.assert(!this.hasStarted_, + 'Tried to call gesture.handleWorkspaceHintStart, but the gesture had already ' + + 'been started.'); + this.setStartWorkspaceHint(hint); + this.mostRecentEvent_ = e; +}; + /* Begin functions defining what actions to take to execute clicks on each type * of target. Any developer wanting to add behaviour on clicks should modify * only this code. */ @@ -818,6 +828,12 @@ Blockly.Gesture.prototype.setStartBubble = function(bubble) { } }; +Blockly.Gesture.prototype.setStartWorkspaceHint = function(hint) { + if (!this.startHint_) { + this.startWorkspaceHint_ = hint; + } +}; + /** * Record the block that a gesture started on, and set the target block * appropriately. diff --git a/core/workspace_hint.js b/core/workspace_hint.js index befeaebf91..1844e2cf1b 100644 --- a/core/workspace_hint.js +++ b/core/workspace_hint.js @@ -19,20 +19,25 @@ Blockly.WorkspaceHint.prototype.MARGIN_BOTTOM_ = 12; Blockly.WorkspaceHint.prototype.MARGIN_SIDE_ = 12; Blockly.WorkspaceHint.prototype.HEIGHT_ = 124; +Blockly.WorkspaceHint.prototype.getId = function () { + return "workspaceHintId"; +} + +Blockly.WorkspaceHint.prototype.getText = function () { + return null; +} + Blockly.WorkspaceHint.prototype.createHint = function () { console.log("todo: create hint"); this.iconGroup_ = this.createHintIcon_(); this.workspace_.svgGroup_.appendChild(this.iconGroup_); this.position(); + Blockly.bindEventWithChecks_( - this.iconGroup_, 'mouseup', this, - function () { - console.log("clicked"); - } - // this.iconClick_ - ); + this.iconGroup_, 'mousedown', this, this.pathMouseDown_); }; + Blockly.WorkspaceHint.prototype.position = function () { this.bottom_ = this.MARGIN_BOTTOM_; var metrics = this.workspace_.getMetrics(); @@ -65,6 +70,13 @@ Blockly.WorkspaceHint.prototype.position = function () { } +Blockly.WorkspaceHint.prototype.pathMouseDown_ = function(e) { + var gesture = this.workspace_.getGesture(e); + if (gesture) { + gesture.handleWorkspaceHintStart(e, this); + } + }; + Blockly.WorkspaceHint.prototype.createHintIcon_ = function () { let iconGroup_ = Blockly.utils.createSvgElement('g', { 'class': 'blocklyIconGroup' }, null); let WIDTH_ = 8 * Blockly.BlockSvg.GRID_UNIT; @@ -83,3 +95,26 @@ Blockly.WorkspaceHint.prototype.createHintIcon_ = function () { return iconGroup_; } + +Blockly.WorkspaceHint.hintImproveOption = function(hint) { + let wsId = hint.workspace_.id; + var hintOption = { + text: "Help me improve!", + enabled: true, + callback: function() { + console.log("Hint callback to improve get invoked"); + var event = new Blockly.Events.HintClick(hint, "improve_option_click"); + event.workspaceId = wsId; + Blockly.Events.fire(event); + } + }; + return hintOption; + }; + +Blockly.WorkspaceHint.prototype.showContextMenu_ = function (e) { + var menuOptions = []; + menuOptions.push(Blockly.WorkspaceHint.hintImproveOption(this)); + + Blockly.ContextMenu.show(e, menuOptions, this.workspace_.RTL); + +} \ No newline at end of file From 14a188d87e64b110a08d0e1e2cbfd6052fcd8ab1 Mon Sep 17 00:00:00 2001 From: Peeratham Techapalokul Date: Wed, 6 Mar 2019 13:41:28 -0500 Subject: [PATCH 09/23] hide and show hint --- core/workspace.js | 19 +++++++++++++++++-- core/workspace_hint.js | 21 ++++++++++++++++++--- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/core/workspace.js b/core/workspace.js index 4e2a456d4b..bb514653c8 100644 --- a/core/workspace.js +++ b/core/workspace.js @@ -119,6 +119,7 @@ Blockly.Workspace = function(opt_options) { * BlockTransformer */ this.blockTransformer = new Blockly.BlockTransformer(this); + this.workspaceHint = new Blockly.WorkspaceHint(this); }; /** @@ -669,10 +670,24 @@ Blockly.Workspace.getById = function(id) { return Blockly.Workspace.WorkspaceDB_[id] || null; }; -Blockly.Workspace.prototype.createHint = function() { - this.workspaceHint_ = new Blockly.WorkspaceHint(this); + +Blockly.Workspace.prototype.setHint = function(hintData){ + if(this.workspaceHint){ + this.workspaceHint.setHint(hintData); + } } +Blockly.Workspace.prototype.hideHint = function(){ + if(this.workspaceHint){ + this.workspaceHint.setVisible(false); + } +} + +Blockly.Workspace.prototype.showHint = function(){ + if(this.workspaceHint){ + this.workspaceHint.setVisible(true); + } +} // Export symbols that would otherwise be renamed by Closure compiler. Blockly.Workspace.prototype['clear'] = Blockly.Workspace.prototype.clear; diff --git a/core/workspace_hint.js b/core/workspace_hint.js index 1844e2cf1b..5b997ab239 100644 --- a/core/workspace_hint.js +++ b/core/workspace_hint.js @@ -9,7 +9,6 @@ goog.require('Blockly.Icon'); Blockly.WorkspaceHint = function (workspace) { this.workspace_ = workspace; - this.createHint(); }; Blockly.WorkspaceHint.prototype.WIDTH_ = 20; @@ -20,15 +19,20 @@ Blockly.WorkspaceHint.prototype.MARGIN_SIDE_ = 12; Blockly.WorkspaceHint.prototype.HEIGHT_ = 124; Blockly.WorkspaceHint.prototype.getId = function () { - return "workspaceHintId"; + return this.id; } +/** + * To be removed, after Blockly.Hint + */ Blockly.WorkspaceHint.prototype.getText = function () { return null; } -Blockly.WorkspaceHint.prototype.createHint = function () { +Blockly.WorkspaceHint.prototype.setHint = function (hintData) { console.log("todo: create hint"); + this.hintData = hintData; + this.id = hintData.id; this.iconGroup_ = this.createHintIcon_(); this.workspace_.svgGroup_.appendChild(this.iconGroup_); this.position(); @@ -37,6 +41,17 @@ Blockly.WorkspaceHint.prototype.createHint = function () { this.iconGroup_, 'mousedown', this, this.pathMouseDown_); }; +Blockly.WorkspaceHint.prototype.setVisible = function(visible){ + if(visible){ + if(!this.iconGroup_ && this.hintData){ + this.setHint(this.hintData); + } + }else{ + goog.dom.removeNode(this.iconGroup_); + this.iconGroup_ = null; + } +} + Blockly.WorkspaceHint.prototype.position = function () { this.bottom_ = this.MARGIN_BOTTOM_; From 2c676c7fba30f1489be19c6a495cbce936399210 Mon Sep 17 00:00:00 2001 From: Peeratham Techapalokul Date: Wed, 6 Mar 2019 13:46:38 -0500 Subject: [PATCH 10/23] use id instead of hintData.id --- core/workspace_hint.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/workspace_hint.js b/core/workspace_hint.js index 5b997ab239..11164dbcb1 100644 --- a/core/workspace_hint.js +++ b/core/workspace_hint.js @@ -19,7 +19,7 @@ Blockly.WorkspaceHint.prototype.MARGIN_SIDE_ = 12; Blockly.WorkspaceHint.prototype.HEIGHT_ = 124; Blockly.WorkspaceHint.prototype.getId = function () { - return this.id; + return this.hintData.id; } /** @@ -32,7 +32,6 @@ Blockly.WorkspaceHint.prototype.getText = function () { Blockly.WorkspaceHint.prototype.setHint = function (hintData) { console.log("todo: create hint"); this.hintData = hintData; - this.id = hintData.id; this.iconGroup_ = this.createHintIcon_(); this.workspace_.svgGroup_.appendChild(this.iconGroup_); this.position(); From 000304ba1757d7f2c4f190b439b428481d4a15df Mon Sep 17 00:00:00 2001 From: Peeratham Techapalokul Date: Wed, 20 Mar 2019 22:19:09 -0400 Subject: [PATCH 11/23] workspace hint --- core/workspace_hint.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/workspace_hint.js b/core/workspace_hint.js index 11164dbcb1..0923270952 100644 --- a/core/workspace_hint.js +++ b/core/workspace_hint.js @@ -13,9 +13,9 @@ Blockly.WorkspaceHint = function (workspace) { Blockly.WorkspaceHint.prototype.WIDTH_ = 20; Blockly.WorkspaceHint.prototype.HEIGHT_ = 20; -Blockly.WorkspaceHint.prototype.MARGIN_TOP_ = 30; +Blockly.WorkspaceHint.prototype.MARGIN_TOP_ = 80; Blockly.WorkspaceHint.prototype.MARGIN_BOTTOM_ = 12; -Blockly.WorkspaceHint.prototype.MARGIN_SIDE_ = 12; +Blockly.WorkspaceHint.prototype.MARGIN_SIDE_ = 20; Blockly.WorkspaceHint.prototype.HEIGHT_ = 124; Blockly.WorkspaceHint.prototype.getId = function () { From 9cb59de31762118a9be7d41ece11d292ba75b255 Mon Sep 17 00:00:00 2001 From: Peeratham Techapalokul Date: Fri, 19 Apr 2019 18:28:42 -0400 Subject: [PATCH 12/23] improve hint highlighting --- core/inject.js | 74 +++++++++++++++++++++++++++++++++++++++++++ core/workspace_svg.js | 47 +++++++++++++++++++-------- 2 files changed, 107 insertions(+), 14 deletions(-) diff --git a/core/inject.js b/core/inject.js index 01f9d9c700..08ac391898 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': '#73C2FB', + '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': '#73C2FB', + '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/workspace_svg.js b/core/workspace_svg.js index 39ab1eb5d7..33ecc585b1 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -995,13 +995,20 @@ 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) { + var svg = null; var block1 = null, block2 = null; + if (id) { block1 = this.getBlockById(id); if (!block1) { throw 'Tried to highlight block that does not exist.'; } + if (block1.getFirstStatementConnection()) { + svg = block1.getSvgRoot(); + svg.setAttribute('filter', 'url(#' + 'focusBlocksStackGlowFilter' + ')'); + //just need to set filter as with other glow + } } if (id2) { block2 = this.getBlockById(id2); @@ -1009,24 +1016,36 @@ 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())); + if (!svg) { + svg = Blockly.utils.createSvgElement('path', + { + 'd': Blockly.utils.getBoundingPath(block1, block2), + 'class': 'blocklyBlockBackground', + 'fill': 'black', + 'fill-opacity': '0', + 'stroke': '#73C2FB', + // 'stroke-width': '1px', + }, + block1.getSvgRoot()); + 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; i Date: Fri, 19 Apr 2019 23:46:33 -0400 Subject: [PATCH 13/23] improve block highlighting when highlighting a single control block --- core/utils.js | 64 ++++++++++++++++++++++++++++++++++ core/workspace_svg.js | 29 +++++++++------ tests/vertical_playground.html | 25 ++++++++++--- 3 files changed, 104 insertions(+), 14 deletions(-) diff --git a/core/utils.js b/core/utils.js index 8b47816d20..64de42c49b 100644 --- a/core/utils.js +++ b/core/utils.js @@ -994,6 +994,70 @@ 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); + 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){ + var d = ""; + var block = block1; + + 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) + + " 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) + + " 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) + + " H" + pd[5] + Blockly.utils.pathBetweenForControlBlock(substack2, newTranslate2) + + " H" + pd[7]; + } else { + d = d + " H" + pd[2]; + } + + return d; +}; + + /** *Returns path string for nested blocks */ diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 33ecc585b1..71c8eb9081 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -1001,14 +1001,24 @@ Blockly.WorkspaceSvg.prototype.drawHighlightBox = function (id, id2 = 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': '#73C2FB' + }, + block1.getSvgRoot()); + svg.setAttribute('filter', 'url(#' + 'blocklyFocusBlocksGlowFilter' + ')'); + + this.highlightBoxs_.push(svg); + return; + } if (!block1) { throw 'Tried to highlight block that does not exist.'; } - if (block1.getFirstStatementConnection()) { - svg = block1.getSvgRoot(); - svg.setAttribute('filter', 'url(#' + 'focusBlocksStackGlowFilter' + ')'); - //just need to set filter as with other glow - } } if (id2) { block2 = this.getBlockById(id2); @@ -1016,19 +1026,18 @@ Blockly.WorkspaceSvg.prototype.drawHighlightBox = function (id, id2 = null) { throw 'Tried to highlight block that does not exist.'; } } - if (!svg) { + svg = Blockly.utils.createSvgElement('path', { - 'd': Blockly.utils.getBoundingPath(block1, block2), + 'd': Blockly.utils.getBoundingPath2(block1, block2), 'class': 'blocklyBlockBackground', 'fill': 'black', 'fill-opacity': '0', - 'stroke': '#73C2FB', - // 'stroke-width': '1px', + 'stroke': '#73C2FB' }, block1.getSvgRoot()); svg.setAttribute('filter', 'url(#' + 'blocklyFocusBlocksGlowFilter' + ')'); - } + this.highlightBoxs_.push(svg); }; diff --git a/tests/vertical_playground.html b/tests/vertical_playground.html index eb26d39024..ee20d96465 100644 --- a/tests/vertical_playground.html +++ b/tests/vertical_playground.html @@ -112,10 +112,23 @@ // Restore flyout event logging state. state = sessionStorage.getItem('logFlyoutEvents'); logFlyoutEvents(Boolean(state)); - showHintSetup(); + // showHintSetup(); + highlightBlocksSetup(); + svgSetup(); } } + function svgSetup(){ + var svg = document.getElementById('testSvg'); //Get svg element + var newElement = document.createElementNS("http://www.w3.org/2000/svg", 'path'); //Create a path in SVG's namespace + newElement.setAttribute("d","M 0 0 L 10 10"); //Set path's data + newElement.setAttribute("id", "testPath"); + newElement.style.stroke = "#000"; //Set stroke colour + newElement.style.strokeWidth = "5px"; //Set stroke width + svg.appendChild(newElement); + + } + function getToolboxElement() { var match = location.search.match(/toolbox=([^&]+)/); return document.getElementById('toolbox-' + (match ? match[1] : 'categories')); @@ -308,9 +321,12 @@ } } - function showHintSetup(){ + function highlightBlocksSetup(){ var input = document.getElementById('importExport'); - input.value = "10"; + input.value = + "1010" + // "10_mouse_" + ; fromXml(); Blockly.selected = workspace.getBlockById('*`#p/~R4kNb@uT|}iU@u'); @@ -318,7 +334,7 @@ Blockly.selected.setHintText("abc"); Blockly.selected.hint.setVisible(true); } - + workspace.drawHighlightBox('target') } function setLocale(locale) { @@ -600,5 +616,6 @@

Vertical Blocks

+ From cb12ae2b5ea0a1bb01ed1e060150ae63eade4636 Mon Sep 17 00:00:00 2001 From: Peeratham Techapalokul Date: Sat, 20 Apr 2019 00:11:46 -0400 Subject: [PATCH 14/23] fix bug highlighting block --- core/utils.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/core/utils.js b/core/utils.js index 64de42c49b..a2153baa89 100644 --- a/core/utils.js +++ b/core/utils.js @@ -1001,7 +1001,7 @@ Blockly.utils.getBoundingPathForControlBlock = function(block){ d = d + pd1[0] + " H" + pd1[1]; var blockType = block.type.replace("control_",""); - d = d + Blockly.utils.pathBetweenForControlBlock(block); + 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]; @@ -1021,10 +1021,10 @@ Blockly.utils.getBoundingPathForControlBlock = function(block){ return d; }; -Blockly.utils.pathBetweenForControlBlock = function(block1, translate=0){ +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_",""); @@ -1032,13 +1032,13 @@ Blockly.utils.pathBetweenForControlBlock = function(block1, translate=0){ 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) + 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) + 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 ) @@ -1047,12 +1047,19 @@ Blockly.utils.pathBetweenForControlBlock = function(block1, translate=0){ 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) - + " H" + pd[5] + Blockly.utils.pathBetweenForControlBlock(substack2, newTranslate2) + 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; }; From b5cd210927c837e30a865cd5984b7949e29984db Mon Sep 17 00:00:00 2001 From: Peeratham Techapalokul Date: Sat, 20 Apr 2019 18:49:57 -0400 Subject: [PATCH 15/23] add highlighting color as options --- core/workspace_svg.js | 16 ++++++++++------ tests/vertical_playground.html | 8 +++++--- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 71c8eb9081..45fd6ec585 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -995,7 +995,7 @@ 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; @@ -1008,11 +1008,13 @@ Blockly.WorkspaceSvg.prototype.drawHighlightBox = function (id, id2 = null) { 'class': 'blocklyBlockBackground', 'fill': 'black', 'fill-opacity': '0', - 'stroke': '#73C2FB' + 'stroke': options?options.color:'#73C2FB', + 'stroke-width': options?'7px':'1px' }, block1.getSvgRoot()); - svg.setAttribute('filter', 'url(#' + 'blocklyFocusBlocksGlowFilter' + ')'); - + if(!options){ + svg.setAttribute('filter', 'url(#' + 'blocklyFocusBlocksGlowFilter' + ')'); + } this.highlightBoxs_.push(svg); return; } @@ -1029,14 +1031,16 @@ Blockly.WorkspaceSvg.prototype.drawHighlightBox = function (id, id2 = null) { svg = Blockly.utils.createSvgElement('path', { - 'd': Blockly.utils.getBoundingPath2(block1, block2), + 'd': Blockly.utils.getBoundingPathForControlBlock(block1, block2), 'class': 'blocklyBlockBackground', 'fill': 'black', 'fill-opacity': '0', 'stroke': '#73C2FB' }, block1.getSvgRoot()); - svg.setAttribute('filter', 'url(#' + 'blocklyFocusBlocksGlowFilter' + ')'); + if(!options){ + svg.setAttribute('filter', 'url(#' + 'blocklyFocusBlocksGlowFilter' + ')'); + } this.highlightBoxs_.push(svg); diff --git a/tests/vertical_playground.html b/tests/vertical_playground.html index ee20d96465..62cea73ce7 100644 --- a/tests/vertical_playground.html +++ b/tests/vertical_playground.html @@ -323,8 +323,9 @@ function highlightBlocksSetup(){ var input = document.getElementById('importExport'); - input.value = - "1010" + input.value = + "101010_mouse_1010" + // "1010" // "10_mouse_" ; fromXml(); @@ -334,7 +335,8 @@ Blockly.selected.setHintText("abc"); Blockly.selected.hint.setVisible(true); } - workspace.drawHighlightBox('target') + workspace.drawHighlightBox('target',undefined,{color:'#92C124'}) + // workspace.drawHighlightBox('target',null,{color:'#3C91E6'}) } function setLocale(locale) { From 2f6ce264916ee04a3cdf76e3ac2faa7868cc17be Mon Sep 17 00:00:00 2001 From: Peeratham Techapalokul Date: Mon, 22 Apr 2019 12:33:08 -0400 Subject: [PATCH 16/23] fix bug highlighting --- core/inject.js | 4 ++-- core/workspace_svg.js | 7 ++++--- tests/vertical_playground.html | 15 ++++++++------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/core/inject.js b/core/inject.js index 08ac391898..96ea8de8f3 100644 --- a/core/inject.js +++ b/core/inject.js @@ -238,7 +238,7 @@ Blockly.createDom_ = function(container, options) { componentTransfer2); // Color the highlight Blockly.utils.createSvgElement('feFlood', - {'flood-color': '#73C2FB', + {'flood-color': 'lightgreen', 'flood-opacity': Blockly.Colours.stackGlowOpacity, 'result': 'outColor'}, focusBlocksGlowFilter); @@ -274,7 +274,7 @@ Blockly.createDom_ = function(container, options) { componentTransfer2); // Color the highlight Blockly.utils.createSvgElement('feFlood', - {'flood-color': '#73C2FB', + {'flood-color': 'lightgreen', 'flood-opacity': Blockly.Colours.stackGlowOpacity, 'result': 'outColor'}, focusBlocksStackGlowFilter); diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 45fd6ec585..9f86ba2272 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -1008,7 +1008,7 @@ Blockly.WorkspaceSvg.prototype.drawHighlightBox = function (id, id2 = null, opti 'class': 'blocklyBlockBackground', 'fill': 'black', 'fill-opacity': '0', - 'stroke': options?options.color:'#73C2FB', + 'stroke': options?options.color:'lightgreen', 'stroke-width': options?'7px':'1px' }, block1.getSvgRoot()); @@ -1031,11 +1031,12 @@ Blockly.WorkspaceSvg.prototype.drawHighlightBox = function (id, id2 = null, opti svg = Blockly.utils.createSvgElement('path', { - 'd': Blockly.utils.getBoundingPathForControlBlock(block1, block2), + 'd': Blockly.utils.getBoundingPath(block1, block2), 'class': 'blocklyBlockBackground', 'fill': 'black', 'fill-opacity': '0', - 'stroke': '#73C2FB' + 'stroke': 'lightgreen', + 'stroke-width': options?'7px':'1px' }, block1.getSvgRoot()); if(!options){ diff --git a/tests/vertical_playground.html b/tests/vertical_playground.html index 62cea73ce7..15a29b756e 100644 --- a/tests/vertical_playground.html +++ b/tests/vertical_playground.html @@ -324,18 +324,19 @@ function highlightBlocksSetup(){ var input = document.getElementById('importExport'); input.value = - "101010_mouse_1010" + "1090101500" + //"101010_mouse_1010" // "1010" // "10_mouse_" ; fromXml(); - Blockly.selected = workspace.getBlockById('*`#p/~R4kNb@uT|}iU@u'); - if (Blockly.selected) { - Blockly.selected.setHintText("abc"); - Blockly.selected.hint.setVisible(true); - } - workspace.drawHighlightBox('target',undefined,{color:'#92C124'}) +// 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('target',null,{color:'#3C91E6'}) } From 5db6cd58cc6abc302020dd765b4c8958a34962d5 Mon Sep 17 00:00:00 2001 From: Peeratham Techapalokul Date: Tue, 20 Aug 2019 16:06:51 -0400 Subject: [PATCH 17/23] fire block events --- core/block_transformer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/core/block_transformer.js b/core/block_transformer.js index 74036e676e..a4d00be0de 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) From 84800f91cb3334715e6badc8604fe3c03211edc0 Mon Sep 17 00:00:00 2001 From: Peeratham Techapalokul Date: Wed, 21 Aug 2019 14:57:19 -0400 Subject: [PATCH 18/23] field highlighting --- core/workspace_svg.js | 22 ++++++++++++++++++++++ tests/vertical_playground.html | 33 +++++++++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 9f86ba2272..a7784bec3d 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -1063,6 +1063,28 @@ Blockly.WorkspaceSvg.prototype.removeHighlightBox = function () { } }; + +Blockly.WorkspaceSvg.prototype.highlightField = function (shadowId) { + var shadowBlock = null; + if(!shadowBlock){ + shadowBlock = this.getBlockById(shadowId); + var fieldPath = shadowBlock.getSvgRoot().querySelector('path'); + var highlightFieldStyle = document.createAttribute("style"); + highlightFieldStyle.value = "stroke: limegreen; stroke-width: 5;"; + fieldPath.attributes.setNamedItem(highlightFieldStyle); + } +}; + +Blockly.WorkspaceSvg.prototype.unHighlightField = function (shadowId) { + var shadowBlock = null; + if(!shadowBlock){ + shadowBlock = this.getBlockById(shadowId); + var fieldPath = shadowBlock.getSvgRoot().querySelector('path'); + fieldPath.attributes.removeNamedItem('style'); + } +}; + + /** * Glow/unglow a stack in the workspace. * @param {?string} id ID of block which starts the stack. diff --git a/tests/vertical_playground.html b/tests/vertical_playground.html index 15a29b756e..8f92a482f6 100644 --- a/tests/vertical_playground.html +++ b/tests/vertical_playground.html @@ -113,8 +113,10 @@ state = sessionStorage.getItem('logFlyoutEvents'); logFlyoutEvents(Boolean(state)); // showHintSetup(); - highlightBlocksSetup(); - svgSetup(); + // highlightBlocksSetup(); + highlighFieldSetup(); + // svgSetup(); + // transformSetup(); } } @@ -321,6 +323,13 @@ } } + function transformSetup(){ + var input = document.getElementById('importExport'); + input.value = + "900036810361610"; + fromXml(); + } + function highlightBlocksSetup(){ var input = document.getElementById('importExport'); input.value = @@ -336,9 +345,25 @@ // Blockly.selected.setHintText("abc"); // Blockly.selected.hint.setVisible(true); // } - workspace.drawHighlightBox('`!ttdW7uQ~+?pJ#xb@n`','1TQo0bqgwh%R+%QOA50o') + workspace.drawHighlightBox('`!ttdW7uQ~+?pJ#xb@n`','1TQo0bqgwh%R+%QOA50o'); // 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); From 340f871c2fd045d6337baeae6df5fc6db7ed1e14 Mon Sep 17 00:00:00 2001 From: Peeratham Techapalokul Date: Tue, 27 Aug 2019 20:37:00 -0400 Subject: [PATCH 19/23] basic field (un) highlight --- core/gesture.js | 1 + core/workspace.js | 4 ++++ core/workspace_hint.js | 6 ++++++ 3 files changed, 11 insertions(+) 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/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 From 53a436bb521387e4047a1bf8c15bd188fc15f038 Mon Sep 17 00:00:00 2001 From: Peeratham Techapalokul Date: Wed, 28 Aug 2019 11:40:50 -0400 Subject: [PATCH 20/23] fix bug insert after --- core/block_transformer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/block_transformer.js b/core/block_transformer.js index a4d00be0de..6ca2712863 100644 --- a/core/block_transformer.js +++ b/core/block_transformer.js @@ -81,7 +81,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; From af8bbde2f952dfb62454c2e7f78a4d72f5ae47fc Mon Sep 17 00:00:00 2001 From: Peeratham Techapalokul Date: Tue, 10 Sep 2019 11:52:08 -0400 Subject: [PATCH 21/23] procedure feature patch --- blocks_vertical/procedures.js | 65 ++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) 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'] = { From ceccbdd73431e13445c6c49f60d120f08bbb581e Mon Sep 17 00:00:00 2001 From: Peeratham Techapalokul Date: Thu, 3 Oct 2019 12:47:54 -0400 Subject: [PATCH 22/23] fix bug highlighting blocks --- core/block_transformer.js | 23 ++++++++++++++++++++--- core/utils.js | 2 +- tests/vertical_playground.html | 10 ++++++---- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/core/block_transformer.js b/core/block_transformer.js index 6ca2712863..a76e60ab21 100644 --- a/core/block_transformer.js +++ b/core/block_transformer.js @@ -41,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); @@ -51,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 }; @@ -212,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/utils.js b/core/utils.js index a2153baa89..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(); diff --git a/tests/vertical_playground.html b/tests/vertical_playground.html index 8f92a482f6..c50b960dce 100644 --- a/tests/vertical_playground.html +++ b/tests/vertical_playground.html @@ -113,8 +113,8 @@ state = sessionStorage.getItem('logFlyoutEvents'); logFlyoutEvents(Boolean(state)); // showHintSetup(); - // highlightBlocksSetup(); - highlighFieldSetup(); + highlightBlocksSetup(); + // highlighFieldSetup(); // svgSetup(); // transformSetup(); } @@ -333,10 +333,11 @@ function highlightBlocksSetup(){ var input = document.getElementById('importExport'); input.value = - "1090101500" + // "1090101500" //"101010_mouse_1010" // "1010" // "10_mouse_" + "15101010" ; fromXml(); @@ -345,7 +346,8 @@ // Blockly.selected.setHintText("abc"); // Blockly.selected.hint.setVisible(true); // } - workspace.drawHighlightBox('`!ttdW7uQ~+?pJ#xb@n`','1TQo0bqgwh%R+%QOA50o'); + // 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'}) } From a69bbc77fb4e5ff6093121b6607e248da2986694 Mon Sep 17 00:00:00 2001 From: Peeratham Techapalokul Date: Thu, 3 Oct 2019 12:48:39 -0400 Subject: [PATCH 23/23] fix rendering context menu options should be no scrollbar --- core/css.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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... */ '}',