field_variable.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /**
  2. * @license
  3. * Visual Blocks Editor
  4. *
  5. * Copyright 2012 Google Inc.
  6. * https://developers.google.com/blockly/
  7. *
  8. * Licensed under the Apache License, Version 2.0 (the "License");
  9. * you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. /**
  21. * @fileoverview Variable input field.
  22. * @author fraser@google.com (Neil Fraser)
  23. */
  24. 'use strict';
  25. goog.provide('Blockly.FieldVariable');
  26. goog.require('Blockly.FieldDropdown');
  27. goog.require('Blockly.Msg');
  28. goog.require('Blockly.Variables');
  29. goog.require('goog.asserts');
  30. goog.require('goog.string');
  31. /**
  32. * Class for a variable's dropdown field.
  33. * @param {?string} varname The default name for the variable. If null,
  34. * a unique variable name will be generated.
  35. * @param {Function=} opt_validator A function that is executed when a new
  36. * option is selected. Its sole argument is the new option value.
  37. * @extends {Blockly.FieldDropdown}
  38. * @constructor
  39. */
  40. Blockly.FieldVariable = function(varname, opt_validator) {
  41. Blockly.FieldVariable.superClass_.constructor.call(this,
  42. Blockly.FieldVariable.dropdownCreate, opt_validator);
  43. this.setValue(varname || '');
  44. };
  45. goog.inherits(Blockly.FieldVariable, Blockly.FieldDropdown);
  46. /**
  47. * The menu item index for the new variable option.
  48. * @type {number}
  49. */
  50. Blockly.FieldVariable.prototype.newVarItemIndex_ = -1;
  51. /**
  52. * The menu item index for the rename variable option.
  53. * @type {number}
  54. */
  55. Blockly.FieldVariable.prototype.renameVarItemIndex_ = -1;
  56. /**
  57. * The menu item index for the delete variable option.
  58. * @type {number}
  59. */
  60. Blockly.FieldVariable.prototype.deleteVarItemIndex_ = -1;
  61. /**
  62. * Install this dropdown on a block.
  63. */
  64. Blockly.FieldVariable.prototype.init = function() {
  65. if (this.fieldGroup_) {
  66. // Dropdown has already been initialized once.
  67. return;
  68. }
  69. Blockly.FieldVariable.superClass_.init.call(this);
  70. if (!this.getValue()) {
  71. // Variables without names get uniquely named for this workspace.
  72. var workspace =
  73. this.sourceBlock_.isInFlyout ?
  74. this.sourceBlock_.workspace.targetWorkspace :
  75. this.sourceBlock_.workspace;
  76. this.setValue(Blockly.Variables.generateUniqueName(workspace));
  77. }
  78. // If the selected variable doesn't exist yet, create it.
  79. // For instance, some blocks in the toolbox have variable dropdowns filled
  80. // in by default.
  81. if (!this.sourceBlock_.isInFlyout) {
  82. this.sourceBlock_.workspace.createVariable(this.getValue());
  83. }
  84. };
  85. /**
  86. * Attach this field to a block.
  87. * @param {!Blockly.Block} block The block containing this field.
  88. */
  89. Blockly.FieldVariable.prototype.setSourceBlock = function(block) {
  90. goog.asserts.assert(!block.isShadow(),
  91. 'Variable fields are not allowed to exist on shadow blocks.');
  92. Blockly.FieldVariable.superClass_.setSourceBlock.call(this, block);
  93. };
  94. /**
  95. * Get the variable's name (use a variableDB to convert into a real name).
  96. * Unline a regular dropdown, variables are literal and have no neutral value.
  97. * @return {string} Current text.
  98. */
  99. Blockly.FieldVariable.prototype.getValue = function() {
  100. return this.getText();
  101. };
  102. /**
  103. * Set the variable name.
  104. * @param {string} newValue New text.
  105. */
  106. Blockly.FieldVariable.prototype.setValue = function(newValue) {
  107. if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
  108. Blockly.Events.fire(new Blockly.Events.Change(
  109. this.sourceBlock_, 'field', this.name, this.value_, newValue));
  110. }
  111. this.value_ = newValue;
  112. this.setText(newValue);
  113. };
  114. /**
  115. * Return a sorted list of variable names for variable dropdown menus.
  116. * Include a special option at the end for creating a new variable name.
  117. * @return {!Array.<string>} Array of variable names.
  118. * @this {!Blockly.FieldVariable}
  119. */
  120. Blockly.FieldVariable.dropdownCreate = function() {
  121. if (this.sourceBlock_ && this.sourceBlock_.workspace) {
  122. // Get a copy of the list, so that adding rename and new variable options
  123. // doesn't modify the workspace's list.
  124. var variableList = this.sourceBlock_.workspace.variableList.slice(0);
  125. } else {
  126. var variableList = [];
  127. }
  128. // Ensure that the currently selected variable is an option.
  129. var name = this.getText();
  130. if (name && variableList.indexOf(name) == -1) {
  131. variableList.push(name);
  132. }
  133. variableList.sort(goog.string.caseInsensitiveCompare);
  134. this.newVarItemIndex_ = variableList.length;
  135. variableList.push(Blockly.Msg.NEW_VARIABLE);
  136. this.renameVarItemIndex_ = variableList.length;
  137. variableList.push(Blockly.Msg.RENAME_VARIABLE);
  138. this.deleteVarItemIndex_ = variableList.length;
  139. variableList.push(Blockly.Msg.DELETE_VARIABLE.replace('%1', name));
  140. // Variables are not language-specific, use the name as both the user-facing
  141. // text and the internal representation.
  142. var options = [];
  143. for (var i = 0; i < variableList.length; i++) {
  144. options[i] = [variableList[i], variableList[i]];
  145. }
  146. return options;
  147. };
  148. /**
  149. * Handle the selection of an item in the variable dropdown menu.
  150. * Special case the 'Rename variable...' and 'Delete variable...' options.
  151. * In the rename case, prompt the user for a new name.
  152. * @param {!goog.ui.Menu} menu The Menu component clicked.
  153. * @param {!goog.ui.MenuItem} menuItem The MenuItem selected within menu.
  154. */
  155. Blockly.FieldVariable.prototype.onItemSelected = function(menu, menuItem) {
  156. var menuLength = menu.getChildCount();
  157. var itemText = menuItem.getValue();
  158. if (this.sourceBlock_) {
  159. var workspace = this.sourceBlock_.workspace;
  160. if (this.renameVarItemIndex_ >= 0 &&
  161. menu.getChildAt(this.renameVarItemIndex_) === menuItem) {
  162. // Rename variable.
  163. var oldName = this.getText();
  164. Blockly.hideChaff();
  165. Blockly.Variables.promptName(
  166. Blockly.Msg.RENAME_VARIABLE_TITLE.replace('%1', oldName), oldName,
  167. function(newName) {
  168. if (newName) {
  169. workspace.renameVariable(oldName, newName);
  170. }
  171. });
  172. return;
  173. } else if (this.newVarItemIndex_ >= 0 &&
  174. menu.getChildAt(this.newVarItemIndex_) == menuItem) {
  175. var that = this;
  176. Blockly.Variables.createVariable(workspace, function(new_name) {
  177. if (new_name !== null && new_name !== undefined) {
  178. that.setValue(new_name);
  179. }
  180. });
  181. return;
  182. } else if (this.deleteVarItemIndex_ >= 0 &&
  183. menu.getChildAt(this.deleteVarItemIndex_) === menuItem) {
  184. // Delete variable.
  185. workspace.deleteVariable(this.getText());
  186. return;
  187. }
  188. // Call any validation function, and allow it to override.
  189. itemText = this.callValidator(itemText);
  190. }
  191. if (itemText !== null) {
  192. this.setValue(itemText);
  193. }
  194. };