Skip to content

Text input restriction and application to FieldNumber #429

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

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 6 additions & 9 deletions blocks/math.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,14 @@ Blockly.Blocks.math.HUE = Blockly.Colours.textField;

Blockly.Blocks['math_number'] = {
/**
* Block for numeric value.
* Block for generic numeric value.
* @this Blockly.Block
*/
init: function() {
this.setHelpUrl(Blockly.Msg.MATH_NUMBER_HELPURL);
this.setColour(Blockly.Blocks.math.HUE);
this.appendDummyInput()
.appendField(new Blockly.FieldNumber('0',
Blockly.FieldTextInput.numberValidator, 20, -Infinity, Infinity), 'NUM');
.appendField(new Blockly.FieldNumber('0', true, true), 'NUM');
this.setOutput(true, 'Number');
// Assign 'this' to a variable for use in the tooltip closure below.
var thisBlock = this;
Expand All @@ -61,30 +60,28 @@ Blockly.Blocks['math_number'] = {

Blockly.Blocks['math_whole_number'] = {
/**
* Block for whole number value, with min of 0 and max Infinity, precision 0.
* Block for whole number value, no negatives or decimals.
* @this Blockly.Block
*/
init: function() {
this.setHelpUrl(Blockly.Msg.MATH_NUMBER_HELPURL);
this.setColour(Blockly.Blocks.math.HUE);
this.appendDummyInput()
.appendField(new Blockly.FieldNumber('0',
Blockly.FieldNumber.numberValidator, 0, 0, Infinity), 'NUM');
.appendField(new Blockly.FieldNumber('0', false, false), 'NUM');
this.setOutput(true, 'Number');
}
};

Blockly.Blocks['math_positive_number'] = {
/**
* Block for positive number value, with min of 0 and max Infinity, precision 20.
* Block for positive number value, with decimal.
* @this Blockly.Block
*/
init: function() {
this.setHelpUrl(Blockly.Msg.MATH_NUMBER_HELPURL);
this.setColour(Blockly.Blocks.math.HUE);
this.appendDummyInput()
.appendField(new Blockly.FieldNumber('0',
Blockly.FieldNumber.numberValidator, 20, 0, Infinity), 'NUM');
.appendField(new Blockly.FieldNumber('0', true, false), 'NUM');
this.setOutput(true, 'Number');
}
};
2 changes: 1 addition & 1 deletion core/block_render_svg_vertical.js
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ Blockly.BlockSvg.FIELD_WIDTH_MIN_EDIT = 8 * Blockly.BlockSvg.GRID_UNIT;
* Maximum width of user inputs during editing
* @const
*/
Blockly.BlockSvg.FIELD_WIDTH_MAX_EDIT = 52 * Blockly.BlockSvg.GRID_UNIT;
Blockly.BlockSvg.FIELD_WIDTH_MAX_EDIT = Infinity;

/**
* Maximum height of user inputs during editing
Expand Down
87 changes: 26 additions & 61 deletions core/field_number.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,41 @@ goog.require('goog.math');
goog.require('goog.userAgent');


/**
* Return an appropriate restrictor, depending on whether this FieldNumber
* allows decimal or negative numbers.
* @param {boolean} decimalAllowed Whether number may have decimal/float component.
* @param {boolean} negativeAllowed Whether number may be negative.
* @return {!RegExp} Regular expression for this FieldNumber's restrictor.
*/
var getNumRestrictor = function(decimalAllowed, negativeAllowed) {
var pattern = "[\\d]|[e]"; // Always allow digits and e, from Scratch 2.0.
if (decimalAllowed) {
pattern += "|[\\.]";
}
if (negativeAllowed) {
pattern += "|[-]";
}
return new RegExp(pattern);
};

/**
* Class for an editable number field.
* @param {string} text The initial content of the field.
* @param {boolean} decimalAllowed Whether number may have decimal/float component.
* @param {boolean} negativeAllowed Whether number may be negative.
* @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 the accepted text or null to abort
* the change.
* @param {number} precision Precision of the decimal value (negative power of 10).
* @param {number} min Minimum value of the number.
* @param {number} max Maximum value of the number.
* @extends {Blockly.FieldTextInput}
* @constructor
*/
Blockly.FieldNumber = function(text, opt_validator, precision, min, max) {
this.precision_ = precision;
this.min_ = min;
this.max_ = max;
Blockly.FieldNumber.superClass_.constructor.call(this, text, opt_validator);
Blockly.FieldNumber = function(text, decimalAllowed, negativeAllowed, opt_validator) {

This comment was marked as abuse.

This comment was marked as abuse.

This comment was marked as abuse.

This comment was marked as abuse.

This comment was marked as abuse.

This comment was marked as abuse.

this.decimalAllowed_ = decimalAllowed;
this.negativeAllowed_ = negativeAllowed;
var numRestrictor = getNumRestrictor(decimalAllowed, negativeAllowed);
Blockly.FieldNumber.superClass_.constructor.call(this, text, opt_validator, numRestrictor);
};
goog.inherits(Blockly.FieldNumber, Blockly.FieldTextInput);

Expand Down Expand Up @@ -101,37 +118,6 @@ Blockly.FieldNumber.NUMPAD_DELETE_ICON = 'data:image/svg+xml;utf8,' +
*/
Blockly.FieldNumber.activeField_ = null;

/**
* Sets a new change handler for angle field.
* @param {Function} handler New change handler, or null.
*/
Blockly.FieldNumber.prototype.setValidator = function(handler) {
var wrappedHandler;
if (handler) {
// Wrap the user's change handler together with the number validator.
// This is copied entirely from FieldAngle.
wrappedHandler = function(value) {
var v1 = handler.call(this, value);
var v2;
if (v1 === null) {
v2 = v1;
} else {
if (v1 === undefined) {
v1 = value;
}
v2 = Blockly.FieldNumber.numberValidator.call(this, v1);
if (v2 === undefined) {
v2 = v1;
}
}
return v2 === value ? undefined : v2;
};
} else {
wrappedHandler = Blockly.FieldNumber.numberValidator;
}
Blockly.FieldNumber.superClass_.setValidator.call(this, wrappedHandler);
};

/**
* Show the inline free-text editor on top of the text and the num-pad if appropriate.
* @private
Expand Down Expand Up @@ -170,7 +156,7 @@ Blockly.FieldNumber.prototype.showNumPad_ = function() {
button.innerHTML = buttonText;
Blockly.bindEvent_(button, 'mousedown', button,
Blockly.FieldNumber.numPadButtonTouch_);
if (buttonText == '.' && this.precision_ == 0) {
if (buttonText == '.' && !this.decimalAllowed) {
// Don't show the decimal point for inputs that must be round numbers
button.setAttribute('style', 'visibility: hidden');
}
Expand Down Expand Up @@ -267,27 +253,6 @@ Blockly.FieldNumber.prototype.onHide_ = function() {
Blockly.DropDownDiv.content_.removeAttribute('aria-haspopup');
};

/**
* Ensure that only a number may be entered with the properties of this field.
* @param {string} text The user's text.
* @return {?string} A string representing a valid angle, or null if invalid.
*/
Blockly.FieldNumber.numberValidator = function(text) {
var n = Blockly.FieldTextInput.numberValidator(text);
if (n !== null) {
// string -> float
n = parseFloat(n);
// Keep within min and max
n = Math.min(Math.max(n, this.min_), this.max_);
// Update float precision (returns a string)
n = n.toFixed(this.precision_);
// Parse to a float and back to string to remove trailing decimals
n = parseFloat(n);
n = String(n);
}
return n;
};

/**
* Border radius for drawing this field, called when rendering the owning shadow block.
* @return {Number} Border radius in px.
Expand Down
24 changes: 23 additions & 1 deletion core/field_textinput.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,16 @@ goog.require('goog.userAgent');
* 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.
* @param {RegExp=} opt_restrictor An optional regular expression to restrict
* typed text to. Text that doesn't match the restrictor will never show
* in the text field.
* @extends {Blockly.Field}
* @constructor
*/
Blockly.FieldTextInput = function(text, opt_validator) {
Blockly.FieldTextInput = function(text, opt_validator, opt_restrictor) {
Blockly.FieldTextInput.superClass_.constructor.call(this, text,
opt_validator);
this.setRestrictor(opt_restrictor);
};
goog.inherits(Blockly.FieldTextInput, Blockly.Field);

Expand Down Expand Up @@ -109,6 +113,15 @@ Blockly.FieldTextInput.prototype.setSpellcheck = function(check) {
this.spellcheck_ = check;
};

/**
* Set the restrictor regex for this text input.
* Text that doesn't match the restrictor will never show in the text field.
* @param {?RegExp} restrictor Regular expression to restrict text.
*/
Blockly.FieldTextInput.prototype.setRestrictor = function(restrictor) {
this.restrictor_ = restrictor;
};

/**
* Show the inline free-text editor on top of the text.
* @param {boolean=} opt_quietInput True if editor should be created without
Expand Down Expand Up @@ -208,6 +221,15 @@ Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_ = function(e) {
* @private
*/
Blockly.FieldTextInput.prototype.onHtmlInputChange_ = function(e) {
// Check if the key matches the restrictor.
if (e.type === 'keypress' && this.restrictor_) {
var charCode = String.fromCharCode(e.keyCode);
if (!this.restrictor_.test(charCode) && e.preventDefault) {
// Failed to pass restrictor.
e.preventDefault();
return;
}
}
var htmlInput = Blockly.FieldTextInput.htmlInput_;
// Update source block.
var text = htmlInput.value;
Expand Down
8 changes: 4 additions & 4 deletions tests/vertical_playground.html
Original file line number Diff line number Diff line change
Expand Up @@ -318,15 +318,15 @@
<category name="Control">
<block type="control_wait">
<value name="DURATION">
<shadow type="math_number">
<shadow type="math_positive_number">
<field name="NUM">1</field>
</shadow>
</value>
</block>
<block type="control_repeat">
<value name="TIMES">
<shadow type="math_number">
<field name="NUM">4</field>
<shadow type="math_whole_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
Expand Down Expand Up @@ -468,7 +468,7 @@
</block>
<block type="operator_letter_of">
<value name="LETTER">
<shadow type="math_number">
<shadow type="math_whole_number">
<field name="NUM">1</field>
</shadow>
</value>
Expand Down