mirror of
https://github.com/scratchfoundation/scratch-blocks.git
synced 2025-08-28 22:10:31 -04:00
cleanup and documentation
This commit is contained in:
parent
2a16e1fa32
commit
3bcea6b6fe
1 changed files with 106 additions and 56 deletions
|
@ -33,8 +33,10 @@ goog.require('goog.userAgent');
|
|||
/**
|
||||
* Class for an editable number field.
|
||||
* In scratch-blocks, the min/max/precision properties are only used
|
||||
* to construct a restrictor on typable characters, and to inform the pop-up numpad on touch devices.
|
||||
* These properties are included here (i.e. instead of just accepting a decimalAllowed, negativeAllowed)
|
||||
* to construct a restrictor on typable characters, and to inform the pop-up
|
||||
* numpad on touch devices.
|
||||
* These properties are included here (i.e. instead of just accepting a
|
||||
* decimalAllowed, negativeAllowed)
|
||||
* to maintain API compatibility with Blockly and Blockly for Android.
|
||||
* @param {number|string} value The initial content of the field.
|
||||
* @param {number|string|undefined} opt_min Minimum value.
|
||||
|
@ -47,9 +49,11 @@ goog.require('goog.userAgent');
|
|||
* @extends {Blockly.FieldTextInput}
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.FieldNumber = function(value, opt_min, opt_max, opt_precision, opt_validator) {
|
||||
Blockly.FieldNumber = function(value, opt_min, opt_max, opt_precision,
|
||||
opt_validator) {
|
||||
var numRestrictor = this.getNumRestrictor(opt_min, opt_max, opt_precision);
|
||||
Blockly.FieldNumber.superClass_.constructor.call(this, value, opt_validator, numRestrictor);
|
||||
Blockly.FieldNumber.superClass_.constructor.call(this, value, opt_validator,
|
||||
numRestrictor);
|
||||
this.addArgType('number');
|
||||
};
|
||||
goog.inherits(Blockly.FieldNumber, Blockly.FieldTextInput);
|
||||
|
@ -76,7 +80,8 @@ Blockly.FieldNumber.DROPDOWN_Y_PADDING = 8;
|
|||
* @const
|
||||
*/
|
||||
// Calculator order
|
||||
Blockly.FieldNumber.NUMPAD_BUTTONS = ['7', '8', '9', '4', '5', '6', '1', '2', '3', '.', '0'];
|
||||
Blockly.FieldNumber.NUMPAD_BUTTONS =
|
||||
['7', '8', '9', '4', '5', '6', '1', '2', '3', '.', '0'];
|
||||
|
||||
/**
|
||||
* Src for the delete icon to be shown on the num-pad.
|
||||
|
@ -111,10 +116,9 @@ Blockly.FieldNumber.activeField_ = null;
|
|||
* @param {number|string|undefined} opt_precision Precision for value.
|
||||
* @return {!RegExp} Regular expression for this FieldNumber's restrictor.
|
||||
*/
|
||||
Blockly.FieldNumber.prototype.getNumRestrictor = function(opt_min, opt_max, opt_precision) {
|
||||
this.decimalAllowed_ = (typeof opt_precision == 'undefined') || isNaN(opt_precision) ||
|
||||
(opt_precision == 0) || (Math.floor(opt_precision) != opt_precision);
|
||||
this.negativeAllowed_ = (typeof opt_min == 'undefined') || isNaN(opt_min) || opt_min < 0;
|
||||
Blockly.FieldNumber.prototype.getNumRestrictor = function(opt_min, opt_max,
|
||||
opt_precision) {
|
||||
this.setConstraints_(opt_min, opt_max, opt_precision);
|
||||
var pattern = "[\\d]"; // Always allow digits.
|
||||
if (this.decimalAllowed_) {
|
||||
pattern += "|[\\.]";
|
||||
|
@ -131,14 +135,18 @@ Blockly.FieldNumber.prototype.getNumRestrictor = function(opt_min, opt_max, opt_
|
|||
* @param {number=} opt_max Maximum number allowed.
|
||||
* @param {number=} opt_precision Step allowed between numbers
|
||||
*/
|
||||
Blockly.FieldNumber.prototype.setConstraints_ = function(opt_min, opt_max, opt_precision) {
|
||||
this.decimalAllowed_ = (typeof opt_precision == 'undefined') || isNaN(opt_precision) ||
|
||||
(opt_precision == 0) || (Math.floor(opt_precision) != opt_precision);
|
||||
this.negativeAllowed_ = (typeof opt_min == 'undefined') || isNaN(opt_min) || opt_min < 0;
|
||||
Blockly.FieldNumber.prototype.setConstraints_ = function(opt_min, opt_max,
|
||||
opt_precision) {
|
||||
this.decimalAllowed_ = (typeof opt_precision == 'undefined') ||
|
||||
isNaN(opt_precision) || (opt_precision == 0) ||
|
||||
(Math.floor(opt_precision) != opt_precision);
|
||||
this.negativeAllowed_ = (typeof opt_min == 'undefined') || isNaN(opt_min) ||
|
||||
opt_min < 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the inline free-text editor on top of the text and the num-pad if appropriate.
|
||||
* Show the inline free-text editor on top of the text and the num-pad if
|
||||
* appropriate.
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldNumber.prototype.showEditor_ = function() {
|
||||
|
@ -154,8 +162,13 @@ Blockly.FieldNumber.prototype.showEditor_ = function() {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the number pad.
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldNumber.prototype.showNumPad_ = function() {
|
||||
// If there is an existing drop-down someone else owns, hide it immediately and clear it.
|
||||
// If there is an existing drop-down someone else owns, hide it immediately
|
||||
// and clear it.
|
||||
Blockly.DropDownDiv.hideWithoutAnimation();
|
||||
Blockly.DropDownDiv.clearContent();
|
||||
|
||||
|
@ -165,6 +178,50 @@ Blockly.FieldNumber.prototype.showNumPad_ = function() {
|
|||
contentDiv.setAttribute('role', 'menu');
|
||||
contentDiv.setAttribute('aria-haspopup', 'true');
|
||||
|
||||
this.addButtons_(contentDiv);
|
||||
|
||||
// Set colour and size of drop-down
|
||||
Blockly.DropDownDiv.setColour(Blockly.Colours.numPadBackground,
|
||||
Blockly.Colours.numPadBorder);
|
||||
contentDiv.style.width = Blockly.FieldNumber.DROPDOWN_WIDTH + 'px';
|
||||
|
||||
this.position_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Figure out where to place the drop-down, and move it there.
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldNumber.prototype.position_ = function() {
|
||||
// Calculate positioning for the drop-down
|
||||
// sourceBlock_ is the rendered shadow field input box
|
||||
var scale = this.sourceBlock_.workspace.scale;
|
||||
var bBox = this.sourceBlock_.getHeightWidth();
|
||||
bBox.width *= scale;
|
||||
bBox.height *= scale;
|
||||
var position = this.getAbsoluteXY_();
|
||||
// If we can fit it, render below the shadow block
|
||||
var primaryX = position.x + bBox.width / 2;
|
||||
var primaryY = position.y + bBox.height +
|
||||
Blockly.FieldNumber.DROPDOWN_Y_PADDING;
|
||||
// If we can't fit it, render above the entire parent block
|
||||
var secondaryX = primaryX;
|
||||
var secondaryY = position.y - (Blockly.BlockSvg.MIN_BLOCK_Y * scale) -
|
||||
(Blockly.BlockSvg.FIELD_Y_OFFSET * scale);
|
||||
|
||||
Blockly.DropDownDiv.setBoundsElement(
|
||||
this.sourceBlock_.workspace.getParentSvg().parentNode);
|
||||
Blockly.DropDownDiv.show(this, primaryX, primaryY, secondaryX, secondaryY,
|
||||
this.onHide_.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Add number, punctuation, and erase buttons to the numeric keypad's content
|
||||
* div.
|
||||
* @param {Element} contentDiv The div for the numeric keypad.
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldNumber.prototype.addButtons_ = function(contentDiv) {
|
||||
// Add numeric keypad buttons
|
||||
var buttons = Blockly.FieldNumber.NUMPAD_BUTTONS;
|
||||
for (var i = 0, buttonText; buttonText = buttons[i]; i++) {
|
||||
|
@ -174,7 +231,7 @@ Blockly.FieldNumber.prototype.showNumPad_ = function() {
|
|||
button.title = buttonText;
|
||||
button.innerHTML = buttonText;
|
||||
Blockly.bindEvent_(button, 'mousedown', button,
|
||||
Blockly.FieldNumber.numPadButtonTouch_);
|
||||
Blockly.FieldNumber.numPadButtonTouch);
|
||||
if (buttonText == '.' && !this.decimalAllowed_) {
|
||||
// Don't show the decimal point for inputs that must be round numbers
|
||||
button.setAttribute('style', 'visibility: hidden');
|
||||
|
@ -186,40 +243,21 @@ Blockly.FieldNumber.prototype.showNumPad_ = function() {
|
|||
eraseButton.setAttribute('role', 'menuitem');
|
||||
eraseButton.setAttribute('class', 'blocklyNumPadButton');
|
||||
eraseButton.title = 'Delete';
|
||||
|
||||
var eraseImage = document.createElement('img');
|
||||
eraseImage.src = Blockly.FieldNumber.NUMPAD_DELETE_ICON;
|
||||
eraseButton.appendChild(eraseImage);
|
||||
|
||||
Blockly.bindEvent_(eraseButton, 'mousedown', null,
|
||||
Blockly.FieldNumber.numPadEraseButtonTouch_);
|
||||
Blockly.FieldNumber.numPadEraseButtonTouch);
|
||||
contentDiv.appendChild(eraseButton);
|
||||
|
||||
// Set colour and size of drop-down
|
||||
Blockly.DropDownDiv.setColour(Blockly.Colours.numPadBackground, Blockly.Colours.numPadBorder);
|
||||
contentDiv.style.width = Blockly.FieldNumber.DROPDOWN_WIDTH + 'px';
|
||||
|
||||
// Calculate positioning for the drop-down
|
||||
// sourceBlock_ is the rendered shadow field input box
|
||||
var scale = this.sourceBlock_.workspace.scale;
|
||||
var bBox = this.sourceBlock_.getHeightWidth();
|
||||
bBox.width *= scale;
|
||||
bBox.height *= scale;
|
||||
var position = this.getAbsoluteXY_();
|
||||
// If we can fit it, render below the shadow block
|
||||
var primaryX = position.x + bBox.width / 2;
|
||||
var primaryY = position.y + bBox.height + Blockly.FieldNumber.DROPDOWN_Y_PADDING;
|
||||
// If we can't fit it, render above the entire parent block
|
||||
var secondaryX = primaryX;
|
||||
var secondaryY = position.y - (Blockly.BlockSvg.MIN_BLOCK_Y * scale) - (Blockly.BlockSvg.FIELD_Y_OFFSET * scale);
|
||||
|
||||
Blockly.DropDownDiv.setBoundsElement(this.sourceBlock_.workspace.getParentSvg().parentNode);
|
||||
Blockly.DropDownDiv.show(this, primaryX, primaryY, secondaryX, secondaryY, this.onHide_.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Call for when a num-pad button is touched.
|
||||
* Determine what the user is inputting, and update the text field appropriately.
|
||||
* Call for when a num-pad number or punctuation button is touched.
|
||||
* Determine what the user is inputting and update the text field appropriately.
|
||||
*/
|
||||
Blockly.FieldNumber.numPadButtonTouch_ = function() {
|
||||
Blockly.FieldNumber.numPadButtonTouch = function() {
|
||||
// String of the button (e.g., '7')
|
||||
var spliceValue = this.innerHTML;
|
||||
// Old value of the text field
|
||||
|
@ -227,39 +265,51 @@ Blockly.FieldNumber.numPadButtonTouch_ = function() {
|
|||
// Determine the selected portion of the text field
|
||||
var selectionStart = Blockly.FieldTextInput.htmlInput_.selectionStart;
|
||||
var selectionEnd = Blockly.FieldTextInput.htmlInput_.selectionEnd;
|
||||
|
||||
// Splice in the new value
|
||||
var newValue = oldValue.slice(0, selectionStart) + spliceValue + oldValue.slice(selectionEnd);
|
||||
// Updates the display. The actual setValue occurs when the field is stopped editing.
|
||||
Blockly.FieldTextInput.htmlInput_.value = newValue;
|
||||
// Resize and scroll the text field appropriately
|
||||
Blockly.FieldNumber.superClass_.resizeEditor_.call(Blockly.FieldNumber.activeField_);
|
||||
Blockly.FieldTextInput.htmlInput_.setSelectionRange(newValue.length, newValue.length);
|
||||
Blockly.FieldTextInput.htmlInput_.scrollLeft = Blockly.FieldTextInput.htmlInput_.scrollWidth;
|
||||
Blockly.FieldNumber.activeField_.validate_();
|
||||
var newValue = oldValue.slice(0, selectionStart) + spliceValue +
|
||||
oldValue.slice(selectionEnd);
|
||||
|
||||
Blockly.FieldNumber.updateDisplay_(newValue);
|
||||
};
|
||||
|
||||
/**
|
||||
* Call for when the num-pad erase button is touched.
|
||||
* Determine what the user is asking to erase, and erase it.
|
||||
*/
|
||||
Blockly.FieldNumber.numPadEraseButtonTouch_ = function() {
|
||||
Blockly.FieldNumber.numPadEraseButtonTouch = function() {
|
||||
// Old value of the text field
|
||||
var oldValue = Blockly.FieldTextInput.htmlInput_.value;
|
||||
// Determine what is selected to erase (if anything)
|
||||
var selectionStart = Blockly.FieldTextInput.htmlInput_.selectionStart;
|
||||
var selectionEnd = Blockly.FieldTextInput.htmlInput_.selectionEnd;
|
||||
// Cut out anything that was previously selected
|
||||
var newValue = oldValue.slice(0, selectionStart) + oldValue.slice(selectionEnd);
|
||||
var newValue = oldValue.slice(0, selectionStart) +
|
||||
oldValue.slice(selectionEnd);
|
||||
if (selectionEnd - selectionStart == 0) { // Length of selection == 0
|
||||
// Delete the last character if nothing was selected
|
||||
newValue = oldValue.slice(0, selectionStart - 1) + oldValue.slice(selectionStart);
|
||||
newValue = oldValue.slice(0, selectionStart - 1) +
|
||||
oldValue.slice(selectionStart);
|
||||
}
|
||||
// Update the display to show erased value.
|
||||
|
||||
Blockly.FieldNumber.updateDisplay_(newValue);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the displayed value and resize/scroll the text field as needed.
|
||||
* @param {string} newValue The new text to display.
|
||||
* @private.
|
||||
*/
|
||||
Blockly.FieldNumber.updateDisplay_ = function(newValue) {
|
||||
// Updates the display. The actual setValue occurs when editing ends.
|
||||
Blockly.FieldTextInput.htmlInput_.value = newValue;
|
||||
// Resize and scroll the text field appropriately
|
||||
Blockly.FieldNumber.superClass_.resizeEditor_.call(Blockly.FieldNumber.activeField_);
|
||||
Blockly.FieldTextInput.htmlInput_.setSelectionRange(newValue.length, newValue.length);
|
||||
Blockly.FieldTextInput.htmlInput_.scrollLeft = Blockly.FieldTextInput.htmlInput_.scrollWidth;
|
||||
Blockly.FieldNumber.superClass_.resizeEditor_.call(
|
||||
Blockly.FieldNumber.activeField_);
|
||||
Blockly.FieldTextInput.htmlInput_.setSelectionRange(newValue.length,
|
||||
newValue.length);
|
||||
Blockly.FieldTextInput.htmlInput_.scrollLeft =
|
||||
Blockly.FieldTextInput.htmlInput_.scrollWidth;
|
||||
Blockly.FieldNumber.activeField_.validate_();
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue