Move getOrCreateVariable to variables.js

This commit is contained in:
Rachel Fenichel
2017-12-05 13:08:01 -08:00
parent f37386d2bf
commit 1ce46ab88b
6 changed files with 168 additions and 86 deletions

View File

@@ -266,6 +266,10 @@ Blockly.Blocks['procedures_defnoreturn'] = {
*/
getVarModels: function() {
var vars = [];
// Not fully initialized.
if (!this.arguments_) {
return vars;
}
for (var i = 0, argName; argName = this.arguments_[i]; i++) {
// TODO (#1199): When we switch to tracking variables by ID,
// update this.

View File

@@ -47,11 +47,11 @@ goog.require('goog.string');
* @constructor
*/
Blockly.FieldVariable = function(varname, opt_validator, opt_variableTypes) {
// Don't call the FieldDropdown constructor. It'll try too hard.
// The FieldDropdown constructor would call setValue, which might create a
// spurious variable. Just do the relevant parts of the constructor.
this.menuGenerator_ = Blockly.FieldVariable.dropdownCreate;
this.size_ = new goog.math.Size(0, Blockly.BlockSvg.MIN_BLOCK_Y);
this.setValidator(opt_validator);
//this.setValue(varname || '');
// TODO: Add opt_default_type to match default value. If not set, ''.
this.defaultVariableName = (varname || '');
this.defaultType_ = '';
@@ -59,42 +59,11 @@ Blockly.FieldVariable = function(varname, opt_validator, opt_variableTypes) {
};
goog.inherits(Blockly.FieldVariable, Blockly.FieldDropdown);
Blockly.FieldVariable.getOrCreateVariable = function(workspace, text, type,
id) {
var potentialVariableMap = workspace.isFlyout ?
workspace.targetWorkspace.potentialVariableMap_ : null;
if (id) {
var variable = workspace.getVariableById(id);
if (!variable && potentialVariableMap) {
variable = potentialVariableMap.getVariableById(id);
}
} else {
var variable = workspace.getVariable(text, type);
if (!variable && potentialVariableMap) {
variable = potentialVariableMap.getVariable(text, type);
}
}
// Didn't find the variable.
if (!variable) {
if (!text) {
var ws = workspace.isFlyout ? workspace.targetWorkspace : workspace;
// Variables without names get uniquely named for this workspace.
text = Blockly.Variables.generateUniqueName(ws);
}
if (potentialVariableMap) {
variable = potentialVariableMap.createVariable(text, type, id);
} else {
variable = workspace.createVariable(text, type, id);
}
}
return variable;
};
/**
* Install this dropdown on a block.
* Initialize everything needed to render this field. This includes making sure
* that the field's value is valid.
* @public
*/
Blockly.FieldVariable.prototype.init = function() {
if (this.fieldGroup_) {
@@ -107,36 +76,26 @@ Blockly.FieldVariable.prototype.init = function() {
this.initModel();
};
/**
* Initialize the model for this field if it has not already been initialized.
* If the value has not been set to a variable by the first render, we make up a
* variable rather than let the value be invalid.
* @package
*/
Blockly.FieldVariable.prototype.initModel = function() {
// this.workspace_ = this.sourceBlock_.isInFlyout ?
// this.sourceBlock_.workspace.targetWorkspace :
// this.sourceBlock_.workspace;
// // TODO: Describe how the potential variable map is different from the variable
// // map; use getters.
// this.variableMap_ = this.sourceBlock_.isInFlyout ?
// this.workspace_.potentialVariableMap_ : this.workspace_.variableMap_;
// var name = this.defaultValue;
// if (!name) {
// // Variables without names get uniquely named for this workspace.
// name = Blockly.Variables.generateUniqueName(this.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.
// var variable = this.variableMap_.getVariable(name, this.defaultType_);
// if (!variable) {
// variable = this.variableMap_.createVariable(name, this.defaultType_);
// }
if (this.variable_) {
return; // Initialization already happened.
}
this.workspace_ = this.sourceBlock_.workspace;
var variable = Blockly.FieldVariable.getOrCreateVariable(
this.workspace_, this.defaultVariableName, this.defaultType_, null);
var variable = Blockly.Variables.getOrCreateVariable(
this.workspace_, null, this.defaultVariableName, this.defaultType_);
this.setValue(variable.getId());
};
/**
* Dispose of this field.
* @public
*/
Blockly.FieldVariable.dispose = function() {
Blockly.FieldVariable.superClass_.dispose.call(this);
this.workspace_ = null;
@@ -154,15 +113,17 @@ Blockly.FieldVariable.prototype.setSourceBlock = function(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.
* Get the variable's ID.
* @return {string} Current variable's ID.
*/
Blockly.FieldVariable.prototype.getValue = function() {
//return this.getText();
return this.variable_ ? this.variable_.getId() : '';
};
/**
* Get the text from this field.
* @return {string} Current text.
*/
Blockly.FieldVariable.prototype.getText = function() {
//return this.getText();
return this.variable_ ? this.variable_.name : '';
@@ -177,16 +138,7 @@ Blockly.FieldVariable.prototype.setValue = function(id) {
// TODO: Handle undo a change to go back to the default value.
// TODO: Handle null ID, which means "use default".
var workspace = this.sourceBlock_.workspace;
var variable = workspace.getVariableById(id);
if (!variable) {
if (workspace.isFlyout && workspace.targetWorkspace) {
var potentialVariableMap =
workspace.targetWorkspace.potentialVariableMap_;
if (potentialVariableMap) {
variable = potentialVariableMap.getVariableById(id);
}
}
}
var variable = Blockly.Variables.getVariable(workspace, id);
if (!variable) {
throw new Error('Variable id doesn\'t point to a real variable! ID was ' +
@@ -207,6 +159,12 @@ Blockly.FieldVariable.prototype.setValue = function(id) {
this.setText(variable.name);
};
/**
* Check whether the given variable type is allowed on this field.
* @param {string} type The type to check.
* @return {boolean} True if the type is in the list of allowed types.
* @private
*/
Blockly.FieldVariable.prototype.typeIsAllowed_ = function(type) {
var typeList = this.getVariableTypes_();
if (!typeList) {
@@ -300,16 +258,11 @@ Blockly.FieldVariable.dropdownCreate = function() {
*/
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.
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 (id == Blockly.RENAME_VARIABLE_ID) {
// Rename variable.
variable = this.variable_;
Blockly.Variables.renameVariable(workspace, variable);
Blockly.Variables.renameVariable(workspace, this.variable_);
return;
} else if (id == Blockly.DELETE_VARIABLE_ID) {
// Delete variable.

View File

@@ -415,7 +415,6 @@ Blockly.Flyout.prototype.hide = function() {
this.workspace_.removeChangeListener(this.reflowWrapper_);
this.reflowWrapper_ = null;
}
// Do NOT delete the blocks here. Wait until Flyout.show.
// https://neil.fraser.name/news/2014/08/09/
};

View File

@@ -65,13 +65,13 @@ Blockly.Variables.allUsedVariables = function(root) {
// Iterate through every block and add each variable to the hash.
for (var x = 0; x < blocks.length; x++) {
// TODO (#1199) Switch to IDs.
var blockVariables = blocks[x].getVars();
var blockVariables = blocks[x].getVarModels();
if (blockVariables) {
for (var y = 0; y < blockVariables.length; y++) {
var varName = blockVariables[y];
// Variable name may be null if the block is only half-built.
if (varName) {
variableHash[varName.toLowerCase()] = varName;
var variable = blockVariables[y];
// Variable ID may be null if the block is only half-built.
if (variable.getId()) {
variableHash[variable.name.toLowerCase()] = variable.name;
}
}
}
@@ -339,3 +339,106 @@ Blockly.Variables.generateVariableFieldXml_ = function(variableModel) {
'">' + variableModel.name + '</field>';
return text;
};
/**
* Helper function to look up or create a variable on the given workspace.
* If no variable exists, creates and returns it.
* @param {!Blockly.Workspace} workspace The workspace to search for the
* variable. It may be a flyout workspace or main workspace.
* @param {string} id The ID to use to look up or create the variable, or null.
* @param {string} name The string to use to look up or create the variable,
* @param {string} type The type to use to look up or create the variable.
* @return {!Blockly.VariableModel} The variable corresponding to the given ID
* or name + type combination.
* @package
*/
Blockly.Variables.getOrCreateVariable = function(workspace, id, name, type) {
var variable = Blockly.Variables.getVariable(workspace, id, name, type);
if (!variable) {
variable = Blockly.Variables.createVariable_(workspace, id, name, type);
}
return variable;
};
/**
* Look up a variable on the given workspace.
* Always looks in the main workspace before looking in the flyout workspace.
* Always prefers lookup by ID to lookup by name + type.
* @param {!Blockly.Workspace} workspace The workspace to search for the
* variable. It may be a flyout workspace or main workspace.
* @param {string} id The ID to use to look up the variable, or null.
* @param {string=} opt_name The string to use to look up the variable. Only
* used if lookup by ID fails.
* @param {string=} opt_type The type to use to look up the variable. Only used
* if lookup by ID fails.
* @return {?Blockly.VariableModel} The variable corresponding to the given ID
* or name + type combination, or null if not found.
* @private
*/
Blockly.Variables.getVariable = function(workspace, id, opt_name, opt_type) {
var potentialVariableMap =
Blockly.Variables.getPotentialVariableMap_(workspace);
// Try to just get the variable, by ID if possible.
if (id) {
// Look in the real variable map before checking the potential variable map.
var variable = workspace.getVariableById(id);
if (!variable && potentialVariableMap) {
variable = potentialVariableMap.getVariableById(id);
}
} else if (opt_name && (opt_type != undefined)){
// Otherwise look up by name and type.
var variable = workspace.getVariable(opt_name, opt_type);
if (!variable && potentialVariableMap) {
variable = potentialVariableMap.getVariable(opt_name, opt_type);
}
}
return variable;
};
/**
* Helper function to create a variable on the given workspace.
* @param {!Blockly.Workspace} workspace The workspace in which to create the
* variable. It may be a flyout workspace or main workspace.
* @param {string} id The ID to use to create the variable, or null.
* @param {string} name The string to use to create the variable.
* @param {string} type The type to use to create the variable.
* @return {!Blockly.VariableModel} The variable corresponding to the given ID
* or name + type combination.
* @private
*/
Blockly.Variables.createVariable_ = function(workspace, id, name, type) {
var potentialVariableMap =
Blockly.Variables.getPotentialVariableMap_(workspace);
// Variables without names get uniquely named for this workspace.
if (!name) {
var ws = workspace.isFlyout ? workspace.targetWorkspace : workspace;
name = Blockly.Variables.generateUniqueName(ws);
}
// Create a potential variable if in the flyout.
if (potentialVariableMap) {
var variable = potentialVariableMap.createVariable(name, type, id);
} else { // In the main workspace, create a real variable.
var variable = workspace.createVariable(name, type, id);
}
return variable;
};
/**
* Blocks in the flyout can refer to variables that don't exist in the
* workspace. For instance, the "get item in list" block refers to an "item"
* variable regardless of whether the variable has been created yet.
* A FieldVariable must always refer to a Blockly.VariableModel. We reconcile
* these by tracking "potential" variables in the flyout. These variables
* become real when references to them are dragged into the main workspace.
* @param {!Blockly.Workspace} workspace The workspace to get the potential
* variable map from.
* @return {?Blockly.VariableMap} The potential variable map for the given
* workspace, or null if it was not a flyout workspace.
* @private
*/
Blockly.Variables.getPotentialVariableMap_ = function(workspace) {
return workspace.isFlyout && workspace.targetWorkspace ?
workspace.targetWorkspace.getPotentialVariableMap() : null;
};

View File

@@ -85,6 +85,16 @@ Blockly.Workspace = function(opt_options) {
*/
this.variableMap_ = new Blockly.VariableMap(this);
/**
* Blocks in the flyout can refer to variables that don't exist in the
* workspace. For instance, the "get item in list" block refers to an "item"
* variable regardless of whether the variable has been created yet.
* A FieldVariable must always refer to a Blockly.VariableModel. We reconcile
* these by tracking "potential" variables in the flyout. These variables
* become real when references to them are dragged into the main workspace.
* @type {!Blockly.VariableMap}
* @private
*/
this.potentialVariableMap_ = new Blockly.VariableMap(this);
};
@@ -199,6 +209,7 @@ Blockly.Workspace.prototype.clear = function() {
Blockly.Events.setGroup(false);
}
this.variableMap_.clear();
this.potentialVariableMap_.clear();
};
/**
@@ -628,6 +639,18 @@ Blockly.Workspace.prototype.getAllVariables = function() {
return this.variableMap_.getAllVariables();
};
/**
* Return the variable map that contains "potential" variables. These exist in
* the flyout but not in the workspace.
* TODO: Decide if this can be stored on the flyout workspace instead of the
* main workspace.
* @return {?Blockly.VariableMap} The potential variable map.
* @package
*/
Blockly.Workspace.prototype.getPotentialVariableMap = function() {
return this.potentialVariableMap_;
};
/**
* Database of all workspaces.
* @private

View File

@@ -730,8 +730,8 @@ Blockly.Xml.domToFieldVariable_ = function(workspace, xml, text, field) {
// TODO: Consider using a different name (var_id?) because this is the
// node's ID.
var id = xml.id;
var variable = Blockly.FieldVariable.getOrCreateVariable(workspace, text,
type, id);
var variable =
Blockly.Variables.getOrCreateVariable(workspace, id, text, type);
// This should never happen :)
if (type != null && type !== variable.type) {