Can now create a variable with the button in the flyout; drag a block with a variable out of the flyout; handle default variable names; and import and export variables

This commit is contained in:
Rachel Fenichel
2017-11-30 16:30:58 -08:00
parent 731d2735c0
commit 745bed5ac3
4 changed files with 192 additions and 57 deletions

View File

@@ -47,13 +47,45 @@ goog.require('goog.string');
* @constructor
*/
Blockly.FieldVariable = function(varname, opt_validator, opt_variableTypes) {
Blockly.FieldVariable.superClass_.constructor.call(this,
Blockly.FieldVariable.dropdownCreate, opt_validator);
this.setValue(varname || '');
// Don't call the FieldDropdown constructor. It'll try too hard.
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_ = '';
this.variableTypes = 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 (potentialVariableMap) {
variable = potentialVariableMap.createVariable(text, type, id);
} else {
variable = workspace.createVariable(text, type, id);
}
}
return variable;
};
/**
* Install this dropdown on a block.
*/
@@ -69,20 +101,39 @@ Blockly.FieldVariable.prototype.init = function() {
};
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) {
this.sourceBlock_.workspace.createVariable(this.getValue());
// 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);
this.setValue(variable.getId());
};
Blockly.FieldVariable.dispose = function() {
Blockly.FieldVariable.superClass_.dispose.call(this);
this.workspace_ = null;
this.variableMap_ = null;
};
/**
@@ -101,14 +152,20 @@ Blockly.FieldVariable.prototype.setSourceBlock = function(block) {
* @return {string} Current text.
*/
Blockly.FieldVariable.prototype.getValue = function() {
return this.getText();
//return this.getText();
return this.variable_ ? this.variable_.getId() : '';
};
Blockly.FieldVariable.prototype.getText = function() {
//return this.getText();
return this.variable_ ? this.variable_.name : '';
};
/**
* Set the variable name.
* Set the variable name. (DEPRECATED)
* @param {string} value New text.
*/
Blockly.FieldVariable.prototype.setValue = function(value) {
Blockly.FieldVariable.prototype.oldSetValue = function(value) {
var newValue = value;
var newText = value;
@@ -131,6 +188,47 @@ Blockly.FieldVariable.prototype.setValue = function(value) {
this.setText(newText);
};
/**
* Set the variable ID.
* @param {string} id New variable ID, which must reference an existing
* variable.
*/
Blockly.FieldVariable.prototype.setValue = function(id) {
var workspace = this.sourceBlock_.workspace;
//var variable = this.variableMap_.getVariableById(id);
var potentialVariableMap = workspace.isFlyout ?
workspace.targetWorkspace.potentialVariableMap_ : null;
var variable = workspace.getVariableById(id);
if (!variable && potentialVariableMap) {
variable = potentialVariableMap.getVariableById(id);
}
if (!variable) {
throw new Error('Variable id doesn\'t point to a real variable! ID was ' +
id);
}
// Type checks!
var type = variable.type;
if (!this.typeIsAllowed_(type)) {
throw new Error('Variable type doesn\'t match this field! Type was ' +
type);
}
this.variable_ = variable;
this.setText(variable.name);
};
Blockly.FieldVariable.prototype.typeIsAllowed_ = function(type) {
var typeList = this.getVariableTypes_();
if (!typeList) {
return true; // If it's null, all types are valid.
}
for (var i = 0; i < typeList.length; i++) {
if (type == typeList[i]) {
return true;
}
}
return false;
};
/**
* Return a list of variable types to include in the dropdown.
* @return {!Array.<string>} Array of variable types.
@@ -138,6 +236,8 @@ Blockly.FieldVariable.prototype.setValue = function(value) {
* @private
*/
Blockly.FieldVariable.prototype.getVariableTypes_ = function() {
// TODO: Why does this happen every time, instead of once when the workspace
// is set? Do we expect the variable types to change that much?
var variableTypes = this.variableTypes;
if (variableTypes === null || variableTypes === undefined) {
// If variableTypes is null, return all variable types.
@@ -234,7 +334,7 @@ Blockly.FieldVariable.prototype.onItemSelected = function(menu, menuItem) {
return;
} else if (id == Blockly.DELETE_VARIABLE_ID) {
// Delete variable.
workspace.deleteVariable(this.getText());
workspace.deleteVariableById(this.variable_.getId());
return;
}

View File

@@ -330,12 +330,20 @@ Blockly.Variables.promptName = function(promptText, defaultText, callback) {
Blockly.Variables.generateVariableFieldXml_ = function(variableModel) {
// The variable name may be user input, so it may contain characters that need
// to be escaped to create valid XML.
var element = goog.dom.createDom('field');
element.setAttribute('name', 'VAR');
element.setAttribute('variabletype', variableModel.type);
element.setAttribute('id', variableModel.getId());
element.textContent = variableModel.name;
var typeString = variableModel.type;
if (typeString == '') {
typeString = '\'\'';
}
var text = '<field name="VAR" id="' + variableModel.getId() +
'" variabletype="' + typeString +
'">' + variableModel.name + '</field>';
return text;
// var element = goog.dom.createDom('field');
// element.setAttribute('name', 'VAR');
// element.setAttribute('variabletype', variableModel.type);
// element.setAttribute('id', variableModel.getId());
// element.textContent = variableModel.name;
var xmlString = Blockly.Xml.domToText(element);
return xmlString;
// var xmlString = Blockly.Xml.domToText(element);
// return xmlString;
};

View File

@@ -84,6 +84,8 @@ Blockly.Workspace = function(opt_options) {
* @private
*/
this.variableMap_ = new Blockly.VariableMap(this);
this.potentialVariableMap_ = new Blockly.VariableMap(this);
};
/**

View File

@@ -86,6 +86,28 @@ Blockly.Xml.blockToDomWithXY = function(block, opt_noId) {
return element;
};
Blockly.Xml.fieldToDomVariable_ = function(field, workspace) {
var potentialVariableMap = workspace.isFlyout ?
workspace.targetWorkspace.potentialVariableMap_ : null;
// Ugh that's not true at all.
var id = field.getValue();
var variable = workspace.getVariableById(id);
if (!variable && potentialVariableMap) {
variable = potentialVariableMap.getVariableById(id);
}
if (variable) {
var container = goog.dom.createDom('field', null, variable.name);
container.setAttribute('name', field.name);
container.setAttribute('id', variable.getId());
container.setAttribute('variabletype', variable.type);
return container;
} else {
// something went wrong?
console.log('no variable in fieldtodom');
return null;
}
};
/**
* Encode a field as XML.
* @param {!Blockly.Field} field The field to encode.
@@ -96,16 +118,13 @@ Blockly.Xml.blockToDomWithXY = function(block, opt_noId) {
*/
Blockly.Xml.fieldToDom_ = function(field, workspace) {
if (field.name && field.EDITABLE) {
var container = goog.dom.createDom('field', null, field.getValue());
container.setAttribute('name', field.name);
if (field instanceof Blockly.FieldVariable) {
var variable = workspace.getVariable(field.getValue());
if (variable) {
container.setAttribute('id', variable.getId());
container.setAttribute('variabletype', variable.type);
}
return Blockly.Xml.fieldToDomVariable_(field, workspace);
} else {
var container = goog.dom.createDom('field', null, field.getValue());
container.setAttribute('name', field.name);
return container;
}
return container;
}
return null;
};
@@ -698,6 +717,31 @@ Blockly.Xml.domToBlockHeadless_ = function(xmlBlock, workspace) {
return block;
};
Blockly.Xml.domToFieldVariable_ = function(workspace, xml, text, field) {
// TODO (#1199): When we change setValue and getValue to
// interact with IDs instead of names, update this so that we get
// the variable based on ID instead of textContent.
var type = xml.getAttribute('variabletype') || '';
if (type == '\'\'') {
type = '';
}
// 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);
// This should never happen :)
if (type != null && type !== variable.type) {
throw Error('Serialized variable type with id \'' +
variable.getId() + '\' had type ' + variable.type + ', and ' +
'does not match variable field that references it: ' +
Blockly.Xml.domToText(xml) + '.');
}
field.setValue(variable.getId());
};
/**
* Decode an XML field tag and set the value of that field on the given block.
* @param {!Blockly.Block} block The block that is currently being deserialized.
@@ -716,29 +760,10 @@ Blockly.Xml.domToField_ = function(block, fieldName, xml) {
var workspace = block.workspace;
var text = xml.textContent;
if (field instanceof Blockly.FieldVariable) {
// TODO (#1199): When we change setValue and getValue to
// interact with IDs instead of names, update this so that we get
// the variable based on ID instead of textContent.
var type = xml.getAttribute('variabletype') || '';
// TODO: Consider using a different name (varID?) because this is the
// node's ID.
var id = xml.id;
if (id) {
var variable = workspace.getVariableById(id);
} else {
var variable = workspace.getVariable(text, type);
}
if (!variable) {
variable = workspace.createVariable(text, type, id);
}
if (type != null && type !== variable.type) {
throw Error('Serialized variable type with id \'' +
variable.getId() + '\' had type ' + variable.type + ', and ' +
'does not match variable field that references it: ' +
Blockly.Xml.domToText(xml) + '.');
}
Blockly.Xml.domToFieldVariable_(workspace, xml, text, field);
} else {
field.setValue(text);
}
field.setValue(text);
};
/**