Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Textarea Field, Multiline Block #2584

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
220297c
Field Textarea, text_multiline block, generators
acbart Jun 22, 2019
ffd43f8
Pilcrow icon for multiline text block
acbart Jun 22, 2019
261a5ac
Merge branch 'develop' of https://github.com/google/blockly into develop
acbart Jun 22, 2019
083c660
Fix linting
acbart Jun 22, 2019
3c0b0a9
Fix round trip XML
acbart Jun 23, 2019
78385d2
Fix license year
acbart Jun 23, 2019
6657cb2
Fix requires to match new style
acbart Jun 23, 2019
328187a
Removed unnecessary argument for constructor call
acbart Jun 23, 2019
9a4a069
Clean up old code in render_
acbart Jun 23, 2019
2007b08
Add tabbed navigation back in to block
acbart Jun 23, 2019
c19dead
Remove unnecessary resizeEditor implementation
acbart Jun 23, 2019
83801d9
Adjustments to textarea margins, fix scaling
acbart Jun 23, 2019
77b4436
Remove pilcrow mixin, make static image
acbart Jun 23, 2019
9a39a3a
Clean up code
acbart Jun 23, 2019
593d314
Add multiline text block to other playgrounds
acbart Jun 23, 2019
c32a106
Fixed linting
acbart Jun 23, 2019
b1879de
Rename field_textarea to field_multilinetext
acbart Jun 25, 2019
1e4e54e
Fixed unnecessary attribute, pull out magic number for lineHeight
acbart Jun 25, 2019
0ab1261
Fix line height variable access
acbart Jun 25, 2019
46b3e4a
Rebuild
acbart Jun 25, 2019
afb5f95
Checked linting, extracted more constants
acbart Jun 25, 2019
05a6c5c
Change input field to/from XML to encode rather than use complex attr…
acbart Jun 27, 2019
eebafe9
Move field from/to xml decoding to Xml area. Also wrap mulitiline jav…
acbart Jun 28, 2019
9e74ca7
Neil's fix for roundtripping xml
acbart Jun 29, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
412 changes: 211 additions & 201 deletions blockly_accessible_compressed.js

Large diffs are not rendered by default.

181 changes: 87 additions & 94 deletions blockly_accessible_uncompressed.js

Large diffs are not rendered by default.

414 changes: 212 additions & 202 deletions blockly_compressed.js

Large diffs are not rendered by default.

181 changes: 87 additions & 94 deletions blockly_uncompressed.js

Large diffs are not rendered by default.

