mirror of
https://github.com/scratchfoundation/scratch-blocks.git
synced 2025-06-03 16:34:59 -04:00
723 lines
24 KiB
JavaScript
723 lines
24 KiB
JavaScript
/**
|
|
* @license
|
|
* Visual Blocks Editor
|
|
*
|
|
* Copyright 2017 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 Methods for rendering a workspace comment as SVG
|
|
* @author fenichel@google.com (Rachel Fenichel)
|
|
*/
|
|
'use strict';
|
|
|
|
goog.provide('Blockly.WorkspaceCommentSvg.render');
|
|
|
|
goog.require('Blockly.WorkspaceCommentSvg');
|
|
|
|
/**
|
|
* Radius of the border around the comment.
|
|
* @type {number}
|
|
* @const
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.BORDER_WIDTH = 1;
|
|
|
|
/**
|
|
* Size of the resize icon.
|
|
* @type {number}
|
|
* @const
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.RESIZE_SIZE = 16;
|
|
|
|
/**
|
|
* Offset from the foreignobject edge to the textarea edge.
|
|
* @type {number}
|
|
* @const
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.TEXTAREA_OFFSET = 12;
|
|
|
|
/**
|
|
* The height of the comment top bar.
|
|
* @package
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT = 32;
|
|
|
|
/**
|
|
* The size of the minimize arrow icon in the comment top bar.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.MINIMIZE_ICON_SIZE = 32;
|
|
|
|
/**
|
|
* The size of the delete icon in the comment top bar.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.DELETE_ICON_SIZE = 32;
|
|
|
|
/**
|
|
* The inset for the top bar icons.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.TOP_BAR_ICON_INSET = 0;
|
|
|
|
/**
|
|
* The bottom corner padding of the resize handle touch target.
|
|
* Extends slightly outside the comment box.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.RESIZE_CORNER_PAD = 4;
|
|
|
|
/**
|
|
* The top/side padding around resize handle touch target.
|
|
* Extends about one extra "diagonal" above resize handle.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.RESIZE_OUTER_PAD = 8;
|
|
|
|
/**
|
|
* Width that a minimized comment should have.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.MINIMIZE_WIDTH = 200;
|
|
|
|
/**
|
|
* Returns a bounding box describing the dimensions of this comment.
|
|
* @return {!{height: number, width: number}} Object with height and width
|
|
* properties in workspace units.
|
|
* @package
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.getHeightWidth = function() {
|
|
return { width: this.getWidth(), height: this.getHeight() };
|
|
};
|
|
|
|
/**
|
|
* Renders the workspace comment.
|
|
* @package
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.render = function() {
|
|
if (this.rendered_) {
|
|
return;
|
|
}
|
|
|
|
var size = this.getHeightWidth();
|
|
|
|
// Add text area
|
|
this.commentEditor_ = this.createEditor_();
|
|
this.svgGroup_.appendChild(this.commentEditor_);
|
|
|
|
this.createCommentTopBar_();
|
|
|
|
this.svgRectTarget_ = Blockly.utils.createSvgElement('rect',
|
|
{
|
|
'class': 'blocklyDraggable scratchCommentTarget',
|
|
'x': 0,
|
|
'y': Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT,
|
|
'rx': 4 * Blockly.WorkspaceCommentSvg.BORDER_WIDTH,
|
|
'ry': 4 * Blockly.WorkspaceCommentSvg.BORDER_WIDTH
|
|
});
|
|
this.svgGroup_.appendChild(this.svgRectTarget_);
|
|
|
|
// Add the resize icon
|
|
this.addResizeDom_();
|
|
|
|
// Show / hide relevant things based on minimized state
|
|
if (this.isMinimized()) {
|
|
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');
|
|
}
|
|
|
|
this.setSize(size.width, size.height);
|
|
|
|
// Set the content
|
|
this.textarea_.value = this.content_;
|
|
|
|
this.rendered_ = true;
|
|
|
|
if (this.resizeGroup_) {
|
|
Blockly.bindEventWithChecks_(
|
|
this.resizeGroup_, 'mousedown', this, this.resizeMouseDown_);
|
|
Blockly.bindEventWithChecks_(
|
|
this.resizeGroup_, 'mouseup', this, this.resizeMouseUp_);
|
|
}
|
|
|
|
Blockly.bindEventWithChecks_(
|
|
this.minimizeArrow_, 'mousedown', this, this.minimizeArrowMouseDown_, true);
|
|
Blockly.bindEventWithChecks_(
|
|
this.minimizeArrow_, 'mouseout', this, this.minimizeArrowMouseOut_, true);
|
|
Blockly.bindEventWithChecks_(
|
|
this.minimizeArrow_, 'mouseup', this, this.minimizeArrowMouseUp_, true);
|
|
Blockly.bindEventWithChecks_(
|
|
this.deleteIcon_, 'mousedown', this, this.deleteMouseDown_, true);
|
|
Blockly.bindEventWithChecks_(
|
|
this.deleteIcon_, 'mouseout', this, this.deleteMouseOut_, true);
|
|
Blockly.bindEventWithChecks_(
|
|
this.deleteIcon_, 'mouseup', this, this.deleteMouseUp_, true);
|
|
};
|
|
|
|
/**
|
|
* Create the text area for the comment.
|
|
* @return {!Element} The top-level node of the editor.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.createEditor_ = function() {
|
|
this.foreignObject_ = Blockly.utils.createSvgElement(
|
|
'foreignObject',
|
|
{
|
|
'x': Blockly.WorkspaceCommentSvg.BORDER_WIDTH,
|
|
'y': Blockly.WorkspaceCommentSvg.BORDER_WIDTH + Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT,
|
|
'class': 'scratchCommentForeignObject'
|
|
},
|
|
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.RTL ? 'RTL' : 'LTR');
|
|
textarea.setAttribute('maxlength', Blockly.WorkspaceComment.COMMENT_TEXT_LIMIT);
|
|
textarea.setAttribute('placeholder', Blockly.Msg.WORKSPACE_COMMENT_DEFAULT_TEXT);
|
|
body.appendChild(textarea);
|
|
this.textarea_ = textarea;
|
|
this.textarea_.style.margin = (Blockly.WorkspaceCommentSvg.TEXTAREA_OFFSET) + 'px';
|
|
this.foreignObject_.appendChild(body);
|
|
Blockly.bindEventWithChecks_(textarea, 'mousedown', this, function(e) {
|
|
e.stopPropagation(); // Propagation causes preventDefault from workspace handler
|
|
}, true, true);
|
|
// 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) {
|
|
this.setText(textarea.value);
|
|
}
|
|
});
|
|
|
|
this.labelText_ = this.getLabelText();
|
|
|
|
return this.foreignObject_;
|
|
};
|
|
|
|
/**
|
|
* Add the resize icon to the DOM
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.addResizeDom_ = function() {
|
|
this.resizeGroup_ = Blockly.utils.createSvgElement(
|
|
'g',
|
|
{
|
|
'class': this.RTL ? 'scratchCommentResizeSW' : 'scratchCommentResizeSE'
|
|
},
|
|
this.svgGroup_);
|
|
var resizeSize = Blockly.WorkspaceCommentSvg.RESIZE_SIZE;
|
|
var outerPad = Blockly.ScratchBubble.RESIZE_OUTER_PAD;
|
|
var cornerPad = Blockly.ScratchBubble.RESIZE_CORNER_PAD;
|
|
// Build an (invisible) triangle that will catch resizes. It is padded on the
|
|
// top/left by outerPad, and padded down/right by cornerPad.
|
|
Blockly.utils.createSvgElement('polygon',
|
|
{
|
|
'points': [
|
|
-outerPad, resizeSize + cornerPad,
|
|
resizeSize + cornerPad, resizeSize + cornerPad,
|
|
resizeSize + cornerPad, -outerPad
|
|
].join(' ')
|
|
},
|
|
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_);
|
|
};
|
|
|
|
/**
|
|
* Create the comment top bar and its contents.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.createCommentTopBar_ = function() {
|
|
this.svgHandleTarget_ = Blockly.utils.createSvgElement('rect',
|
|
{
|
|
'class': 'blocklyDraggable scratchCommentTopBar',
|
|
'rx': Blockly.WorkspaceCommentSvg.BORDER_WIDTH,
|
|
'ry': Blockly.WorkspaceCommentSvg.BORDER_WIDTH,
|
|
'height': Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT
|
|
}, this.svgGroup_);
|
|
|
|
this.createTopBarIcons_();
|
|
this.createTopBarLabel_();
|
|
};
|
|
|
|
/**
|
|
* Create the comment top bar label. This is the truncated comment text
|
|
* that shows when comment is minimized.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.createTopBarLabel_ = function() {
|
|
this.topBarLabel_ = Blockly.utils.createSvgElement('text',
|
|
{
|
|
'class': 'scratchCommentText',
|
|
'x': this.width_ / 2,
|
|
'y': (Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT / 2) + Blockly.WorkspaceCommentSvg.BORDER_WIDTH,
|
|
'text-anchor': 'middle',
|
|
'dominant-baseline': 'middle'
|
|
}, this.svgGroup_);
|
|
|
|
var labelTextNode = document.createTextNode(this.labelText_);
|
|
this.topBarLabel_.appendChild(labelTextNode);
|
|
};
|
|
|
|
/**
|
|
* Create the minimize toggle and delete icons that in the comment top bar.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.createTopBarIcons_ = function() {
|
|
var topBarMiddleY = (Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT / 2) +
|
|
Blockly.WorkspaceCommentSvg.BORDER_WIDTH;
|
|
|
|
// Minimize Toggle Icon in Comment Top Bar
|
|
var xInset = Blockly.WorkspaceCommentSvg.TOP_BAR_ICON_INSET;
|
|
this.minimizeArrow_ = Blockly.utils.createSvgElement('image',
|
|
{
|
|
'x': xInset,
|
|
'y': topBarMiddleY - Blockly.WorkspaceCommentSvg.MINIMIZE_ICON_SIZE / 2,
|
|
'width': Blockly.WorkspaceCommentSvg.MINIMIZE_ICON_SIZE,
|
|
'height': Blockly.WorkspaceCommentSvg.MINIMIZE_ICON_SIZE
|
|
}, this.svgGroup_);
|
|
|
|
// Delete Icon in Comment Top Bar
|
|
this.deleteIcon_ = Blockly.utils.createSvgElement('image',
|
|
{
|
|
'x': xInset,
|
|
'y': topBarMiddleY - Blockly.WorkspaceCommentSvg.DELETE_ICON_SIZE / 2,
|
|
'width': Blockly.WorkspaceCommentSvg.DELETE_ICON_SIZE,
|
|
'height': Blockly.WorkspaceCommentSvg.DELETE_ICON_SIZE
|
|
}, this.svgGroup_);
|
|
this.deleteIcon_.setAttributeNS('http://www.w3.org/1999/xlink',
|
|
'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'delete-x.svg');
|
|
};
|
|
|
|
/**
|
|
* Handle a mouse-down on bubble's minimize icon.
|
|
* @param {!Event} e Mouse down event.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.minimizeArrowMouseDown_ = function(e) {
|
|
// Set a property to indicate that this minimize arrow icon had a mouse down
|
|
// event. This property will get reset if the mouse leaves the icon, or when
|
|
// a mouse up event occurs on this icon.
|
|
this.shouldToggleMinimize_ = true;
|
|
e.stopPropagation();
|
|
};
|
|
|
|
/**
|
|
* Handle a mouse-out on bubble's minimize icon.
|
|
* @param {!Event} _e Mouse out event.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.minimizeArrowMouseOut_ = function(_e) {
|
|
// If the mouse leaves the minimize arrow icon, make sure the
|
|
// shouldToggleMinimize_ property gets reset.
|
|
this.shouldToggleMinimize_ = false;
|
|
};
|
|
|
|
/**
|
|
* Handle a mouse-up on bubble's minimize icon.
|
|
* @param {!Event} e Mouse up event.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.minimizeArrowMouseUp_ = function(e) {
|
|
// First check if this is the icon that had a mouse down event on it and that
|
|
// the mouse never left the icon.
|
|
if (this.shouldToggleMinimize_) {
|
|
this.shouldToggleMinimize = false;
|
|
this.toggleMinimize_();
|
|
}
|
|
e.stopPropagation();
|
|
};
|
|
|
|
/**
|
|
* Handle a mouse-down on bubble's minimize icon.
|
|
* @param {!Event} e Mouse down event.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.deleteMouseDown_ = function(e) {
|
|
// Set a property to indicate that this delete icon had a mouse down event.
|
|
// This property will get reset if the mouse leaves the icon, or when
|
|
// a mouse up event occurs on this icon.
|
|
this.shouldDelete_ = true;
|
|
e.stopPropagation();
|
|
};
|
|
|
|
/**
|
|
* Handle a mouse-out on bubble's minimize icon.
|
|
* @param {!Event} _e Mouse out event.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.deleteMouseOut_ = function(_e) {
|
|
// If the mouse leaves the delete icon, reset the shouldDelete_ property.
|
|
this.shouldDelete_ = false;
|
|
};
|
|
|
|
/**
|
|
* Handle a mouse-up on bubble's delete icon.
|
|
* @param {!Event} e Mouse up event.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.deleteMouseUp_ = function(e) {
|
|
// First check that this same icon had a mouse down event on it and that the
|
|
// mouse never left the icon.
|
|
if (this.shouldDelete_) {
|
|
this.dispose();
|
|
}
|
|
e.stopPropagation();
|
|
};
|
|
|
|
/**
|
|
* Handle a mouse-down on comment's resize corner.
|
|
* @param {!Event} e Mouse down event.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.resizeMouseDown_ = function(e) {
|
|
this.resizeStartSize_ = {width: this.width_, height: this.height_};
|
|
this.unbindDragEvents_();
|
|
this.workspace.setResizesEnabled(false);
|
|
if (Blockly.utils.isRightButton(e)) {
|
|
// No right-click.
|
|
e.stopPropagation();
|
|
return;
|
|
}
|
|
// Left-click (or middle click)
|
|
this.workspace.startDrag(e, new goog.math.Coordinate(
|
|
this.workspace.RTL ? -this.width_ : this.width_, this.height_));
|
|
|
|
this.onMouseUpWrapper_ = Blockly.bindEventWithChecks_(
|
|
document, 'mouseup', this, this.resizeMouseUp_);
|
|
this.onMouseMoveWrapper_ = Blockly.bindEventWithChecks_(
|
|
document, 'mousemove', this, this.resizeMouseMove_);
|
|
Blockly.hideChaff();
|
|
// This event has been handled. No need to bubble up to the document.
|
|
e.stopPropagation();
|
|
};
|
|
|
|
|
|
/**
|
|
* Set the apperance of the workspace comment bubble to the minimized or full size
|
|
* appearance. In the minimized state, the comment should only have the top bar
|
|
* displayed, with the minimize icon swapped to the minimized state, and
|
|
* truncated comment text is shown in the middle of the top bar. There should be
|
|
* no resize handle when the workspace comment is in its minimized state.
|
|
* @param {boolean} minimize Whether the bubble should be minimized
|
|
* @param {?string} labelText Optional label text for the comment top bar
|
|
* when it is minimized.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.setRenderedMinimizeState_ = function(minimize, labelText) {
|
|
if (minimize) {
|
|
// 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_.textContent = labelText;
|
|
}
|
|
Blockly.utils.removeAttribute(this.topBarLabel_, 'display');
|
|
} else {
|
|
// 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');
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Stop binding to the global mouseup and mousemove events.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.unbindDragEvents_ = function() {
|
|
if (this.onMouseUpWrapper_) {
|
|
Blockly.unbindEvent_(this.onMouseUpWrapper_);
|
|
this.onMouseUpWrapper_ = null;
|
|
}
|
|
if (this.onMouseMoveWrapper_) {
|
|
Blockly.unbindEvent_(this.onMouseMoveWrapper_);
|
|
this.onMouseMoveWrapper_ = null;
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Handle a mouse-up event while dragging a comment's border or resize handle.
|
|
* @param {!Event} e Mouse up event.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.resizeMouseUp_ = function(/*e*/) {
|
|
Blockly.Touch.clearTouchIdentifier();
|
|
this.unbindDragEvents_();
|
|
var oldHW = this.resizeStartSize_;
|
|
this.resizeStartSize_ = null;
|
|
if (this.width_ == oldHW.width && this.height_ == oldHW.height) {
|
|
return;
|
|
}
|
|
// Fire a change event for the new width/height after
|
|
// resize mouse up
|
|
Blockly.Events.fire(new Blockly.Events.CommentChange(
|
|
this, {width: oldHW.width , height: oldHW.height},
|
|
{width: this.width_, height: this.height_}));
|
|
|
|
this.workspace.setResizesEnabled(true);
|
|
};
|
|
|
|
/**
|
|
* Resize this comment to follow the mouse.
|
|
* @param {!Event} e Mouse move event.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.resizeMouseMove_ = function(e) {
|
|
this.autoLayout_ = false;
|
|
var newXY = this.workspace.moveDrag(e);
|
|
// The call to setSize below emits a CommentChange event,
|
|
// but we don't want multiple CommentChange events to be
|
|
// emitted while the user is still in the process of resizing
|
|
// the comment, so disable events here. The event is emitted in
|
|
// resizeMouseUp_.
|
|
var disabled = false;
|
|
if (Blockly.Events.isEnabled()) {
|
|
Blockly.Events.disable();
|
|
disabled = true;
|
|
}
|
|
this.setSize(this.RTL ? -newXY.x : newXY.x, newXY.y);
|
|
if (disabled) {
|
|
Blockly.Events.enable();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Callback function triggered when the comment has resized.
|
|
* Resize the text area accordingly.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.resizeComment_ = function() {
|
|
var doubleBorderWidth = 2 * Blockly.WorkspaceCommentSvg.BORDER_WIDTH;
|
|
var topOffset = Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT;
|
|
var textOffset = Blockly.WorkspaceCommentSvg.TEXTAREA_OFFSET * 2;
|
|
|
|
this.foreignObject_.setAttribute('width',
|
|
this.width_ - doubleBorderWidth);
|
|
this.foreignObject_.setAttribute('height',
|
|
this.height_ - doubleBorderWidth - topOffset);
|
|
if (this.RTL) {
|
|
this.foreignObject_.setAttribute('x',
|
|
-this.width_);
|
|
}
|
|
this.textarea_.style.width =
|
|
(this.width_ - textOffset) + 'px';
|
|
this.textarea_.style.height =
|
|
(this.height_ - doubleBorderWidth - textOffset - topOffset) + 'px';
|
|
};
|
|
|
|
/**
|
|
* Set size
|
|
* @param {number} width width of the container
|
|
* @param {number} height height of the container
|
|
* @package
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.setSize = function(width, height) {
|
|
var oldWidth = this.width_;
|
|
var oldHeight = this.height_;
|
|
|
|
var doubleBorderWidth = 2 * Blockly.WorkspaceCommentSvg.BORDER_WIDTH;
|
|
|
|
if (this.isMinimized_) {
|
|
width = Blockly.WorkspaceCommentSvg.MINIMIZE_WIDTH;
|
|
height = Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT;
|
|
} else {
|
|
// Minimum size of a 'full size' (not minimized) comment.
|
|
width = Math.max(width, doubleBorderWidth + 50);
|
|
height = Math.max(height, doubleBorderWidth + 20 + Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT);
|
|
|
|
// Note we are only updating this.width_ or this.height_ here
|
|
// and not in the case above, because when we're minimizing a comment,
|
|
// we want to keep track of the width/height of the maximized comment
|
|
this.width_ = width;
|
|
this.height_ = height;
|
|
Blockly.Events.fire(new Blockly.Events.CommentChange(this,
|
|
{width: oldWidth, height: oldHeight},
|
|
{width: this.width_, height: this.height_}));
|
|
}
|
|
this.svgRect_.setAttribute('width', width);
|
|
this.svgRect_.setAttribute('height', height);
|
|
this.svgRectTarget_.setAttribute('width', width);
|
|
this.svgRectTarget_.setAttribute('height', height - Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT);
|
|
this.svgHandleTarget_.setAttribute('width', width);
|
|
this.svgHandleTarget_.setAttribute('height', Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT);
|
|
if (this.RTL) {
|
|
this.minimizeArrow_.setAttribute('x', width -
|
|
(Blockly.WorkspaceCommentSvg.MINIMIZE_ICON_SIZE) -
|
|
Blockly.WorkspaceCommentSvg.TOP_BAR_ICON_INSET);
|
|
this.deleteIcon_.setAttribute('x', (-width +
|
|
Blockly.WorkspaceCommentSvg.TOP_BAR_ICON_INSET));
|
|
this.svgRect_.setAttribute('transform', 'scale(-1 1)');
|
|
this.svgHandleTarget_.setAttribute('transform', 'scale(-1 1)');
|
|
this.svgHandleTarget_.setAttribute('transform', 'translate(' + -width + ', 1)');
|
|
this.minimizeArrow_.setAttribute('transform', 'translate(' + -width + ', 1)');
|
|
this.deleteIcon_.setAttribute('tranform', 'translate(' + -width + ', 1)');
|
|
this.svgRectTarget_.setAttribute('transform', 'translate(' + -width + ', 1)');
|
|
this.topBarLabel_.setAttribute('transform', 'translate(' + -width + ', 1)');
|
|
} else {
|
|
this.deleteIcon_.setAttribute('x', width -
|
|
Blockly.WorkspaceCommentSvg.DELETE_ICON_SIZE -
|
|
Blockly.WorkspaceCommentSvg.TOP_BAR_ICON_INSET);
|
|
}
|
|
|
|
var resizeSize = Blockly.WorkspaceCommentSvg.RESIZE_SIZE;
|
|
if (this.resizeGroup_) {
|
|
if (this.RTL) {
|
|
// Mirror the resize group.
|
|
this.resizeGroup_.setAttribute('transform', 'translate(' +
|
|
(-width + doubleBorderWidth + resizeSize) + ',' +
|
|
(height - doubleBorderWidth - resizeSize) + ') scale(-1 1)');
|
|
} else {
|
|
this.resizeGroup_.setAttribute('transform', 'translate(' +
|
|
(width - doubleBorderWidth - resizeSize) + ',' +
|
|
(height - doubleBorderWidth - resizeSize) + ')');
|
|
}
|
|
}
|
|
|
|
if (this.isMinimized_) {
|
|
this.topBarLabel_.setAttribute('x', width / 2);
|
|
this.topBarLabel_.setAttribute('y', height / 2);
|
|
}
|
|
|
|
// Allow the contents to resize.
|
|
this.resizeComment_();
|
|
};
|
|
|
|
/**
|
|
* Toggle the minimization state of this comment.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceComment.prototype.toggleMinimize_ = function() {
|
|
this.setMinimized(!this.isMinimized_);
|
|
};
|
|
|
|
/**
|
|
* Set the minimized state for this comment. If the comment is rendered,
|
|
* change the appearance of the comment accordingly.
|
|
* @param {boolean} minimize Whether the comment should be minimized
|
|
* @package
|
|
*/
|
|
Blockly.WorkspaceComment.prototype.setMinimized = function(minimize) {
|
|
if (this.isMinimized_ == minimize) {
|
|
return;
|
|
}
|
|
Blockly.Events.fire(new Blockly.Events.CommentChange(this,
|
|
{minimized: this.isMinimized_}, {minimized: minimize}));
|
|
this.isMinimized_ = minimize;
|
|
if (minimize) {
|
|
if (this.rendered_) {
|
|
this.setRenderedMinimizeState_(true, this.getLabelText());
|
|
}
|
|
this.setSize(Blockly.WorkspaceCommentSvg.MINIMIZE_WIDTH,
|
|
Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT);
|
|
} else {
|
|
if (this.rendered_) {
|
|
this.setRenderedMinimizeState_(false);
|
|
}
|
|
this.setText(this.content_);
|
|
this.setSize(this.width_, this.height_);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Dispose of any rendered comment components.
|
|
* @private
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.disposeInternal_ = function() {
|
|
this.textarea_ = null;
|
|
this.foreignObject_ = null;
|
|
this.svgRect_ = null;
|
|
this.svgRectTarget_ = null;
|
|
this.svgHandleTarget_ = null;
|
|
};
|
|
|
|
/**
|
|
* Set the focus on the text area.
|
|
* @package
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.setFocus = function() {
|
|
var comment = this;
|
|
this.focused_ = true;
|
|
comment.textarea_.focus();
|
|
// Defer CSS changes.
|
|
setTimeout(function() {
|
|
comment.addFocus();
|
|
Blockly.utils.addClass(
|
|
comment.svgRectTarget_, 'scratchCommentTargetFocused');
|
|
Blockly.utils.addClass(
|
|
comment.svgHandleTarget_, 'scratchCommentHandleTargetFocused');
|
|
}, 0);
|
|
};
|
|
|
|
/**
|
|
* Remove focus from the text area.
|
|
* @package
|
|
*/
|
|
Blockly.WorkspaceCommentSvg.prototype.blurFocus = function() {
|
|
var comment = this;
|
|
this.focused_ = false;
|
|
comment.textarea_.blur();
|
|
// Defer CSS changes.
|
|
setTimeout(function() {
|
|
if (comment.svgGroup_) { // Could have been deleted in the meantime
|
|
comment.removeFocus();
|
|
Blockly.utils.removeClass(
|
|
comment.svgRectTarget_, 'scratchCommentTargetFocused');
|
|
Blockly.utils.removeClass(
|
|
comment.svgHandleTarget_, 'scratchCommentHandleTargetFocused');
|
|
}
|
|
}, 0);
|
|
};
|