diff --git a/core/field_variable.js b/core/field_variable.js index 2f48e627d..0221f2937 100644 --- a/core/field_variable.js +++ b/core/field_variable.js @@ -110,7 +110,6 @@ Blockly.FieldVariable.dropdownCreate = function() { } variableList.sort(goog.string.caseInsensitiveCompare); variableList.push(Blockly.Msg.RENAME_VARIABLE); - variableList.push(Blockly.Msg.NEW_VARIABLE); variableList.push(Blockly.Msg.DELETE_VARIABLE.replace('%1', name)); // Variables are not language-specific, use the name as both the user-facing // text and the internal representation. @@ -123,8 +122,8 @@ Blockly.FieldVariable.dropdownCreate = function() { /** * Event handler for a change in variable name. - * Special case the 'New variable...' and 'Rename variable...' options. - * In both of these special cases, prompt the user for a new name. + * Special case the 'Rename variable...' and 'Delete variable...' options. + * In the rename case, prompt the user for a new name. * @param {string} text The selected dropdown menu option. * @return {null|undefined|string} An acceptable new variable name, or null if * change is to be either aborted (cancel button) or has been already @@ -132,40 +131,17 @@ Blockly.FieldVariable.dropdownCreate = function() { * @this {!Blockly.FieldVariable} */ Blockly.FieldVariable.classValidator = function(text) { - function promptName(promptText, defaultText) { - Blockly.hideChaff(); - var newVar = window.prompt(promptText, defaultText); - // Merge runs of whitespace. Strip leading and trailing whitespace. - // Beyond this, all names are legal. - if (newVar) { - newVar = newVar.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, ''); - if (newVar == Blockly.Msg.RENAME_VARIABLE || - newVar == Blockly.Msg.NEW_VARIABLE) { - // Ok, not ALL names are legal... - newVar = null; - } - } - return newVar; - } var workspace = this.sourceBlock_.workspace; if (text == Blockly.Msg.RENAME_VARIABLE) { var oldVar = this.getText(); - text = promptName(Blockly.Msg.RENAME_VARIABLE_TITLE.replace('%1', oldVar), - oldVar); + Blockly.hideChaff(); + text = Blockly.Variables.promptName( + Blockly.Msg.RENAME_VARIABLE_TITLE.replace('%1', oldVar), oldVar); if (text) { Blockly.Variables.renameVariable(oldVar, text, workspace); } return null; - } else if (text == Blockly.Msg.NEW_VARIABLE) { - text = promptName(Blockly.Msg.NEW_VARIABLE_TITLE, ''); - // Since variables are case-insensitive, ensure that if the new variable - // matches with an existing variable, the new case prevails throughout. - if (text) { - Blockly.Variables.renameVariable(text, text, workspace); - return text; - } - return null; - } else if (text == Blockly.Msg.DELETE_VARIABLE.replace('%1', this.getText())) { + }else if (text == Blockly.Msg.DELETE_VARIABLE.replace('%1', this.getText())) { Blockly.Variables.delete(this.getText(), this.sourceBlock_.workspace); return null; } diff --git a/core/flyout.js b/core/flyout.js index e13bc48de..942b0be38 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -608,7 +608,8 @@ Blockly.Flyout.prototype.show = function(xmlList) { } else if (tagName == 'BUTTON') { var label = xml.getAttribute('text'); - var curButton = new Blockly.FlyoutButton(this.workspace_, label); + var curButton = new Blockly.FlyoutButton(this.workspace_, + this.targetWorkspace_, label); contents.push({type: 'button', button: curButton}); gaps.push(this.MARGIN); } diff --git a/core/flyout_button.js b/core/flyout_button.js index 77d18f426..cd251897e 100644 --- a/core/flyout_button.js +++ b/core/flyout_button.js @@ -34,16 +34,23 @@ goog.require('goog.math.Coordinate'); * Class for a button in the flyout. * @param {!Blockly.Workspace} workspace The workspace in which to place this * button. + * @param {!Blockly.Workspace} targetWorkspace The flyout's target workspace. * @param {string} text The text to display on the button. * @constructor */ -Blockly.FlyoutButton = function(workspace, text) { +Blockly.FlyoutButton = function(workspace, targetWorkspace, text) { /** * @type {!Blockly.Workspace} * @private */ this.workspace_ = workspace; + /** + * @type {!Blockly.Workspace} + * @private + */ + this.targetWorkspace_ = targetWorkspace; + /** * @type {string} * @private @@ -150,9 +157,10 @@ Blockly.FlyoutButton.prototype.dispose = function() { * @param {!Event} e Mouse up event. */ Blockly.FlyoutButton.prototype.onMouseUp = function(e) { - console.log("Button was clicked"); // Don't scroll the page. e.preventDefault(); // Don't propagate mousewheel event (zooming). e.stopPropagation(); + + Blockly.Variables.createVariable(this.targetWorkspace_); }; diff --git a/core/toolbox.js b/core/toolbox.js index b5b27f869..3f715ed3a 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -420,6 +420,18 @@ Blockly.Toolbox.prototype.getClientRect = function() { } }; +/** + * Update the flyout's contents without closing it. Should be used in response + * to a change in one of the dynamic categories, such as variables or + * procedures. + */ +Blockly.Toolbox.prototype.refreshSelection = function() { + var selectedItem = this.tree_.getSelectedItem(); + if (selectedItem && selectedItem.blocks) { + this.flyout_.show(selectedItem.blocks); + } +}; + // Extending Closure's Tree UI. /** diff --git a/core/variables.js b/core/variables.js index 37a881791..3bc77f0e3 100644 --- a/core/variables.js +++ b/core/variables.js @@ -110,29 +110,31 @@ Blockly.Variables.flyoutCategory = function(workspace) { var button = goog.dom.createDom('button'); button.setAttribute('text', 'Create variable'); xmlList.push(button); - for (var i = 0; i < variableList.length; i++) { - if (Blockly.Blocks['variables_set']) { - // - // item - // - var block = goog.dom.createDom('block'); - block.setAttribute('type', 'variables_set'); - if (Blockly.Blocks['variables_get']) { - block.setAttribute('gap', 8); - } - var field = goog.dom.createDom('field', null, variableList[i]); - field.setAttribute('name', 'VAR'); - block.appendChild(field); - xmlList.push(block); - } + + if (Blockly.Blocks['variables_set']) { + // + // item + // + var block = goog.dom.createDom('block'); + block.setAttribute('type', 'variables_set'); if (Blockly.Blocks['variables_get']) { - // + block.setAttribute('gap', 20); + } + var field = goog.dom.createDom('field', null, variableList[0]); + field.setAttribute('name', 'VAR'); + block.appendChild(field); + xmlList.push(block); + } + + for (var i = 0; i < variableList.length; i++) { + if (Blockly.Blocks['variables_get']) { + // // item // var block = goog.dom.createDom('block'); block.setAttribute('type', 'variables_get'); if (Blockly.Blocks['variables_set']) { - block.setAttribute('gap', 24); + block.setAttribute('gap', 8); } var field = goog.dom.createDom('field', null, variableList[i]); field.setAttribute('name', 'VAR'); @@ -244,3 +246,44 @@ Blockly.Variables.delete = function(name, workspace) { Blockly.Variables.disposeUses(name, workspace); }; + +/** + * Create a new variable on the given workspace. + * @param {!Blockly.Workspace} workspace The workspace on which to create the + * variable. + * @return {null|undefined|string} An acceptable new variable name, or null if + * change is to be aborted (cancel button), or undefined if an existing + * variable was chosen. + */ +Blockly.Variables.createVariable = function(workspace) { + var text = Blockly.Variables.promptName(Blockly.Msg.NEW_VARIABLE_TITLE, ''); + // Since variables are case-insensitive, ensure that if the new variable + // matches with an existing variable, the new case prevails throughout. + if (text) { + workspace.createVariable(text); + return text; + } + return null; +}; + +/** + * Prompt the user for a new variable name. + * @param {string} promptText The string of the prompt. + * @param {string} defaultText The default value to show in the prompt's field. + * @return {string|null} The new variable name, or null if the user picked + * something illegal. + */ +Blockly.Variables.promptName = function(promptText, defaultText) { + var newVar = window.prompt(promptText, defaultText); + // Merge runs of whitespace. Strip leading and trailing whitespace. + // Beyond this, all names are legal. + if (newVar) { + newVar = newVar.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, ''); + if (newVar == Blockly.Msg.RENAME_VARIABLE || + newVar == Blockly.Msg.NEW_VARIABLE) { + // Ok, not ALL names are legal... + newVar = null; + } + } + return newVar; +}; diff --git a/core/workspace.js b/core/workspace.js index 4d4d635d2..6e044fcb4 100644 --- a/core/workspace.js +++ b/core/workspace.js @@ -217,20 +217,33 @@ Blockly.Workspace.prototype.updateVariableList = function() { /** * Rename a variable by updating its name in the variable list. + * TODO: #468 * @param {string} oldName Variable to rename. * @param {string} newName New variable name. */ Blockly.Workspace.prototype.renameVariable = function(oldName, newName) { // Find the old name in the list and replace it. var variableIndex = this.variableList.indexOf(oldName); - if (variableIndex != -1) { + var newVariableIndex = this.variableList.indexOf(newName); + if (variableIndex != -1 && newVariableIndex == -1) { this.variableList[variableIndex] = newName; + } else if (variableIndex != -1 && newVariableIndex != -1) { + this.variableList.splice(variableIndex, 1); } else { this.variableList.push(newName); console.log('Tried to rename an non-existent variable.'); } }; +/** + * Create a variables with the given name. + * TODO: #468 + * @param {string} name The new variable's name. + */ +Blockly.Workspace.prototype.createVariable = function(name) { + this.variableList.push(name); +}; + /** * Returns the horizontal offset of the workspace. * Intended for LTR/RTL compatibility in XML. diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 6f9be212d..d02243551 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -589,6 +589,19 @@ Blockly.WorkspaceSvg.prototype.paste = function(xmlBlock) { block.select(); }; +/** + * Create a new variable with the given name. Update the flyout to show the new + * variable immediately. + * TODO: #468 + * @param {string} name The new variable's name. + */ +Blockly.WorkspaceSvg.prototype.createVariable = function(name) { + Blockly.WorkspaceSvg.superClass_.createVariable.call(this, name); + if (this.toolbox_ && this.toolbox_.flyout_) { + this.toolbox_.refreshSelection(); + } +}; + /** * Make a list of all the delete areas for this workspace. */