Files
blockly/core/workspace_comment.js
Rachel Fenichel ee6f2ea097 Comments v3 (#1817)
* Add skeleton for workspace comments

* XML parsing and encoding of workspace comments.

* Minor fix: piping the height and width from xml to newWorkspaceComment

* Move height and width into workspace_comment_svg

* rename newWorkspaceComment to newComment

* minor refactoring. PR changes

* Functions for managing the comment's lifecycle

* Add initial tests

* Add another test

* Add basic rendering of a comment.

* Cleanup remaining highlighting steps from render

* Fix lint

* Fix aslant

* Add basic comment translate

* Simplify render code into one setPath method

* Move steps to setPath_

* Remove svg elements when disposing of a comment; some code cleanup

* Add a workspace comment on context menu click and position it where the initial context menu was clicked.

* Minor rendering changes, fixes RTL. Fix positioning of new (context menu) comments while workspace is scaled.

* PR feedback

* Gesture code for dragging comments

* Add comment (block drag) surface methods

* minor comment fix

* Comment fixes

* Add comment dragger

* Making rendered private

* Require CommentDragger

* Make basic comment dragging work

* Increase the border around the comment to make a bigger drag handle

* Remove typo

* Allow comments to be selected. Highlight selected comment. Only edit comment on click. Updated comment rendering.

* minor refactor: remove commented out code

* PR comments

* lint and rebuild

* Fix renamed function call

* Fix workspace getMetrics by storing comment size as a number, not a string

* Enable comment deletion when dragging over the toolbox or trash can

* Give issue references to some todos

* Create a helper function for workspace comment creation

* Integrate sam's workspace comments, using the bubble dragger

* Remove comment_dragger references

* Remove comment dragger.js

* Remove pointer handling

* Fix lint

* Move comment XML functions into the comment files.

* Fix tests

* Fix type annotations

* Fix comments on comments

* Fix compiler errors related to visibility.

* Fix merge issues and add an issue number to a TODO

* Add a new message for default text on workspace comments, and rebuild

* Add support for a context menu on workspace comment showing delete and duplication options.
Add copy and paste support.

* PR comment feedback

* Show a delete icon on the comment when selected. Delete icon deletes the comment. Comment can be deleted if dragged onto the toolbox or the trash icon. A normal bubble cannot be deleted that way.

* use isDeletable instead

* Support drag of the comment during editing mode using the top handle.

* Add skeletons for all workspace comment events

* Rebuild with new comments

* Get rid of confused TODO

* JSDoc on a function

* Fix broken tests

* More PR feedback

* Fix lint

* Delete comment on mouse out, highlight on mouse down.

* Fix lint.

* Show delete hand cursor when dragging a comment to delete over the toolbox

* Focus textarea on select

* Add delete events

* Remove workspace comment create event, and add TODO placeholder

* Provide default values if comment height and width are missing in XML

* Set comment handle fill to none by default

* Rebuild

* Comment de/serialization should include location.

* Add comment move events, with undo and redo

* Add comment change events

* Move files up to core

* Add package/private annotations wherever possible

* Move the workspace comment events up to core and into a single file

* Mark things package or private where possible

* Get rid of unnecessary changes to messge files

* Fix lint

* Fix some review feedback

* Make changes to the comment db happen in addTopComment and removeTopComment

* Add css classes for toggling comment focus

* Clean up css for comment focus

* Rebuild
2018-04-27 15:18:59 -07:00

371 lines
9.5 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 Object representing a code comment on the workspace.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.provide('Blockly.WorkspaceComment');
goog.require('Blockly.Events.CommentChange');
goog.require('Blockly.Events.CommentCreate');
goog.require('Blockly.Events.CommentDelete');
goog.require('Blockly.Events.CommentMove');
goog.require('goog.math.Coordinate');
/**
* Class for a workspace comment.
* @param {!Blockly.Workspace} workspace The block's workspace.
* @param {string} content The content of this workspace comment.
* @param {number} height Height of the comment.
* @param {number} width Width of the comment.
* @param {string=} opt_id Optional ID. Use this ID if provided, otherwise
* create a new ID.
* @constructor
*/
Blockly.WorkspaceComment = function(workspace, content, height, width, opt_id) {
/** @type {string} */
this.id = (opt_id && !workspace.getCommentById(opt_id)) ?
opt_id : Blockly.utils.genUid();
workspace.addTopComment(this);
/**
* The comment's position in workspace units. (0, 0) is at the workspace's
* origin; scale does not change this value.
* @type {!goog.math.Coordinate}
* @protected
*/
this.xy_ = new goog.math.Coordinate(0, 0);
/**
* The comment's height in workspace units. Scale does not change this value.
* @type {number}
* @private
*/
this.height_ = height;
/**
* The comment's width in workspace units. Scale does not change this value.
* @type {number}
* @private
*/
this.width_ = width;
/**
* @type {!Blockly.Workspace}
*/
this.workspace = workspace;
/**
* @protected
* @type {boolean}
*/
this.RTL = workspace.RTL;
/**
* @type {boolean}
* @private
*/
this.deletable_ = true;
/**
* @type {boolean}
* @private
*/
this.movable_ = true;
/**
* @protected
* @type {!string}
*/
this.content_ = content;
/**
* @package
* @type {boolean}
*/
this.isComment = true;
Blockly.WorkspaceComment.fireCreateEvent(this);
};
/**
* Dispose of this comment.
* @package
*/
Blockly.WorkspaceComment.prototype.dispose = function() {
if (!this.workspace) {
// The comment has already been deleted.
return;
}
if (Blockly.Events.isEnabled()) {
Blockly.Events.fire(new Blockly.Events.CommentDelete(this));
}
// Remove from the list of top comments and the comment database.
this.workspace.removeTopComment(this);
this.workspace = null;
};
// Height, width, x, and y are all stored on even non-rendered comments, to
// preserve state if you pass the contents through a headless workspace.
/**
* Get comment height.
* @return {number} comment height.
* @package
*/
Blockly.WorkspaceComment.prototype.getHeight = function() {
return this.height_;
};
/**
* Set comment height.
* @param {number} height comment height.
* @package
*/
Blockly.WorkspaceComment.prototype.setHeight = function(height) {
this.height_ = height;
};
/**
* Get comment width.
* @return {number} comment width.
* @package
*/
Blockly.WorkspaceComment.prototype.getWidth = function() {
return this.width_;
};
/**
* Set comment width.
* @param {number} width comment width.
* @package
*/
Blockly.WorkspaceComment.prototype.setWidth = function(width) {
this.width_ = width;
};
/**
* Get stored location.
* @return {!goog.math.Coordinate} The comment's stored location. This is not
* valid if the comment is currently being dragged.
* @package
*/
Blockly.WorkspaceComment.prototype.getXY = function() {
return this.xy_.clone();
};
/**
* Move a comment by a relative offset.
* @param {number} dx Horizontal offset, in workspace units.
* @param {number} dy Vertical offset, in workspace units.
* @package
*/
Blockly.WorkspaceComment.prototype.moveBy = function(dx, dy) {
var event = new Blockly.Events.CommentMove(this);
this.xy_.translate(dx, dy);
event.recordNew();
Blockly.Events.fire(event);
};
/**
* Get whether this comment is deletable or not.
* @return {boolean} True if deletable.
* @package
*/
Blockly.WorkspaceComment.prototype.isDeletable = function() {
return this.deletable_ &&
!(this.workspace && this.workspace.options.readOnly);
};
/**
* Set whether this comment is deletable or not.
* @param {boolean} deletable True if deletable.
* @package
*/
Blockly.WorkspaceComment.prototype.setDeletable = function(deletable) {
this.deletable_ = deletable;
};
/**
* Get whether this comment is movable or not.
* @return {boolean} True if movable.
* @package
*/
Blockly.WorkspaceComment.prototype.isMovable = function() {
return this.movable_ &&
!(this.workspace && this.workspace.options.readOnly);
};
/**
* Set whether this comment is movable or not.
* @param {boolean} movable True if movable.
* @package
*/
Blockly.WorkspaceComment.prototype.setMovable = function(movable) {
this.movable_ = movable;
};
/**
* Returns this comment's text.
* @return {string} Comment text.
* @package
*/
Blockly.WorkspaceComment.prototype.getContent = function() {
return this.content_;
};
/**
* Set this comment's content.
* @param {string} content Comment content.
* @package
*/
Blockly.WorkspaceComment.prototype.setContent = function(content) {
if (this.content_ != content) {
Blockly.Events.fire(
new Blockly.Events.CommentChange(this, this.content_, content));
this.content_ = content;
}
};
/**
* Encode a comment subtree as XML with XY coordinates.
* @param {boolean=} opt_noId True if the encoder should skip the comment id.
* @return {!Element} Tree of XML elements.
* @package
*/
Blockly.WorkspaceComment.prototype.toXmlWithXY = function(opt_noId) {
var element = this.toXml(opt_noId);
element.setAttribute('x', Math.round(this.xy_.x));
element.setAttribute('y', Math.round(this.xy_.y));
element.setAttribute('h', this.height_);
element.setAttribute('w', this.width_);
return element;
};
/**
* Encode a comment subtree as XML, but don't serialize the XY coordinates.
* This method avoids some expensive metrics-related calls that are made in
* toXmlWithXY().
* @param {boolean=} opt_noId True if the encoder should skip the comment id.
* @return {!Element} Tree of XML elements.
* @package
*/
Blockly.WorkspaceComment.prototype.toXml = function(opt_noId) {
var commentElement = goog.dom.createDom('comment');
if (!opt_noId) {
commentElement.setAttribute('id', this.id);
}
commentElement.textContent = this.getContent();
return commentElement;
};
/**
* Fire a create event for the given workspace comment, if comments are enabled.
* @param {!Blockly.WorkspaceComment} comment The comment that was just created.
* @package
*/
Blockly.WorkspaceComment.fireCreateEvent = function(comment) {
if (Blockly.Events.isEnabled()) {
var existingGroup = Blockly.Events.getGroup();
if (!existingGroup) {
Blockly.Events.setGroup(true);
}
try {
Blockly.Events.fire(new Blockly.Events.CommentCreate(comment));
} finally {
if (!existingGroup) {
Blockly.Events.setGroup(false);
}
}
}
};
/**
* Decode an XML comment tag and create a comment on the workspace.
* @param {!Element} xmlComment XML comment element.
* @param {!Blockly.Workspace} workspace The workspace.
* @return {!Blockly.WorkspaceComment} The created workspace comment.
* @package
*/
Blockly.WorkspaceComment.fromXml = function(xmlComment, workspace) {
var info = Blockly.WorkspaceComment.parseAttributes(xmlComment);
var comment = new Blockly.WorkspaceComment(
workspace, info.content, info.h, info.w, info.id);
var commentX = parseInt(xmlComment.getAttribute('x'), 10);
var commentY = parseInt(xmlComment.getAttribute('y'), 10);
if (!isNaN(commentX) && !isNaN(commentY)) {
comment.moveBy(commentX, commentY);
}
Blockly.WorkspaceComment.fireCreateEvent(comment);
return comment;
};
/**
* Decode an XML comment tag and return the results in an object.
* @param {!Element} xml XML comment element.
* @return {!Object} An object containing the information about the comment.
* @package
*/
Blockly.WorkspaceComment.parseAttributes = function(xml) {
var xmlH = xml.getAttribute('h');
var xmlW = xml.getAttribute('w');
return {
/* @type {string} */
id: xml.getAttribute('id'),
/**
* The height of the comment in workspace units, or 100 if not specified.
* @type {number}
*/
h: xmlH ? parseInt(xmlH, 10) : 100,
/**
* The width of the comment in workspace units, or 100 if not specified.
* @type {number}
*/
w: xmlW ? parseInt(xmlW, 10) : 100,
/**
* The x position of the comment in workspace coordinates, or NaN if not
* specified in the XML.
* @type {number}
*/
x: parseInt(xml.getAttribute('x'), 10),
/**
* The y position of the comment in workspace coordinates, or NaN if not
* specified in the XML.
* @type {number}
*/
y: parseInt(xml.getAttribute('y'), 10),
/* @type {string} */
content: xml.textContent
};
};