94 changes: 94 additions & 0 deletions blocks/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,23 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT
"parent_tooltip_when_inline"
]
},
{
"type": "text_multiline",
"message0": "%1",
"args0": [{
"type": "field_textarea",
"name": "TEXT",
"text": ""
}],
"output": "String",
"style": "text_blocks",
"helpUrl": "%{BKY_TEXT_TEXT_HELPURL}",
"tooltip": "%{BKY_TEXT_TEXT_TOOLTIP}",
"extensions": [
"text_pilcrow",
"parent_tooltip_when_inline"
]
},
{
"type": "text_join",
"message0": "",
Expand Down Expand Up @@ -647,6 +664,80 @@ Blockly.Constants.Text.TEXT_QUOTES_EXTENSION = function() {
this.quoteField_('TEXT');
};

/**
*
* @mixin
* @package
* @readonly
*/
Blockly.Constants.Text.PILCROW_IMAGE_MIXIN = {
acbart marked this conversation as resolved.
Show resolved Hide resolved
/**
* Image data URI of a Pilcrow
* Pilcrow
* https://commons.wikimedia.org/wiki/File:Pilcrow.svg
* @readonly
*/
QUOTE_IMAGE_PILCROW:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAARCAYAAADpPU2iAAAA' +
'BGdBTUEAALGPC/xhBQAAAAlwSFlzAAAdhgAAHYYBXaITgQAAABh0RVh0U29mdHdhcmUAcG' +
'FpbnQubmV0IDQuMS42/U4J6AAAAP1JREFUOE+Vks0KQUEYhjmRIja4ABtZ2dm5A3t3Ia6A' +
'Um7CylYuQRaUhZSlLZJiQbFAyRnPN33y01HOW08z8873zpwzM4F3GWOCruvGIE4/rLaV+N' +
'q1hVGMBqzhqlxgCys4wJA65xnogMHsQ5lujnYHTejBBCK2mE4abjCgMGhNxHgDFWjDSG07' +
'kdfVa2pZMf4ZyMAdWmpZMfYOsLiDMYMjlMB+K613QISRhTnITnsYg5yUd0DETmEoMlkFOe' +
'IT/A58iyK5E18BuTBfgYXfwNJv4P9/oEBerLylOnRhygmGdPpTTBZAPkde61lbQe4moWUv' +
'YUZYLfUNftIY4zwA5X2Z9AYnQrEAAAAASUVORK5CYII=',
/**
* Pixel width of QUOTE_IMAGE_PILCROW.
* @readonly
*/
QUOTE_IMAGE_WIDTH: 12,
/**
* Pixel height of QUOTE_IMAGE_PILCROW.
* @readonly
*/
QUOTE_IMAGE_HEIGHT: 17,

/**
* Inserts appropriate quote images before the named field.
* @param {string} fieldName The name of the field to wrap with quotes.
* @this Blockly.Block
*/
prependField_: function(fieldName) {
for (var i = 0, input; input = this.inputList[i]; i++) {
for (var j = 0, field; field = input.fieldRow[j]; j++) {
if (fieldName == field.name) {
input.insertFieldAt(j, this.newPilcrow_());
return;
}
}
}
console.warn('field named "' + fieldName + '" not found in ' + this.toDevString());
},

/**
* A helper function that generates a FieldImage of a Pilcrow.
* The selected Pilcrow will be adapted for RTL blocks.
* @return {!Blockly.FieldImage} The new field.
* @this Blockly.Block
*/
newPilcrow_: function() {
return new Blockly.FieldImage(
this.QUOTE_IMAGE_PILCROW,
this.QUOTE_IMAGE_WIDTH,
this.QUOTE_IMAGE_HEIGHT,
'\u00B6');
}
};

/**
* Prepend TEXT field with image of Pilcrow (paragraph mark).
* @this Blockly.Block
*/
Blockly.Constants.Text.TEXT_PILCROW_EXTENSION = function() {
this.mixin(Blockly.Constants.Text.PILCROW_IMAGE_MIXIN);
this.prependField_('TEXT');
};

/**
* Mixin for mutator functions in the 'text_join_mutator' extension.
* @mixin
Expand Down Expand Up @@ -890,6 +981,9 @@ Blockly.Extensions.register('text_indexOf_tooltip',

Blockly.Extensions.register('text_quotes',
Blockly.Constants.Text.TEXT_QUOTES_EXTENSION);

Blockly.Extensions.register('text_pilcrow',
Blockly.Constants.Text.TEXT_PILCROW_EXTENSION);

Blockly.Extensions.registerMutator('text_join_mutator',
Blockly.Constants.Text.TEXT_JOIN_MUTATOR_MIXIN,
Expand Down
20 changes: 12 additions & 8 deletions blocks_compressed.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions core/blockly.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ goog.require('Blockly.FieldDropdown');
goog.require('Blockly.FieldLabelSerializable');
goog.require('Blockly.FieldImage');
goog.require('Blockly.FieldTextInput');
goog.require('Blockly.FieldTextArea');
goog.require('Blockly.FieldNumber');
goog.require('Blockly.FieldVariable');
goog.require('Blockly.Generator');
Expand Down
4 changes: 4 additions & 0 deletions core/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,10 @@ Blockly.Css.CONTENT = [
'font-family: sans-serif;',
'font-size: 11pt;',
'}',

'.blocklyTextCode {',
' font-family: monospace;',
'}',

'.blocklyNonEditableText>text {',
'pointer-events: none;',
Expand Down
217 changes: 217 additions & 0 deletions core/field_textarea.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
/**
* @license
* Visual Blocks Editor
*
* Copyright 2012 Google Inc.
acbart marked this conversation as resolved.
Show resolved Hide resolved
* 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 Text Area field.
* @author fraser@google.com (Neil Fraser)
* @author Andrew Mee
* @author acbart@udel.edu (Austin Cory Bart)
*/
'use strict';

goog.provide('Blockly.FieldTextArea');

goog.require('Blockly.DropDownDiv');
goog.require('Blockly.Field');
goog.require('Blockly.Msg');
goog.require('Blockly.utils');

acbart marked this conversation as resolved.
Show resolved Hide resolved
goog.require('goog.math.Coordinate');
goog.require('goog.userAgent');


/**
* Class for an editable text field.
* @param {string} text The initial content of the field.
* @param {Function=} opt_validator An optional function that is called
* to validate any constraints on what the user entered. Takes the new
* text as an argument and returns either the accepted text, a replacement
* text, or null to abort the change.
* @extends {Blockly.Field}
* @constructor
*/
Blockly.FieldTextArea = function(text, opt_validator) {
Blockly.FieldTextArea.superClass_.constructor.call(this, text,
opt_validator);
};
goog.inherits(Blockly.FieldTextArea, Blockly.FieldTextInput);

/**
* Construct a FieldTextArea from a JSON arg object,
* dereferencing any string table references.
* @param {!Object} options A JSON object with options (text, class, and
* spellcheck).
* @return {!Blockly.FieldTextArea} The new field instance.
* @package
* @nocollapse
*/
Blockly.FieldTextArea.fromJson = function(options) {
var text = Blockly.utils.replaceMessageReferences(options['text']);
var field = new Blockly.FieldTextArea(text, options['class']);
acbart marked this conversation as resolved.
Show resolved Hide resolved
if (typeof options['spellcheck'] === 'boolean') {
field.setSpellcheck(options['spellcheck']);
}
return field;
};

/**
* Get the text from this field as displayed on screen. May differ from getText
* due to ellipsis, and other formatting.
* @return {string} Currently displayed text.
* @private
*/
Blockly.FieldTextArea.prototype.getDisplayText_ = function() {
var value = this.value_;
if (!value) {
// Prevent the field from disappearing if empty.
return Blockly.Field.NBSP;
}
value = value.split("\n").map(function(value) {
if (value.length > this.maxDisplayLength) {
// Truncate displayed string and add an ellipsis ('...').
value = value.substring(0, this.maxDisplayLength - 4) + '...';
}
// Replace whitespace with non-breaking spaces so the value doesn't collapse - keep newline.
value = value.replace(/\s/g, Blockly.Field.NBSP);
return value;
}.bind(this)).join("\n");
if (this.sourceBlock_.RTL) {
// The SVG is LTR, force value to be RTL.
value += '\u200F';
}
return value;
};

/**
* Updates the colour of the htmlInput given the current validity of the
* field's value.
* @protected
*/
Blockly.FieldTextArea.prototype.render_ = function() {
if (!this.visible_) {
acbart marked this conversation as resolved.
Show resolved Hide resolved
this.size_.width = 0;
return;
}
// Replace the text.
var textElement = this.textElement_;
textElement.setAttribute('class', 'blocklyText blocklyTextCode');
acbart marked this conversation as resolved.
Show resolved Hide resolved
goog.dom.removeChildren(/** @type {!Element} */ (textElement));
acbart marked this conversation as resolved.
Show resolved Hide resolved
var txt = this.getDisplayText_();
var y = 0;
var yoffset = 14; // 12.5 is hard-coded in Blockly.Field
var txtLines = txt.split("\n");
txtLines.forEach(function(t) {
Blockly.utils.dom.createSvgElement('tspan', {x:0,y:y + yoffset}, textElement)
.appendChild(document.createTextNode(t));
y += 20;
acbart marked this conversation as resolved.
Show resolved Hide resolved
});
if (txtLines.length == 0) {
y += 20;
}

// set up widths
acbart marked this conversation as resolved.
Show resolved Hide resolved
this.size_.width = this.textElement_.getBBox().width + 5;
this.size_.height = y + (Blockly.BlockSvg.SEP_SPACE_Y + 5) ;
acbart marked this conversation as resolved.
Show resolved Hide resolved

if (this.borderRect_) {
this.borderRect_.setAttribute('width',
this.size_.width + Blockly.BlockSvg.SEP_SPACE_X);
this.borderRect_.setAttribute('height',
this.size_.height - (Blockly.BlockSvg.SEP_SPACE_Y + 5));
acbart marked this conversation as resolved.
Show resolved Hide resolved
}
};

/**
* Create the text input editor widget.
* @return {!HTMLInputElement} The newly created text input editor.
* @private
*/
Blockly.FieldTextArea.prototype.widgetCreate_ = function() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that here you could call the parent function, and then add your extra style tags. If that works, please change the tag on the parent function to protected.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this will work, unfortunately. FieldTextArea creates a textarea element, while FieldTextInput creates an input element. I could refactor widgetCreate_ to create its element based on a field, but that feels outside my scope.

var div = Blockly.WidgetDiv.DIV;

var htmlInput = document.createElement('textarea');
htmlInput.className = 'blocklyHtmlInput';
htmlInput.setAttribute('spellcheck', this.spellcheck_);
var fontSize =
(Blockly.FieldTextInput.FONTSIZE * this.workspace_.scale) + 'pt';
div.style.fontSize = fontSize;
htmlInput.style.fontSize = fontSize;
htmlInput.style.fontFamily = 'monospace';
htmlInput.style.marginTop = "2px";
htmlInput.setAttribute('spellcheck', this.spellcheck_);
acbart marked this conversation as resolved.
Show resolved Hide resolved
htmlInput.style.resize = 'none';
htmlInput.style['line-height'] = '20px';
htmlInput.style['overflow'] = 'hidden';
htmlInput.style['height'] = '100%';
div.appendChild(htmlInput);

htmlInput.value = htmlInput.defaultValue = this.value_;
htmlInput.untypedDefaultValue_ = this.value_;
htmlInput.oldValue_ = null;
this.resizeEditor_();

this.bindInputEvents_(htmlInput);

return htmlInput;
};

/**
* Handle key down to the editor.
* @param {!Event} e Keyboard event.
* @private
*/
Blockly.FieldTextArea.prototype.onHtmlInputKeyDown_ = function(e) {
acbart marked this conversation as resolved.
Show resolved Hide resolved
var escKey = 27;
if (e.keyCode == escKey) {
Blockly.WidgetDiv.hide();
}
};

/**
* Resize the editor to fit the text.
* @protected
*/
Blockly.FieldTextArea.prototype.resizeEditor_ = function() {
acbart marked this conversation as resolved.
Show resolved Hide resolved
var div = Blockly.WidgetDiv.DIV;
var bBox = this.getScaledBBox_();
div.style.width = bBox.right - bBox.left + 'px';
div.style.height = bBox.bottom - bBox.top + 'px';

// In RTL mode block fields and LTR input fields the left edge moves,
// whereas the right edge is fixed. Reposition the editor.
var x = this.sourceBlock_.RTL ? bBox.right - div.offsetWidth : bBox.left;
var xy = new Blockly.utils.Coordinate(x, bBox.top);

// Shift by a few pixels to line up exactly.
xy.y += 1;
if (Blockly.utils.userAgent.GECKO && Blockly.WidgetDiv.DIV.style.top) {
// Firefox mis-reports the location of the border by a pixel
// once the WidgetDiv is moved into position.
xy.x -= 1;
xy.y -= 1;
}
if (Blockly.utils.userAgent.WEBKIT) {
xy.y -= 3;
}
div.style.left = xy.x + 'px';
div.style.top = xy.y + 'px';
};

Blockly.Field.register('field_textarea', Blockly.FieldTextArea);
4 changes: 2 additions & 2 deletions dart_compressed.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions generators/dart.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,20 @@ Blockly.Dart.quote_ = function(string) {
return '\'' + string + '\'';
};

/**
* Encode a string as a properly escaped multiline Dart string, complete with
* quotes.
* @param {string} string Text to encode.
* @return {string} Dart string.
* @private
*/
Blockly.Dart.multiline_quote_ = function(string) {
// Can't use goog.string.quote since $ must also be escaped.
string = string.replace(/'''/g, '\\\'\\\'\\\'');
return '\'\'\'' + string + '\'\'\'';
};


/**
* Common tasks for generating Dart from blocks.
* Handles comments for the specified block and any connected value blocks.
Expand Down
Loading