scratch-blocks/core/field_variable.js
2017-11-20 17:01:42 -05:00

274 lines
9.4 KiB
JavaScript

/**
* @license
* Visual Blocks Editor
*
* Copyright 2012 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 Variable input field.
* @author fraser@google.com (Neil Fraser)
*/
'use strict';
goog.provide('Blockly.FieldVariable');
goog.require('Blockly.FieldDropdown');
goog.require('Blockly.Msg');
goog.require('Blockly.VariableModel');
goog.require('Blockly.Variables');
goog.require('goog.asserts');
goog.require('goog.string');
/**
* Class for a variable's dropdown field.
* @param {?string} varname The default name for the variable. If null,
* a unique variable name will be generated.
* @param {Function=} opt_validator A function that is executed when a new
* option is selected. Its sole argument is the new option value.
* @param {Array.<string>} opt_variableTypes A list of the types of variables to
* include in the dropdown.
* @extends {Blockly.FieldDropdown}
* @constructor
*/
Blockly.FieldVariable = function(varname, opt_validator, opt_variableTypes) {
Blockly.FieldVariable.superClass_.constructor.call(this,
Blockly.FieldVariable.dropdownCreate, opt_validator);
this.setValue(varname || '');
this.addArgType('variable');
this.variableTypes = opt_variableTypes;
};
goog.inherits(Blockly.FieldVariable, Blockly.FieldDropdown);
/**
* Install this dropdown on a block.
*/
Blockly.FieldVariable.prototype.init = function() {
if (this.fieldGroup_) {
// Dropdown has already been initialized once.
return;
}
Blockly.FieldVariable.superClass_.init.call(this);
// TODO (1010): Change from init/initModel to initView/initModel
this.initModel();
};
Blockly.FieldVariable.prototype.initModel = function() {
if (!this.getValue()) {
// Variables without names get uniquely named for this workspace.
var workspace =
this.sourceBlock_.isInFlyout ?
this.sourceBlock_.workspace.targetWorkspace :
this.sourceBlock_.workspace;
this.setValue(Blockly.Variables.generateUniqueName(workspace));
}
// If the selected variable doesn't exist yet, create it.
// For instance, some blocks in the toolbox have variable dropdowns filled
// in by default.
if (!this.sourceBlock_.isInFlyout) {
// Check if there was exactly one element specified in the
// variableTypes list. This is the list that specifies which types of
// variables to include in the dropdown.
// If there is exactly one element specified, make the variable
// being created this specified type. Else, default behavior is to create
// a scalar variable
if (this.getVariableTypes_().length == 1) {
this.sourceBlock_.workspace.createVariable(this.getValue(),
this.getVariableTypes_()[0]);
} else {
this.sourceBlock_.workspace.createVariable(this.getValue());
}
}
};
/**
* Attach this field to a block.
* @param {!Blockly.Block} block The block containing this field.
*/
Blockly.FieldVariable.prototype.setSourceBlock = function(block) {
goog.asserts.assert(!block.isShadow(),
'Variable fields are not allowed to exist on shadow blocks.');
Blockly.FieldVariable.superClass_.setSourceBlock.call(this, block);
};
/**
* Get the variable's name (use a variableDB to convert into a real name).
* Unline a regular dropdown, variables are literal and have no neutral value.
* @return {string} Current text.
*/
Blockly.FieldVariable.prototype.getValue = function() {
return this.getText();
};
/**
* Set the variable name.
* @param {string} value New text.
*/
Blockly.FieldVariable.prototype.setValue = function(value) {
var newValue = value;
var newText = value;
if (this.sourceBlock_) {
var variable = this.sourceBlock_.workspace.getVariableById(value);
if (variable) {
newText = variable.name;
}
// TODO(marisaleung): Remove name lookup after converting all Field Variable
// instances to use id instead of name.
else if (variable = this.sourceBlock_.workspace.getVariable(value)) {
newValue = variable.getId();
}
if (Blockly.Events.isEnabled()) {
Blockly.Events.fire(new Blockly.Events.BlockChange(
this.sourceBlock_, 'field', this.name, this.value_, newValue));
}
}
this.value_ = newValue;
this.setText(newText);
};
/**
* Return a list of variable types to include in the dropdown.
* @return {!Array.<string>} Array of variable types.
* @throws {Error} if variableTypes is an empty array.
* @private
*/
Blockly.FieldVariable.prototype.getVariableTypes_ = function() {
var variableTypes = this.variableTypes;
if (variableTypes === null) {
// If variableTypes is null, return all variable types.
if (this.sourceBlock_) {
var workspace = this.sourceBlock_.workspace;
return workspace.getVariableTypes();
}
}
variableTypes = variableTypes || [''];
if (variableTypes.length == 0) {
// Throw an error if variableTypes is an empty list.
var name = this.getText();
throw new Error('\'variableTypes\' of field variable ' +
name + ' was an empty list');
}
return variableTypes;
};
/**
* Return a sorted list of variable names for variable dropdown menus.
* Include a special option at the end for creating a new variable name.
* @return {!Array.<string>} Array of variable names.
* @this {Blockly.FieldVariable}
*/
Blockly.FieldVariable.dropdownCreate = function() {
var variableModelList = [];
var name = this.getText();
// Don't create a new variable if there is nothing selected.
var createSelectedVariable = name ? true : false;
var workspace = null;
if (this.sourceBlock_) {
workspace = this.sourceBlock_.workspace;
}
var isBroadcastType = false;
if (workspace) {
var variableTypes = this.getVariableTypes_();
var variableModelList = [];
// Get a copy of the list, so that adding rename and new variable options
// doesn't modify the workspace's list.
for (var i = 0; i < variableTypes.length; i++) {
var variableType = variableTypes[i];
if (variableType == Blockly.BROADCAST_MESSAGE_TYPE){
isBroadcastType = true;
}
var variables = workspace.getVariablesOfType(variableType);
variableModelList = variableModelList.concat(variables);
}
for (var i = 0; i < variableModelList.length; i++){
if (createSelectedVariable &&
goog.string.caseInsensitiveEquals(variableModelList[i].name, name)) {
createSelectedVariable = false;
break;
}
}
}
// Ensure that the currently selected variable is an option.
if (createSelectedVariable && workspace) {
var newVar = workspace.createVariable(name);
variableModelList.push(newVar);
}
variableModelList.sort(Blockly.VariableModel.compareByName);
var options = [];
for (var i = 0; i < variableModelList.length; i++) {
// Set the uuid as the internal representation of the variable.
options[i] = [variableModelList[i].name, variableModelList[i].getId()];
}
if (isBroadcastType) {
options.push([Blockly.Msg.NEW_BROADCAST_MESSAGE, Blockly.NEW_BROADCAST_MESSAGE_ID]);
} else {
options.push([Blockly.Msg.RENAME_VARIABLE, Blockly.RENAME_VARIABLE_ID]);
if (Blockly.Msg.DELETE_VARIABLE) {
options.push([Blockly.Msg.DELETE_VARIABLE.replace('%1', name),
Blockly.DELETE_VARIABLE_ID]);
}
}
return options;
};
/**
* Handle the selection of an item in the variable dropdown menu.
* Special case the 'Rename variable...' and 'Delete variable...' options.
* In the rename case, prompt the user for a new name.
* @param {!goog.ui.Menu} menu The Menu component clicked.
* @param {!goog.ui.MenuItem} menuItem The MenuItem selected within menu.
*/
Blockly.FieldVariable.prototype.onItemSelected = function(menu, menuItem) {
var id = menuItem.getValue();
// TODO(marisaleung): change setValue() to take in an id as the parameter.
// Then remove itemText.
var itemText;
if (this.sourceBlock_ && this.sourceBlock_.workspace) {
var workspace = this.sourceBlock_.workspace;
var variable = workspace.getVariableById(id);
// If the item selected is a variable, set itemText to the variable name.
if (variable) {
itemText = variable.name;
}
else if (id == Blockly.RENAME_VARIABLE_ID) {
// Rename variable.
var currentName = this.getText();
variable = workspace.getVariable(currentName);
Blockly.Variables.renameVariable(workspace, variable);
return;
} else if (id == Blockly.DELETE_VARIABLE_ID) {
// Delete variable.
workspace.deleteVariable(this.getText());
return;
} else if (id == Blockly.NEW_BROADCAST_MESSAGE_ID) {
var thisField = this;
var setName = function(newName) {
thisField.setValue(newName);
};
Blockly.Variables.createVariable(workspace, setName, Blockly.BROADCAST_MESSAGE_TYPE);
return;
}
// Call any validation function, and allow it to override.
itemText = this.callValidator(itemText);
}
if (itemText !== null) {
this.setValue(itemText);
}
};