Merge pull request from kchadha/comments

Scratch Block Comments
This commit is contained in:
kchadha 2018-05-30 19:31:06 -04:00 committed by GitHub
commit e38969831e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 1175 additions and 40 deletions

View file

@ -29,6 +29,7 @@ goog.provide('Blockly.Block');
goog.require('Blockly.Blocks');
goog.require('Blockly.Colours');
goog.require('Blockly.Comment');
goog.require('Blockly.ScratchBlockComment');
goog.require('Blockly.Connection');
goog.require('Blockly.Events.BlockChange');
goog.require('Blockly.Events.BlockCreate');

View file

@ -631,8 +631,15 @@ Blockly.BlockSvg.prototype.render = function(opt_bubble) {
}
// Move the icons into position.
var icons = this.getIcons();
var scratchCommentIcon = null;
for (var i = 0; i < icons.length; i++) {
cursorX = icons[i].renderIcon(cursorX);
if (icons[i] instanceof Blockly.ScratchBlockComment) {
// Don't render scratch block comment icon until
// after the inputs
scratchCommentIcon = icons[i];
} else {
cursorX = icons[i].renderIcon(cursorX);
}
}
cursorX += this.RTL ?
Blockly.BlockSvg.SEP_SPACE_X : -Blockly.BlockSvg.SEP_SPACE_X;
@ -651,6 +658,13 @@ Blockly.BlockSvg.prototype.render = function(opt_bubble) {
this.renderClassify_();
// Position the Scratch Block Comment Icon at the end of the block
if (scratchCommentIcon) {
var iconX = this.RTL ? -inputRows.rightEdge : inputRows.rightEdge;
var inputMarginY = inputRows[0].height / 2;
scratchCommentIcon.renderIcon(iconX, inputMarginY);
}
if (opt_bubble !== false) {
// Render all blocks above this one (propagate a reflow).
var parentBlock = this.getParent();

View file

@ -979,12 +979,15 @@ Blockly.BlockSvg.prototype.getCommentText = function() {
/**
* Set this block's comment text.
* @param {?string} text The text, or null to delete.
* @param {number=} commentX Optional x position for scratch comment in workspace coordinates
* @param {number=} commentY Optional y position for scratch comment in workspace coordinates
* @param {boolean=} minimized Optional minimized state for scratch comment, defaults to false
*/
Blockly.BlockSvg.prototype.setCommentText = function(text) {
Blockly.BlockSvg.prototype.setCommentText = function(text, commentX, commentY, minimized) {
var changedState = false;
if (goog.isString(text)) {
if (!this.comment) {
this.comment = new Blockly.Comment(this);
this.comment = new Blockly.ScratchBlockComment(this, commentX, commentY, minimized);
changedState = true;
}
this.comment.setText(/** @type {string} */ (text));
@ -996,6 +999,9 @@ Blockly.BlockSvg.prototype.setCommentText = function(text) {
}
if (changedState && this.rendered) {
this.render();
if (goog.isString(text)) {
this.comment.setVisible(true);
}
// Adding or removing a comment icon will cause the block to change shape.
this.bumpNeighbours_();
}

View file

@ -261,7 +261,7 @@ Blockly.ContextMenu.blockHelpOption = function(block) {
*/
Blockly.ContextMenu.blockDuplicateOption = function(block) {
var duplicateOption = {
text: Blockly.Msg.DUPLICATE_BLOCK,
text: Blockly.Msg.DUPLICATE,
enabled: true,
callback: block.duplicateAndDragCallback_()
};
@ -414,7 +414,7 @@ Blockly.ContextMenu.wsExpandOption = function(hasCollapsedBlocks, topBlocks) {
*/
Blockly.ContextMenu.commentDeleteOption = function(comment) {
var deleteOption = {
text: Blockly.Msg.REMOVE_COMMENT,
text: Blockly.Msg.DELETE,
enabled: true,
callback: function() {
Blockly.Events.setGroup(true);
@ -434,7 +434,7 @@ Blockly.ContextMenu.commentDeleteOption = function(comment) {
*/
Blockly.ContextMenu.commentDuplicateOption = function(comment) {
var duplicateOption = {
text: Blockly.Msg.DUPLICATE_COMMENT,
text: Blockly.Msg.DUPLICATE,
enabled: true,
callback: function() {
Blockly.duplicate_(comment);

View file

@ -647,6 +647,50 @@ Blockly.Css.CONTENT = [
'stroke: #fc3;',
'}',
// Scratch Comments
'.scratchCommentBody {',
'background-color: #fef49c;',
'display: flex',
'justify-content: center;',
'align-items: center;',
'}',
'.scratchCommentRect {',
'fill: #fef49c;',
'stroke-width: 1px',
'}',
'.scratchCommentTopBar {',
'fill: #000000;',
'fill-opacity: 0.1',
'}',
'.scratchCommentText {',
'font-family: "Helvetica Neue", Helvetica, sans-serif;',
'font-size: 12pt;',
'font-weight: 400;',
'}',
'.scratchCommentTextarea {',
'background-color: #fef49c;',
'border: 0;',
'outline: 0;',
'padding: 0;',
'resize: none;',
'overflow: hidden;',
'}',
'.scratchCommentResizeSE {',
'cursor: se-resize;',
'fill: transparent;',
'}',
'.scratchCommentResizeSW {',
'cursor: sw-resize;',
'fill: transparent;',
'}',
'.blocklyHtmlInput {',
'border: none;',
'font-family: "Helvetica Neue", Helvetica, sans-serif;',

View file

@ -600,18 +600,20 @@ Blockly.Flyout.prototype.recordCategoryScrollPositions_ = function() {
}
// Record the length of each category, setting the final one to 0.
var numCategories = this.categoryScrollPositions.length;
for (var i = 0; i < numCategories - 1; i++) {
var currentPos = this.categoryScrollPositions[i].position;
var nextPos = this.categoryScrollPositions[i+1].position;
var length = nextPos - currentPos;
this.categoryScrollPositions[i].length = length;
}
this.categoryScrollPositions[numCategories - 1].length = 0;
// Record the id of each category.
for (var i = 0; i < numCategories; i++) {
var category = this.parentToolbox_.getCategoryByIndex(i);
if (category && category.id_) {
this.categoryScrollPositions[i].categoryId = category.id_;
if (numCategories > 0) {
for (var i = 0; i < numCategories - 1; i++) {
var currentPos = this.categoryScrollPositions[i].position;
var nextPos = this.categoryScrollPositions[i+1].position;
var length = nextPos - currentPos;
this.categoryScrollPositions[i].length = length;
}
this.categoryScrollPositions[numCategories - 1].length = 0;
// Record the id of each category.
for (var i = 0; i < numCategories; i++) {
var category = this.parentToolbox_.getCategoryByIndex(i);
if (category && category.id_) {
this.categoryScrollPositions[i].categoryId = category.id_;
}
}
}
};

View file

@ -209,13 +209,15 @@ Blockly.VerticalFlyout.prototype.getMetrics_ = function() {
// the last category.
var contentHeight = optionBox.height * this.workspace_.scale;
this.recordCategoryScrollPositions_();
var lastLabel = this.categoryScrollPositions[
this.categoryScrollPositions.length - 1];
var lastPos = lastLabel.position * this.workspace_.scale;
var lastCategoryHeight = contentHeight - lastPos;
var bottomPadding = this.MARGIN;
if (lastCategoryHeight < viewHeight) {
bottomPadding = viewHeight - lastCategoryHeight;
if (this.categoryScrollPositions.length > 0) {
var lastLabel = this.categoryScrollPositions[
this.categoryScrollPositions.length - 1];
var lastPos = lastLabel.position * this.workspace_.scale;
var lastCategoryHeight = contentHeight - lastPos;
if (lastCategoryHeight < viewHeight) {
bottomPadding = viewHeight - lastCategoryHeight;
}
}
var metrics = {

View file

@ -0,0 +1,436 @@
/**
* @license
* Visual Blocks Editor
*
* Copyright 2018 Google Inc.
* https://developers.google.com/blockly/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Object representing a code comment.
* @author kchadha@scratch.mit.edu (Karishma Chadha)
*/
'use strict';
goog.provide('Blockly.ScratchBlockComment');
goog.require('Blockly.Comment');
goog.require('Blockly.Events.BlockChange');
goog.require('Blockly.Events.Ui');
goog.require('Blockly.Icon');
goog.require('Blockly.ScratchBubble');
goog.require('goog.math.Coordinate');
goog.require('goog.userAgent');
/**
* Class for a comment.
* @param {!Blockly.Block} block The block associated with this comment.
* @param {number=} x Initial x position for comment, in workspace coordinates.
* @param {number=} y Initial y position for comment, in workspace coordinates.
* @param {boolean=} minimized Whether or not this comment is minimized
* (only the top bar displays), defaults to false.
* @extends {Blockly.Comment}
* @constructor
*/
Blockly.ScratchBlockComment = function(block, x, y, minimized) {
Blockly.ScratchBlockComment.superClass_.constructor.call(this, block);
this.x_ = x;
this.y_ = y;
this.isMinimized_ = minimized || false;
};
goog.inherits(Blockly.ScratchBlockComment, Blockly.Comment);
/**
* Width of bubble.
* @private
*/
Blockly.ScratchBlockComment.prototype.width_ = 200;
/**
* Height of bubble.
* @private
*/
Blockly.ScratchBlockComment.prototype.height_ = 200;
/**
* Comment Icon Size.
* @package
*/
Blockly.ScratchBlockComment.prototype.SIZE = 0;
/**
* Offset for text area in comment bubble.
* @private
*/
Blockly.ScratchBlockComment.TEXTAREA_OFFSET = 12;
/**
* Maximum lable length (actual label length will include
* one additional character, the ellipsis).
* @private
*/
Blockly.ScratchBlockComment.MAX_LABEL_LENGTH = 16;
/**
* Width that a minimized comment should have.
* @private
*/
Blockly.ScratchBlockComment.MINIMIZE_WIDTH = 200;
/**
* Draw the comment icon.
* @param {!Element} _group The icon group.
* @private
*/
Blockly.ScratchBlockComment.prototype.drawIcon_ = function(_group) {
// NO-OP -- Don't render a comment icon for Scratch block comments
};
// Override renderIcon from Blocky.Icon so that the comment bubble is
// anchored correctly on the block. This function takes in the top margin
// as an input instead of setting an arbitrary one.
/**
* Render the icon.
* @param {number} cursorX Horizontal offset at which to position the icon.
* @param {number} topMargin Vertical offset from the top of the block to position the icon.
* @return {number} Horizontal offset for next item to draw.
* @package
*/
Blockly.ScratchBlockComment.prototype.renderIcon = function(cursorX, topMargin) {
if (this.collapseHidden && this.block_.isCollapsed()) {
this.iconGroup_.setAttribute('display', 'none');
return cursorX;
}
this.iconGroup_.setAttribute('display', 'block');
var width = this.SIZE;
if (this.block_.RTL) {
cursorX -= width;
}
this.iconGroup_.setAttribute('transform',
'translate(' + cursorX + ',' + topMargin + ')');
this.computeIconLocation();
if (this.block_.RTL) {
cursorX -= Blockly.BlockSvg.SEP_SPACE_X;
} else {
cursorX += width + Blockly.BlockSvg.SEP_SPACE_X;
}
return cursorX;
};
/**
* Create the editor for the comment's bubble.
* @return {{commentEditor: !Element, labelText: !string}} The components used
* to render the comment editing/writing area and the truncated label text
* to display in the minimized comment top bar.
* @private
*/
Blockly.ScratchBlockComment.prototype.createEditor_ = function() {
this.foreignObject_ = Blockly.utils.createSvgElement('foreignObject',
{
'x': Blockly.ScratchBubble.BORDER_WIDTH,
'y': Blockly.ScratchBubble.BORDER_WIDTH + Blockly.ScratchBubble.TOP_BAR_HEIGHT,
'class': 'blocklyCommentForeignObject'
},
null);
var body = document.createElementNS(Blockly.HTML_NS, 'body');
body.setAttribute('xmlns', Blockly.HTML_NS);
body.className = 'blocklyMinimalBody scratchCommentBody';
var textarea = document.createElementNS(Blockly.HTML_NS, 'textarea');
textarea.className = 'scratchCommentTextarea scratchCommentText';
textarea.setAttribute('dir', this.block_.RTL ? 'RTL' : 'LTR');
body.appendChild(textarea);
this.textarea_ = textarea;
this.textarea_.style.margin = (Blockly.ScratchBlockComment.TEXTAREA_OFFSET) + 'px';
this.foreignObject_.appendChild(body);
Blockly.bindEventWithChecks_(textarea, 'mouseup', this, this.textareaFocus_);
// Don't zoom with mousewheel.
Blockly.bindEventWithChecks_(textarea, 'wheel', this, function(e) {
e.stopPropagation();
});
Blockly.bindEventWithChecks_(textarea, 'change', this, function(_e) {
if (this.text_ != textarea.value) {
Blockly.Events.fire(new Blockly.Events.BlockChange(
this.block_, 'comment', null, this.text_, textarea.value));
this.text_ = textarea.value;
}
});
setTimeout(function() {
textarea.focus();
}, 0);
// Label for comment top bar when comment is minimized
this.label_ = this.getLabelText();
return {
commentEditor: this.foreignObject_,
labelText: this.label_
};
};
/**
* Callback function triggered when the bubble has resized.
* Resize the text area accordingly.
* @private
*/
Blockly.ScratchBlockComment.prototype.resizeBubble_ = function() {
if (this.isVisible() && !this.isMinimized_) {
var size = this.bubble_.getBubbleSize();
var doubleBorderWidth = 2 * Blockly.ScratchBubble.BORDER_WIDTH;
var textOffset = Blockly.ScratchBlockComment.TEXTAREA_OFFSET * 2;
this.foreignObject_.setAttribute('width', size.width - doubleBorderWidth);
this.foreignObject_.setAttribute('height', size.height - doubleBorderWidth - Blockly.ScratchBubble.TOP_BAR_HEIGHT);
this.textarea_.style.width = (size.width - textOffset) + 'px';
this.textarea_.style.height = (size.height - doubleBorderWidth -
Blockly.ScratchBubble.TOP_BAR_HEIGHT - textOffset) + 'px';
// Actually set the size!
this.width_ = size.width;
this.height_ = size.height;
}
};
/**
* Change the colour of the associated bubble to match its block.
* @package
*/
Blockly.ScratchBlockComment.prototype.updateColour = function() {
if (this.isVisible()) {
this.bubble_.setColour(this.block_.getColourTertiary());
}
};
/**
* Show or hide the comment bubble.
* @param {boolean} visible True if the bubble should be visible.
* @package
*/
Blockly.ScratchBlockComment.prototype.setVisible = function(visible) {
if (visible == this.isVisible()) {
// No change.
return;
}
Blockly.Events.fire(
new Blockly.Events.Ui(this.block_, 'commentOpen', !visible, visible));
if ((!this.block_.isEditable() && !this.textarea_) || goog.userAgent.IE) {
// Steal the code from warnings to make an uneditable text bubble.
// MSIE does not support foreignobject; textareas are impossible.
// http://msdn.microsoft.com/en-us/library/hh834675%28v=vs.85%29.aspx
// Always treat comments in IE as uneditable.
Blockly.Warning.prototype.setVisible.call(this, visible);
return;
}
// Save the bubble stats before the visibility switch.
var text = this.getText();
var size = this.getBubbleSize();
if (visible) {
// Decide on placement of the bubble if x and y coordinates are not provided
// based on knowledge of the block that owns this comment:
if (!this.x_ && this.x_ != 0 && !this.y_ && this.y_ != 0) {
if (this.isMinimized_) {
var minimizedOffset = 4 * Blockly.BlockSvg.GRID_UNIT;
this.x_ = this.block_.RTL ?
this.iconXY_.x - minimizedOffset :
this.iconXY_.x + minimizedOffset;
this.y_ = this.iconXY_.y - (Blockly.ScratchBubble.TOP_BAR_HEIGHT / 2);
} else {
// Check if the width of this block (and all it's children/descendents) is the
// same as the width of just this block
var fullStackWidth = Math.floor(this.block_.getHeightWidth().width);
var thisBlockWidth = Math.floor(this.block_.svgPath_.getBBox().width);
var offset = 8 * Blockly.BlockSvg.GRID_UNIT;
if (fullStackWidth == thisBlockWidth && !this.block_.parentBlock_) {
this.x_ = this.block_.RTL ?
this.iconXY_.x - this.width_ - offset :
this.iconXY_.x + offset;
} else {
this.x_ = this.block_.RTL ?
this.iconXY_.x - this.width_ - fullStackWidth - offset :
this.iconXY_.x + fullStackWidth + offset;
}
this.y_ = this.iconXY_.y - (Blockly.ScratchBubble.TOP_BAR_HEIGHT / 2);
}
}
// Create the bubble.
this.bubble_ = new Blockly.ScratchBubble(
/** @type {!Blockly.WorkspaceSvg} */ (this.block_.workspace),
this.createEditor_(), this.iconXY_, this.width_, this.height_,
this.x_, this.y_, this.isMinimized_);
this.bubble_.setAutoLayout(false);
this.bubble_.registerResizeEvent(this.resizeBubble_.bind(this));
this.bubble_.registerMinimizeToggleEvent(this.toggleMinimize_.bind(this));
this.bubble_.registerDeleteEvent(this.dispose.bind(this));
this.bubble_.registerContextMenuCallback(this.showContextMenu_.bind(this));
this.updateColour();
} else {
// Dispose of the bubble.
this.bubble_.dispose();
this.bubble_ = null;
this.textarea_ = null;
this.foreignObject_ = null;
this.label_ = null;
}
// Restore the bubble stats after the visibility switch.
this.setText(text);
this.setBubbleSize(size.width, size.height);
};
/**
* Toggle the minimization state of this comment.
* @private
*/
Blockly.ScratchBlockComment.prototype.toggleMinimize_ = function() {
this.setMinimized(!this.isMinimized_);
};
/**
* Set the minimized state for this comment.
* @param {boolean} minimize Whether the comment should be minimized
* @package
*/
Blockly.ScratchBlockComment.prototype.setMinimized = function(minimize) {
if (this.isMinimized_ == minimize) return;
this.isMinimized_ = minimize;
if (minimize) {
this.bubble_.setMinimized(true, this.getLabelText());
this.setBubbleSize(Blockly.ScratchBlockComment.MINIMIZE_WIDTH,
Blockly.ScratchBubble.TOP_BAR_HEIGHT);
// Note we are not updating this.width_ or this.height_ here
// because we want to keep track of the width/height of the
// maximized comment
} else {
this.bubble_.setMinimized(false);
this.setText(this.text_);
this.setBubbleSize(this.width_, this.height_);
}
};
/**
* Size this comment's bubble.
* @param {number} width Width of the bubble.
* @param {number} height Height of the bubble.
* @package
*/
Blockly.ScratchBlockComment.prototype.setBubbleSize = function(width, height) {
if (this.bubble_) {
if (this.isMinimized_) {
this.bubble_.setBubbleSize(Blockly.ScratchBlockComment.MINIMIZE_WIDTH,
Blockly.ScratchBubble.TOP_BAR_HEIGHT);
} else {
this.bubble_.setBubbleSize(width, height);
this.width_ = width;
this.height_ = height;
}
} else {
this.width_ = width;
this.height_ = height;
}
};
/**
* Get the truncated text for this comment to display in the minimized
* top bar.
* @return {string} The truncated comment text
* @package
*/
Blockly.ScratchBlockComment.prototype.getLabelText = function() {
if (this.text_.length > Blockly.ScratchBlockComment.MAX_LABEL_LENGTH) {
if (this.block_.RTL) {
return '\u2026' + this.text_.slice(0, Blockly.ScratchBlockComment.MAX_LABEL_LENGTH);
}
return this.text_.slice(0, Blockly.ScratchBlockComment.MAX_LABEL_LENGTH) + '\u2026';
} else {
return this.text_;
}
};
/**
* Set this comment's text.
* @param {string} text Comment text.
* @package
*/
Blockly.ScratchBlockComment.prototype.setText = function(text) {
if (this.text_ != text) {
Blockly.Events.fire(new Blockly.Events.BlockChange(
this.block_, 'comment', null, this.text_, text));
this.text_ = text;
}
if (this.textarea_) {
this.textarea_.value = text;
}
};
/**
* Move this comment to a position given x and y coordinates.
* @param {number} x The x-coordinate on the workspace.
* @param {number} y The y-coordinate on the workspace.
* @package
*/
Blockly.ScratchBlockComment.prototype.moveTo = function(x, y) {
if (this.bubble_) {
this.bubble_.moveTo(x, y);
}
this.x_ = x;
this.y_ = y;
};
/**
* Get the x and y position of this comment.
* @return {goog.math.Coordinate} The XY position
* @package
*/
Blockly.ScratchBlockComment.prototype.getXY = function() {
if (this.bubble_) {
return this.bubble_.getRelativeToSurfaceXY();
} else {
return new goog.math.Coordinate(this.x_, this.y_);
}
};
/**
* Get the height and width of this comment.
* Note: this does not use the current bubble size because
* the bubble may be minimized.
* @return {{height: number, width: number}} The height and width of
* this comment when it is full size. These numbers do not change
* as the workspace zoom changes.
* @package
*/
Blockly.ScratchBlockComment.prototype.getHeightWidth = function() {
return {height: this.height_, width: this.width_};
};
/**
* Check whether this comment is currently minimized.
* @return {boolean} True if minimized
* @package
*/
Blockly.ScratchBlockComment.prototype.isMinimized = function() {
return this.isMinimized_;
};
/**
* Show the context menu for this comment's bubble.
* @param {!Event} e The mouse event
* @private
*/
Blockly.ScratchBlockComment.prototype.showContextMenu_ = function(e) {
var menuOptions = [];
menuOptions.push(Blockly.ContextMenu.commentDeleteOption(this, Blockly.Msg.DELETE));
Blockly.ContextMenu.show(e, menuOptions, this.block_.RTL);
};

574
core/scratch_bubble.js Normal file
View file

@ -0,0 +1,574 @@
/**
* @license
* Visual Blocks Editor
*
* Copyright 2018 Google Inc.
* https://developers.google.com/blockly/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Object representing a UI bubble.
* @author kchadha@scratch.mit.edu (Karishma Chadha)
*/
'use strict';
goog.provide('Blockly.ScratchBubble');
goog.require('Blockly.Touch');
goog.require('Blockly.Workspace');
goog.require('goog.dom');
goog.require('goog.math');
goog.require('goog.math.Coordinate');
goog.require('goog.userAgent');
/**
* Class for Scratch comment UI bubble.
* @param {!Blockly.WorkspaceSvg} workspace The workspace on which to draw the
* bubble.
* @param {!Element} content SVG content for the bubble.
* @param {!goog.math.Coordinate} anchorXY Absolute position of bubble's anchor
* point.
* @param {?number} bubbleWidth Width of bubble, or null if not resizable.
* @param {?number} bubbleHeight Height of bubble, or null if not resizable.
* @param {?number} bubbleX X position of bubble
* @param {?number} bubbleY Y position of bubble
* @param {?boolean} minimized Whether or not this comment bubble is minimized
* (only the top bar displays), defaults to false if not provided.
* @extends {Blockly.Bubble}
* @constructor
*/
Blockly.ScratchBubble = function(workspace, content, anchorXY,
bubbleWidth, bubbleHeight, bubbleX, bubbleY, minimized) {
this.workspace_ = workspace;
this.content_ = content;
this.x = bubbleX;
this.y = bubbleY;
this.isMinimized_ = minimized || false;
var canvas = workspace.getBubbleCanvas();
canvas.appendChild(this.createDom_(content, !!(bubbleWidth && bubbleHeight), this.isMinimized_));
this.setAnchorLocation(anchorXY);
if (!bubbleWidth || !bubbleHeight) {
var bBox = /** @type {SVGLocatable} */ (this.content_).getBBox();
bubbleWidth = bBox.width + 2 * Blockly.ScratchBubble.BORDER_WIDTH;
bubbleHeight = bBox.height + 2 * Blockly.ScratchBubble.BORDER_WIDTH;
}
this.setBubbleSize(bubbleWidth, bubbleHeight);
// Render the bubble.
this.positionBubble_();
this.renderArrow_();
this.rendered_ = true;
if (!workspace.options.readOnly) {
Blockly.bindEventWithChecks_(
this.minimizeArrow_, 'mousedown', this, this.minimizeArrowMouseDown_);
Blockly.bindEventWithChecks_(
this.minimizeArrow_, 'mouseup', this, this.minimizeArrowMouseUp_);
Blockly.bindEventWithChecks_(
this.deleteIcon_, 'mousedown', this, this.deleteMouseDown_);
Blockly.bindEventWithChecks_(
this.deleteIcon_, 'mouseup', this, this.deleteMouseUp_);
Blockly.bindEventWithChecks_(
this.bubbleGroup_, 'mousedown', this, this.bubbleMouseDown_);
if (this.resizeGroup_) {
Blockly.bindEventWithChecks_(
this.resizeGroup_, 'mousedown', this, this.resizeMouseDown_);
}
}
this.setAutoLayout(false);
this.moveTo(this.x, this.y);
};
goog.inherits(Blockly.ScratchBubble, Blockly.Bubble);
/**
* Width of the border around the bubble.
* @package
*/
Blockly.ScratchBubble.BORDER_WIDTH = 1;
/**
* Thickness of the line connecting the bubble
* to the block.
* @private
*/
Blockly.ScratchBubble.LINE_THICKNESS = 1;
/**
* The height of the comment top bar.
* @package
*/
Blockly.ScratchBubble.TOP_BAR_HEIGHT = 32;
/**
* The size of the minimize arrow icon in the comment top bar.
* @private
*/
Blockly.ScratchBubble.MINIMIZE_ICON_SIZE = 16;
/**
* The size of the delete icon in the comment top bar.
* @private
*/
Blockly.ScratchBubble.DELETE_ICON_SIZE = 12;
/**
* The inset for the top bar icons.
* @private
*/
Blockly.ScratchBubble.TOP_BAR_ICON_INSET = 6;
/**
* Create the bubble's DOM.
* @param {!Element} content SVG content for the bubble.
* @param {boolean} hasResize Add diagonal resize gripper if true.
* @param {boolean} minimized Whether the bubble is minimized
* @return {!Element} The bubble's SVG group.
* @private
*/
Blockly.ScratchBubble.prototype.createDom_ = function(content, hasResize, minimized) {
this.bubbleGroup_ = Blockly.utils.createSvgElement('g', {}, null);
this.bubbleArrow_ = Blockly.utils.createSvgElement('line',
{'stroke-linecap': 'round'},
this.bubbleGroup_);
this.bubbleBack_ = Blockly.utils.createSvgElement('rect',
{
'class': 'blocklyDraggable scratchCommentRect',
'x': 0,
'y': 0,
'rx': 4 * Blockly.ScratchBubble.BORDER_WIDTH,
'ry': 4 * Blockly.ScratchBubble.BORDER_WIDTH
},
this.bubbleGroup_);
this.labelText_ = content.labelText;
this.createCommentTopBar_();
// Comment Text Editor
this.commentEditor_ = content.commentEditor;
this.bubbleGroup_.appendChild(this.commentEditor_);
// Comment Resize Handle
if (hasResize) {
this.createResizeHandle_();
} else {
this.resizeGroup_ = null;
}
// Show / hide relevant things based on minimized state
if (minimized) {
this.minimizeArrow_.setAttributeNS('http://www.w3.org/1999/xlink',
'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'comment-arrow-up.svg');
this.commentEditor_.setAttribute('display', 'none');
this.resizeGroup_.setAttribute('display', 'none');
} else {
this.minimizeArrow_.setAttributeNS('http://www.w3.org/1999/xlink',
'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'comment-arrow-down.svg');
this.topBarLabel_.setAttribute('display', 'none');
}
return this.bubbleGroup_;
};
/**
* Create the comment top bar and its contents.
* @private
*/
Blockly.ScratchBubble.prototype.createCommentTopBar_ = function() {
this.commentTopBar_ = Blockly.utils.createSvgElement('rect',
{
'class': 'blocklyDraggable scratchCommentTopBar',
'x': Blockly.ScratchBubble.BORDER_WIDTH,
'y': Blockly.ScratchBubble.BORDER_WIDTH,
'height': Blockly.ScratchBubble.TOP_BAR_HEIGHT
}, this.bubbleGroup_);
this.createTopBarIcons_();
this.createTopBarLabel_();
};
/**
* Create the minimize toggle and delete icons that in the comment top bar.
* @private
*/
Blockly.ScratchBubble.prototype.createTopBarIcons_ = function() {
var topBarMiddleY = (Blockly.ScratchBubble.TOP_BAR_HEIGHT / 2) +
Blockly.ScratchBubble.BORDER_WIDTH;
// Minimize Toggle Icon in Comment Top Bar
var xInset = Blockly.ScratchBubble.TOP_BAR_ICON_INSET;
this.minimizeArrow_ = Blockly.utils.createSvgElement('image',
{
'x': xInset,
'y': topBarMiddleY - Blockly.ScratchBubble.MINIMIZE_ICON_SIZE / 2,
'width': Blockly.ScratchBubble.MINIMIZE_ICON_SIZE,
'height': Blockly.ScratchBubble.MINIMIZE_ICON_SIZE
}, this.bubbleGroup_);
// Delete Icon in Comment Top Bar
this.deleteIcon_ = Blockly.utils.createSvgElement('image',
{
'x': xInset,
'y': topBarMiddleY - Blockly.ScratchBubble.DELETE_ICON_SIZE / 2,
'width': Blockly.ScratchBubble.DELETE_ICON_SIZE,
'height': Blockly.ScratchBubble.DELETE_ICON_SIZE
}, this.bubbleGroup_);
this.deleteIcon_.setAttributeNS('http://www.w3.org/1999/xlink',
'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'delete-x.svg');
};
/**
* Create the comment top bar label. This is the truncated comment text
* that shows when comment is minimized.
* @private
*/
Blockly.ScratchBubble.prototype.createTopBarLabel_ = function() {
this.topBarLabel_ = Blockly.utils.createSvgElement('text',
{
'class': 'scratchCommentText',
'x': this.width_ / 2,
'y': (Blockly.ScratchBubble.TOP_BAR_HEIGHT / 2) + Blockly.ScratchBubble.BORDER_WIDTH,
'text-anchor': 'middle',
'dominant-baseline': 'middle'
}, this.bubbleGroup_);
this.labelTextNode_ = document.createTextNode(this.labelText_);
this.topBarLabel_.appendChild(this.labelTextNode_);
};
/**
* Create the comment resize handle.
* @private
*/
Blockly.ScratchBubble.prototype.createResizeHandle_ = function() {
this.resizeGroup_ = Blockly.utils.createSvgElement('g',
{'class': this.workspace_.RTL ?
'scratchCommentResizeSW' : 'scratchCommentResizeSE'},
this.bubbleGroup_);
var resizeSize = 12 * Blockly.ScratchBubble.BORDER_WIDTH;
Blockly.utils.createSvgElement('polygon',
{'points': '0,x x,x x,0'.replace(/x/g, resizeSize.toString())},
this.resizeGroup_);
Blockly.utils.createSvgElement('line',
{
'class': 'blocklyResizeLine',
'x1': resizeSize / 3, 'y1': resizeSize - 1,
'x2': resizeSize - 1, 'y2': resizeSize / 3
}, this.resizeGroup_);
Blockly.utils.createSvgElement('line',
{
'class': 'blocklyResizeLine',
'x1': resizeSize * 2 / 3,
'y1': resizeSize - 1,
'x2': resizeSize - 1,
'y2': resizeSize * 2 / 3
}, this.resizeGroup_);
};
/**
* Show the context menu for this bubble.
* @param {!Event} e Mouse event.
* @private
*/
Blockly.ScratchBubble.prototype.showContextMenu_ = function(e) {
if (this.workspace_.options.readOnly) {
return;
}
if (this.contextMenuCallback_) {
this.contextMenuCallback_(e);
}
};
/**
* Handle a mouse-down on bubble's minimize icon.
* @param {!Event} e Mouse up event.
* @private
*/
Blockly.ScratchBubble.prototype.minimizeArrowMouseDown_ = function(e) {
e.stopPropagation();
};
/**
* Handle a mouse-up on bubble's minimize icon.
* @param {!Event} e Mouse up event.
* @private
*/
Blockly.ScratchBubble.prototype.minimizeArrowMouseUp_ = function(e) {
if (this.minimizeToggleCallback_) {
this.minimizeToggleCallback_.call(this);
}
e.stopPropagation();
};
/**
* Handle a mouse-down on bubble's minimize icon.
* @param {!Event} e Mouse up event.
* @private
*/
Blockly.ScratchBubble.prototype.deleteMouseDown_ = function(e) {
e.stopPropagation();
};
/**
* Handle a mouse-up on bubble's delete icon.
* @param {!Event} e Mouse up event.
* @private
*/
Blockly.ScratchBubble.prototype.deleteMouseUp_ = function(e) {
if (this.deleteCallback_) {
this.deleteCallback_.call(this);
}
e.stopPropagation();
};
/**
* Set the minimized state of the bubble.
* @param {boolean} minimize Whether the bubble should be minimized
* @param {?string} labelText Optional label text for the comment top bar
* when it is minimized.
* @package
*/
Blockly.ScratchBubble.prototype.setMinimized = function(minimize, labelText) {
if (minimize == this.isMinimized_) {
return;
}
if (minimize) {
this.isMinimized_ = true;
// Change minimize icon
this.minimizeArrow_.setAttributeNS('http://www.w3.org/1999/xlink',
'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'comment-arrow-up.svg');
// Hide text area
this.commentEditor_.setAttribute('display', 'none');
// Hide resize handle if it exists
if (this.resizeGroup_) {
this.resizeGroup_.setAttribute('display', 'none');
}
if (labelText && this.labelText_ != labelText) {
// Update label and display
// TODO is there a better way to do this?
this.topBarLabel_.removeChild(this.labelTextNode_);
this.labelTextNode_ = document.createTextNode(labelText);
this.topBarLabel_.appendChild(this.labelTextNode_);
}
Blockly.utils.removeAttribute(this.topBarLabel_, 'display');
} else {
this.isMinimized_ = false;
// Change minimize icon
this.minimizeArrow_.setAttributeNS('http://www.w3.org/1999/xlink',
'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'comment-arrow-down.svg');
// Hide label
this.topBarLabel_.setAttribute('display', 'none');
// Show text area
Blockly.utils.removeAttribute(this.commentEditor_, 'display');
// Display resize handle if it exists
if (this.resizeGroup_) {
Blockly.utils.removeAttribute(this.resizeGroup_, 'display');
}
}
};
/**
* Register a function as a callback event for when the bubble is minimized.
* @param {!Function} callback The function to call on resize.
* @package
*/
Blockly.ScratchBubble.prototype.registerMinimizeToggleEvent = function(callback) {
this.minimizeToggleCallback_ = callback;
};
/**
* Register a function as a callback event for when the bubble is resized.
* @param {!Function} callback The function to call on resize.
* @package
*/
Blockly.ScratchBubble.prototype.registerDeleteEvent = function(callback) {
this.deleteCallback_ = callback;
};
/**
* Register a function as a callback to show the context menu for this comment.
* @param {!Function} callback The function to call on resize.
* @package
*/
Blockly.ScratchBubble.prototype.registerContextMenuCallback = function(callback) {
this.contextMenuCallback_ = callback;
};
/**
* Notification that the anchor has moved.
* Update the arrow and bubble accordingly.
* @param {!goog.math.Coordinate} xy Absolute location.
* @package
*/
Blockly.ScratchBubble.prototype.setAnchorLocation = function(xy) {
this.anchorXY_ = xy;
if (this.rendered_) {
this.positionBubble_();
}
};
/**
* Move the bubble group to the specified location in workspace coordinates.
* @param {number} x The x position to move to.
* @param {number} y The y position to move to.
* @package
*/
Blockly.ScratchBubble.prototype.moveTo = function(x, y) {
Blockly.ScratchBubble.superClass_.moveTo.call(this, x, y);
this.updatePosition_(x, y);
};
/**
* Size this bubble.
* @param {number} width Width of the bubble.
* @param {number} height Height of the bubble.
* @package
*/
Blockly.ScratchBubble.prototype.setBubbleSize = function(width, height) {
var doubleBorderWidth = 2 * Blockly.ScratchBubble.BORDER_WIDTH;
// Minimum size of a bubble.
width = Math.max(width, doubleBorderWidth + 50);
height = Math.max(height, doubleBorderWidth + Blockly.ScratchBubble.TOP_BAR_HEIGHT);
this.width_ = width;
this.height_ = height;
this.bubbleBack_.setAttribute('width', width);
this.bubbleBack_.setAttribute('height', height);
this.commentTopBar_.setAttribute('width', width - doubleBorderWidth);
this.commentTopBar_.setAttribute('height', Blockly.ScratchBubble.TOP_BAR_HEIGHT);
if (this.workspace_.RTL) {
this.minimizeArrow_.setAttribute('x', width -
(Blockly.ScratchBubble.MINIMIZE_ICON_SIZE) -
Blockly.ScratchBubble.TOP_BAR_ICON_INSET);
} else {
this.deleteIcon_.setAttribute('x', width -
Blockly.ScratchBubble.DELETE_ICON_SIZE -
Blockly.ScratchBubble.TOP_BAR_ICON_INSET);
}
if (this.resizeGroup_) {
var resizeSize = 12 * Blockly.ScratchBubble.BORDER_WIDTH;
if (this.workspace_.RTL) {
// Mirror the resize group.
this.resizeGroup_.setAttribute('transform', 'translate(' +
(resizeSize + doubleBorderWidth) + ',' +
(this.height_ - doubleBorderWidth - resizeSize) + ') scale(-1, 1)');
} else {
this.resizeGroup_.setAttribute('transform', 'translate(' +
(this.width_ - doubleBorderWidth - resizeSize) + ',' +
(this.height_ - doubleBorderWidth - resizeSize) + ')');
}
}
if (this.isMinimized_) {
this.topBarLabel_.setAttribute('x', this.width_ / 2);
this.topBarLabel_.setAttribute('y', this.height_ / 2);
}
if (this.rendered_) {
this.positionBubble_();
this.renderArrow_();
}
// Allow the contents to resize.
if (this.resizeCallback_) {
this.resizeCallback_();
}
};
/**
* Draw the line between the bubble and the origin.
* @private
*/
Blockly.ScratchBubble.prototype.renderArrow_ = function() {
// Find the relative coordinates of the top bar center of the bubble.
var relBubbleX = this.width_ / 2;
var relBubbleY = Blockly.ScratchBubble.TOP_BAR_HEIGHT / 2;
// Find the relative coordinates of the center of the anchor.
var relAnchorX = -this.relativeLeft_;
var relAnchorY = -this.relativeTop_;
if (relBubbleX != relAnchorX || relBubbleY != relAnchorY) {
// Compute the angle of the arrow's line.
var rise = relAnchorY - relBubbleY;
var run = relAnchorX - relBubbleX;
if (this.workspace_.RTL) {
run *= -1;
}
var baseX1 = relBubbleX;
var baseY1 = relBubbleY;
this.bubbleArrow_.setAttribute('x1', baseX1);
this.bubbleArrow_.setAttribute('y1', baseY1);
this.bubbleArrow_.setAttribute('x2', baseX1 + run);
this.bubbleArrow_.setAttribute('y2', baseY1 + rise);
this.bubbleArrow_.setAttribute('stroke-width', Blockly.ScratchBubble.LINE_THICKNESS);
}
};
/**
* Change the colour of a bubble.
* @param {string} hexColour Hex code of colour.
* @package
*/
Blockly.ScratchBubble.prototype.setColour = function(hexColour) {
this.bubbleBack_.setAttribute('stroke', hexColour);
this.bubbleArrow_.setAttribute('stroke', hexColour);
};
/**
* Move this bubble during a drag, taking into account whether or not there is
* a drag surface.
* @param {?Blockly.BlockDragSurfaceSvg} dragSurface The surface that carries
* rendered items during a drag, or null if no drag surface is in use.
* @param {!goog.math.Coordinate} newLoc The location to translate to, in
* workspace coordinates.
* @package
*/
Blockly.ScratchBubble.prototype.moveDuringDrag = function(dragSurface, newLoc) {
if (dragSurface) {
dragSurface.translateSurface(newLoc.x, newLoc.y);
this.updatePosition_(newLoc.x, newLoc.y);
} else {
this.moveTo(newLoc.x, newLoc.y);
}
this.renderArrow_();
};
/**
* Update the relative left and top of the bubble after a move.
* @param {number} x The x position of the bubble
* @param {number} y The y position of the bubble
* @private
*/
Blockly.ScratchBubble.prototype.updatePosition_ = function(x, y) {
if (this.workspace_.RTL) {
this.relativeLeft_ = this.anchorXY_.x - x - this.width_;
} else {
this.relativeLeft_ = x - this.anchorXY_.x;
}
this.relativeTop_ = y - this.anchorXY_.y;
};
/**
* Dispose of this bubble.
* @package
*/
Blockly.ScratchBubble.prototype.dispose = function() {
Blockly.ScratchBubble.superClass_.dispose.call(this);
this.topBarLabel_ = null;
this.commentTopBar_ = null;
this.minimizeArrow_ = null;
this.deleteIcon_ = null;
};

View file

@ -189,17 +189,7 @@ Blockly.Xml.blockToDom = function(block, opt_noId) {
Blockly.Xml.allFieldsToDom_(block, element);
var commentText = block.getCommentText();
if (commentText) {
var commentElement = goog.dom.createDom('comment', null, commentText);
if (typeof block.comment == 'object') {
commentElement.setAttribute('pinned', block.comment.isVisible());
var hw = block.comment.getBubbleSize();
commentElement.setAttribute('h', hw.height);
commentElement.setAttribute('w', hw.width);
}
element.appendChild(commentElement);
}
Blockly.Xml.scratchCommentToDom_(block, element);
if (block.data) {
var dataElement = goog.dom.createDom('data', null, block.data);
@ -265,6 +255,37 @@ Blockly.Xml.blockToDom = function(block, opt_noId) {
return element;
};
/**
* Encode a ScratchBlockComment as XML.
* @param {!Blockly.ScratchBlockComment} block The block possibly containing
* a comment to encode.
* @param {!Element} element The XML element to which the comment should
* encoding should be attached.
* @private
*/
Blockly.Xml.scratchCommentToDom_ = function(block, element) {
var commentText = block.getCommentText();
if (commentText) {
var commentElement = goog.dom.createDom('comment', null, commentText);
if (typeof block.comment == 'object') {
commentElement.setAttribute('pinned', block.comment.isVisible());
var xy = block.comment.getXY();
commentElement.setAttribute('x', xy.x);
commentElement.setAttribute('y', xy.y);
commentElement.setAttribute('minimized', block.comment.isMinimized());
var hw;
if (block.comment instanceof Blockly.ScratchBlockComment) {
hw = block.comment.getHeightWidth();
} else {
hw = block.comment.getBubbleSize();
}
commentElement.setAttribute('h', hw.height);
commentElement.setAttribute('w', hw.width);
}
element.appendChild(commentElement);
}
};
/**
* Deeply clone the shadow's DOM so that changes don't back-wash to the block.
* @param {!Element} shadow A tree of XML elements.
@ -661,7 +682,13 @@ Blockly.Xml.domToBlockHeadless_ = function(xmlBlock, workspace) {
}
break;
case 'comment':
block.setCommentText(xmlChild.textContent);
var bubbleX = parseInt(xmlChild.getAttribute('x'), 10);
var bubbleY = parseInt(xmlChild.getAttribute('y'), 10);
var minimized = xmlChild.getAttribute('minimized') || false;
block.setCommentText(xmlChild.textContent, bubbleX, bubbleY,
minimized == 'true');
var visible = xmlChild.getAttribute('pinned');
if (visible && !block.isInFlyout) {
// Give the renderer a millisecond to render and position the block

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
<title>dropdown-caret-up</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="dropdown-caret-up" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M6,9 C5.72520708,9 5.45163006,8.89695045 5.24127973,8.68965311 L2.31461357,5.80666227 C1.89512881,5.39326583 1.89512881,4.72464202 2.31461357,4.31004733 C2.73288244,3.89665089 9.26711756,3.89665089 9.68538643,4.31004733 C10.1048712,4.72344377 10.1048712,5.39326583 9.68538643,5.80666227 L6.75993617,8.68965311 C6.54958583,8.89695045 6.27600882,9 6,9" id="dropdown-caret" fill="#575E75"></path>
</g>
</svg>

After

(image error) Size: 880 B

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
<title>dropdown-caret-down</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="dropdown-caret-down" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M6,9 C5.72520708,9 5.45163006,8.89695045 5.24127973,8.68965311 L2.31461357,5.80666227 C1.89512881,5.39326583 1.89512881,4.72464202 2.31461357,4.31004733 C2.73288244,3.89665089 9.26711756,3.89665089 9.68538643,4.31004733 C10.1048712,4.72344377 10.1048712,5.39326583 9.68538643,5.80666227 L6.75993617,8.68965311 C6.54958583,8.89695045 6.27600882,9 6,9" id="dropdown-caret" fill="#575E75" transform="translate(6.000000, 6.500000) scale(1, -1) translate(-6.000000, -6.500000) "></path>
</g>
</svg>

After

(image error) Size: 972 B

10
media/delete-x.svg Normal file
View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
<title>delete-x</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="delete-x" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M10.8761123,10.87825 C10.2421123,11.52125 9.20511231,11.52125 8.56211231,10.87825 L5.92911231,8.24525 L3.29511231,10.87825 C2.65611231,11.51725 1.62011231,11.51725 0.981112306,10.87825 C0.662112306,10.55925 0.500112306,10.13725 0.500112306,9.72125 C0.500112306,9.30425 0.662112306,8.88325 0.981112306,8.56425 L3.61411231,5.93125 L0.977112306,3.29325 C0.657112306,2.97325 0.495112306,2.55225 0.500112306,2.13125 C0.500112306,1.71425 0.657112306,1.29825 0.977112306,0.97925 C1.61511231,0.34025 2.65111231,0.34025 3.29111231,0.97925 L5.92911231,3.61625 L8.56611231,0.97925 C9.20511231,0.34025 10.2421123,0.34025 10.8811123,0.97925 C11.5201123,1.61725 11.5201123,2.65425 10.8811123,3.29325 L8.24211231,5.93125 L10.8811123,8.56825 C11.5201123,9.20725 11.5201123,10.23425 10.8761123,10.87825" id="Fill-1" fill="#575E75"></path>
</g>
</svg>

After

(image error) Size: 1.3 KiB

View file

@ -261,7 +261,8 @@ Blockly.Msg.SOUND_SETVOLUMETO = "set volume to %1%";
Blockly.Msg.SOUND_VOLUME = "volume";
// Context menus
Blockly.Msg.DUPLICATE_BLOCK = 'Duplicate';
Blockly.Msg.DUPLICATE = 'Duplicate';
Blockly.Msg.DELETE = 'Delete';
Blockly.Msg.ADD_COMMENT = 'Add Comment';
Blockly.Msg.REMOVE_COMMENT = 'Remove Comment';
Blockly.Msg.DELETE_BLOCK = 'Delete Block';
@ -274,8 +275,6 @@ Blockly.Msg.REDO = 'Redo';
Blockly.Msg.EDIT_PROCEDURE = 'Edit';
Blockly.Msg.SHOW_PROCEDURE_DEFINITION = 'Go to definition';
Blockly.Msg.WORKSPACE_COMMENT_DEFAULT_TEXT = 'Say something...';
Blockly.Msg.DUPLICATE_COMMENT = 'Duplicate Comment';
Blockly.Msg.REMOVE_COMMENT = 'Remove Comment';
// Color
Blockly.Msg.COLOUR_HUE_LABEL = 'Color';

View file

@ -48,7 +48,7 @@
// Create main workspace.
workspace = Blockly.inject('blocklyDiv', {
comments: false,
comments: true,
disable: false,
collapse: false,
media: '../media/',