mirror of
https://github.com/scratchfoundation/scratch-blocks.git
synced 2025-08-28 22:10:31 -04:00
Add variable getter field
This commit is contained in:
parent
57c3666198
commit
c9182b97fa
7 changed files with 210 additions and 11 deletions
|
@ -21,6 +21,7 @@
|
|||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Blocks.data');
|
||||
goog.provide('Blockly.Constants.Data');
|
||||
|
||||
goog.require('Blockly.Blocks');
|
||||
goog.require('Blockly.Colours');
|
||||
|
@ -62,7 +63,8 @@ Blockly.Blocks['data_variable'] = {
|
|||
"message0": "%1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"type": "field_variable_getter",
|
||||
"text": "",
|
||||
"name": "VARIABLE"
|
||||
}
|
||||
],
|
||||
|
@ -72,7 +74,8 @@ Blockly.Blocks['data_variable'] = {
|
|||
"colourTertiary": Blockly.Colours.data.tertiary,
|
||||
"output": "String",
|
||||
"outputShape": Blockly.OUTPUT_SHAPE_ROUND,
|
||||
"checkboxInFlyout": true
|
||||
"checkboxInFlyout": true,
|
||||
"extensions": ["contextMenu_getVariableBlock"]
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -191,7 +194,8 @@ Blockly.Blocks['data_listcontents'] = {
|
|||
"message0": "%1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_variable",
|
||||
"type": "field_variable_getter",
|
||||
"text": "",
|
||||
"name": "LIST"
|
||||
}
|
||||
],
|
||||
|
@ -513,3 +517,56 @@ Blockly.Blocks['data_hidelist'] = {
|
|||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Mixin to add a context menu for a data_variable block. It adds one item for
|
||||
* each variable defined on the workspace.
|
||||
* @mixin
|
||||
* @augments Blockly.Block
|
||||
* @package
|
||||
* @readonly
|
||||
*/
|
||||
Blockly.Constants.Data.CUSTOM_CONTEXT_MENU_GET_VARIABLE_MIXIN = {
|
||||
/**
|
||||
* Add context menu option to create getter block for the loop's variable.
|
||||
* (customContextMenu support limited to web BlockSvg.)
|
||||
* @param {!Array} options List of menu options to add to.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
customContextMenu: function(options) {
|
||||
if (!this.isCollapsed()) {
|
||||
var variablesList = this.workspace.variableList;
|
||||
for (var i = 0; i < variablesList.length; i++) {
|
||||
var option = {enabled: true};
|
||||
option.text = variablesList[i];
|
||||
|
||||
option.callback =
|
||||
Blockly.Constants.Data.VARIABLE_OPTION_CALLBACK_FACTORY(this,
|
||||
option.text);
|
||||
options.push(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Extensions.registerMixin('contextMenu_getVariableBlock',
|
||||
Blockly.Constants.Data.CUSTOM_CONTEXT_MENU_GET_VARIABLE_MIXIN);
|
||||
|
||||
/**
|
||||
* Callback factory for dropdown menu options associated with a variable getter
|
||||
* block. Each variable on the workspace gets its own item in the dropdown
|
||||
* menu, and clicking on that item changes the text of the field on the source
|
||||
* block.
|
||||
* @param {!Blockly.Block} block The block to update.
|
||||
* @param {string} name The new name to display on the block.
|
||||
* @return {!function()} A function that updates the block with the new name.
|
||||
*/
|
||||
Blockly.Constants.Data.VARIABLE_OPTION_CALLBACK_FACTORY = function(block, name) {
|
||||
return function() {
|
||||
var variableField = block.getField('VARIABLE');
|
||||
if (!variableField) {
|
||||
console.log("Tried to get a variable field on the wrong type of block.");
|
||||
}
|
||||
variableField.setText(name);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -31,6 +31,7 @@ goog.require('Blockly.Colours');
|
|||
goog.require('Blockly.Comment');
|
||||
goog.require('Blockly.Connection');
|
||||
goog.require('Blockly.Extensions');
|
||||
goog.require('Blockly.FieldVariableGetter');
|
||||
goog.require('Blockly.Input');
|
||||
goog.require('Blockly.Mutator');
|
||||
goog.require('Blockly.Warning');
|
||||
|
@ -842,7 +843,8 @@ Blockly.Block.prototype.getVars = function() {
|
|||
var vars = [];
|
||||
for (var i = 0, input; input = this.inputList[i]; i++) {
|
||||
for (var j = 0, field; field = input.fieldRow[j]; j++) {
|
||||
if (field instanceof Blockly.FieldVariable) {
|
||||
if (field instanceof Blockly.FieldVariable ||
|
||||
field instanceof Blockly.FieldVariableGetter) {
|
||||
vars.push(field.getValue());
|
||||
}
|
||||
}
|
||||
|
@ -859,7 +861,8 @@ Blockly.Block.prototype.getVars = function() {
|
|||
Blockly.Block.prototype.renameVar = function(oldName, newName) {
|
||||
for (var i = 0, input; input = this.inputList[i]; i++) {
|
||||
for (var j = 0, field; field = input.fieldRow[j]; j++) {
|
||||
if (field instanceof Blockly.FieldVariable &&
|
||||
if ((field instanceof Blockly.FieldVariable ||
|
||||
field instanceof Blockly.FieldVariableGetter) &&
|
||||
Blockly.Names.equals(oldName, field.getValue())) {
|
||||
field.setValue(newName);
|
||||
}
|
||||
|
@ -1363,6 +1366,9 @@ Blockly.Block.prototype.interpolate_ = function(message, args, lastDummyAlign) {
|
|||
field = new Blockly.FieldNumber(element['value'],
|
||||
element['min'], element['max'], element['precision']);
|
||||
break;
|
||||
case 'field_variable_getter':
|
||||
field = Blockly.Block.newFieldVariableGetterFromJson_(element);
|
||||
break;
|
||||
case 'field_date':
|
||||
if (Blockly.FieldDate) {
|
||||
field = new Blockly.FieldDate(element['date']);
|
||||
|
@ -1454,6 +1460,18 @@ Blockly.Block.newFieldVariableFromJson_ = function(options) {
|
|||
return new Blockly.FieldVariable(varname);
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to construct a FieldVariableGetter from a JSON arg object,
|
||||
* dereferencing any string table references.
|
||||
* @param {!Object} options A JSON object with options (variable).
|
||||
* @returns {!Blockly.FieldImage} The new image.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Block.newFieldVariableGetterFromJson_ = function(options) {
|
||||
var varname = Blockly.utils.replaceMessageReferences(options['text']);
|
||||
return new Blockly.FieldVariableGetter(varname, options['name'],
|
||||
options['class']);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a value input, statement input or local variable to this block.
|
||||
|
|
|
@ -266,7 +266,7 @@ Blockly.FieldDropdown.prototype.showEditor_ = function() {
|
|||
Blockly.FieldDropdown.prototype.onHide = function() {
|
||||
this.dropDownOpen_ = false;
|
||||
// Update colour to look selected.
|
||||
if (!this.disableColourChange_) {
|
||||
if (!this.disableColourChange_ && this.sourceBlock_) {
|
||||
if (this.sourceBlock_.isShadow()) {
|
||||
this.sourceBlock_.setColour(this.savedPrimary_,
|
||||
this.sourceBlock_.getColourSecondary(), this.sourceBlock_.getColourTertiary());
|
||||
|
|
92
core/field_variable_getter.js
Normal file
92
core/field_variable_getter.js
Normal file
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 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 getter field. Appears as a label but has a variable
|
||||
* picker in the right-click menu.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.FieldVariableGetter');
|
||||
|
||||
goog.require('Blockly.Field');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a variable getter field.
|
||||
* @param {string} text The initial content of the field.
|
||||
* @param {string} name Optional CSS class for the field's text.
|
||||
* @extends {Blockly.FieldLabel}
|
||||
* @constructor
|
||||
*
|
||||
*/
|
||||
Blockly.FieldVariableGetter = function(text, name) {
|
||||
Blockly.FieldVariableGetter.superClass_.constructor.call(this, text);
|
||||
this.name_ = name;
|
||||
};
|
||||
goog.inherits(Blockly.FieldVariableGetter, Blockly.Field);
|
||||
|
||||
/**
|
||||
* Editable fields are saved by the XML renderer, non-editable fields are not.
|
||||
*/
|
||||
Blockly.FieldVariableGetter.prototype.EDITABLE = true;
|
||||
|
||||
/**
|
||||
* Install this field on a block.
|
||||
*/
|
||||
Blockly.FieldVariableGetter.prototype.init = function() {
|
||||
if (this.fieldGroup_) {
|
||||
// Field has already been initialized once.
|
||||
return;
|
||||
}
|
||||
Blockly.FieldVariableGetter.superClass_.init.call(this);
|
||||
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 field is editable, but only through the right-click menu.
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldVariableGetter.prototype.showEditor_ = function() {
|
||||
// nop.
|
||||
};
|
||||
|
||||
/**
|
||||
* Add or remove the UI indicating if this field is editable or not.
|
||||
* This field is editable, but only through the right-click menu.
|
||||
* Suppress default editable behaviour.
|
||||
*/
|
||||
Blockly.FieldVariableGetter.prototype.updateEditable = function() {
|
||||
// nop.
|
||||
};
|
|
@ -54,6 +54,9 @@ Blockly.Variables.allUsedVariables = function(root) {
|
|||
} else {
|
||||
throw 'Not Block or Workspace: ' + root;
|
||||
}
|
||||
|
||||
var ignorableName = Blockly.Variables.noVariableText();
|
||||
|
||||
var variableHash = Object.create(null);
|
||||
// Iterate through every block and add each variable to the hash.
|
||||
for (var x = 0; x < blocks.length; x++) {
|
||||
|
@ -62,7 +65,7 @@ Blockly.Variables.allUsedVariables = function(root) {
|
|||
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) {
|
||||
if (varName && !varName.toLowerCase() == ignorableName) {
|
||||
variableHash[varName.toLowerCase()] = varName;
|
||||
}
|
||||
}
|
||||
|
@ -116,14 +119,16 @@ Blockly.Variables.flyoutCategory = function(workspace) {
|
|||
for (var i = 0; i < variableList.length; i++) {
|
||||
if (Blockly.Blocks['data_variable']) {
|
||||
// <block type="data_variable">
|
||||
// <value name="VARIABLE">
|
||||
// <shadow type="data_variablemenu"></shadow>
|
||||
// </value>
|
||||
// <field name="VARIABLE">variablename</field>
|
||||
// </block>
|
||||
var block = goog.dom.createDom('block');
|
||||
block.setAttribute('type', 'data_variable');
|
||||
block.setAttribute('gap', 8);
|
||||
block.appendChild(Blockly.Variables.createVariableDom_(variableList[i]));
|
||||
|
||||
var field = goog.dom.createDom('field', null, variableList[i]);
|
||||
field.setAttribute('name', 'VARIABLE');
|
||||
block.appendChild(field);
|
||||
|
||||
xmlList.push(block);
|
||||
}
|
||||
}
|
||||
|
@ -276,6 +281,16 @@ Blockly.Variables.createMathNumberDom_ = function() {
|
|||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the text that should be used in a field_variable or
|
||||
* field_variable_getter when no variable exists.
|
||||
* TODO: #572
|
||||
* @return {string} The text to display.
|
||||
*/
|
||||
Blockly.Variables.noVariableText = function() {
|
||||
return "No variable selected";
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a new variable name that is not yet being used. This will try to
|
||||
* generate single letter variable names in the range 'i' to 'z' to start with.
|
||||
|
|
|
@ -278,6 +278,9 @@ Blockly.Workspace.prototype.renameVariable = function(oldName, newName) {
|
|||
* @param {string} name The new variable's name.
|
||||
*/
|
||||
Blockly.Workspace.prototype.createVariable = function(name) {
|
||||
if (name.toLowerCase() == Blockly.Variables.noVariableText()) {
|
||||
return;
|
||||
}
|
||||
var index = this.variableIndexOf(name);
|
||||
if (index == -1) {
|
||||
this.variableList.push(name);
|
||||
|
|
|
@ -956,6 +956,20 @@ Blockly.WorkspaceSvg.prototype.paste = function(xmlBlock) {
|
|||
block.select();
|
||||
};
|
||||
|
||||
/**
|
||||
* Rename a variable by updating its name in the variable list.
|
||||
* TODO: google/blockly:#468
|
||||
* @param {string} oldName Variable to rename.
|
||||
* @param {string} newName New variable name.
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.renameVariable = function(oldName, newName) {
|
||||
Blockly.WorkspaceSvg.superClass_.renameVariable.call(this, oldName, newName);
|
||||
// Refresh the toolbox unless there's a drag in progress.
|
||||
if (this.toolbox_ && this.toolbox_.flyout_ && !Blockly.Flyout.startFlyout_) {
|
||||
this.toolbox_.refreshSelection();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new variable with the given name. Update the flyout to show the new
|
||||
* variable immediately.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue