mirror of
https://github.com/google/blockly.git
synced 2026-01-07 00:50:27 +01:00
Porting Loop blocks to JSON (#919)
* Improved documentation on `Blockly.Extensions.buildTooltipForDropdown` * Replaced incorrect uses of `@mixes` JSDoc annotation (on mixin extensions) with `@augments Blockly.Block`. * Added Blockly.Extensions.buildTooltipWithFieldValue() extension helper.
This commit is contained in:
committed by
GitHub
parent
49c9c024e4
commit
a4cfd5cbc8
@@ -273,9 +273,7 @@ Blockly.defineBlocksWithJsonArray([ // Mutator blocks. Do not extract.
|
||||
/**
|
||||
* Tooltip text, keyed by block OP value. Used by logic_compare and
|
||||
* logic_operation blocks.
|
||||
*
|
||||
* Messages are not dereferenced here in order to capture possible language
|
||||
* changes.
|
||||
* @see {Blockly.Extensions#buildTooltipForDropdown}
|
||||
* @package
|
||||
* @readonly
|
||||
*/
|
||||
@@ -297,10 +295,10 @@ Blockly.Extensions.register('logic_op_tooltip',
|
||||
Blockly.Extensions.buildTooltipForDropdown(
|
||||
'OP', Blockly.Constants.Logic.TOOLTIPS_BY_OP));
|
||||
|
||||
|
||||
/**
|
||||
* Mutator methods added to controls_if blocks.
|
||||
* @mixin
|
||||
* @augments Blockly.Block
|
||||
* @package
|
||||
* @readonly
|
||||
*/
|
||||
@@ -460,7 +458,6 @@ Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN = {
|
||||
* "controls_if" extension function. Adds mutator, shape updating methods, and
|
||||
* dynamic tooltip to "controls_if" blocks.
|
||||
* @this Blockly.Block
|
||||
* @mixes Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN
|
||||
* @package
|
||||
*/
|
||||
Blockly.Constants.Logic.CONTROLS_IF_EXTENSION = function() {
|
||||
@@ -517,6 +514,7 @@ Blockly.Constants.Logic.fixLogicCompareRtlOpLabels =
|
||||
/**
|
||||
* Adds dynamic type validation for the left and right sides of a logic_compate block.
|
||||
* @mixin
|
||||
* @augments Blockly.Block
|
||||
* @package
|
||||
* @readonly
|
||||
*/
|
||||
@@ -557,7 +555,6 @@ Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN = {
|
||||
* dropdown labels, and adds type left and right side type checking to
|
||||
* "logic_compare" blocks.
|
||||
* @this Blockly.Block
|
||||
* @mixes Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN
|
||||
* @package
|
||||
* @readonly
|
||||
*/
|
||||
@@ -577,6 +574,7 @@ Blockly.Extensions.register('logic_compare',
|
||||
/**
|
||||
* Adds type coordination between inputs and output.
|
||||
* @mixin
|
||||
* @augments Blockly.Block
|
||||
* @package
|
||||
* @readonly
|
||||
*/
|
||||
|
||||
463
blocks/loops.js
463
blocks/loops.js
@@ -20,165 +20,252 @@
|
||||
|
||||
/**
|
||||
* @fileoverview Loop blocks for Blockly.
|
||||
*
|
||||
* This file is scraped to extract a .json file of block definitions. The array
|
||||
* passed to defineBlocksWithJsonArray(..) must be strict JSON: double quotes
|
||||
* only, no outside references, no functions, no trailing commas, etc. The one
|
||||
* exception is end-of-line comments, which the scraper will remove.
|
||||
* @author fraser@google.com (Neil Fraser)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Blocks.loops');
|
||||
goog.provide('Blockly.Blocks.loops'); // Deprecated
|
||||
goog.provide('Blockly.Constants.Loops');
|
||||
|
||||
goog.require('Blockly.Blocks');
|
||||
|
||||
|
||||
/**
|
||||
* Common HSV hue for all blocks in this category.
|
||||
* Should be the same as Blockly.Msg.LOOPS_HUE
|
||||
* @readonly
|
||||
*/
|
||||
Blockly.Blocks.loops.HUE = 120;
|
||||
Blockly.Constants.Loops.HUE = 120;
|
||||
/** @deprecated Use Blockly.Constants.Loops.HUE */
|
||||
Blockly.Blocks.loops.HUE = Blockly.Constants.Loops.HUE;
|
||||
|
||||
Blockly.Blocks['controls_repeat_ext'] = {
|
||||
/**
|
||||
* Block for repeat n times (external number).
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
init: function() {
|
||||
this.jsonInit({
|
||||
"message0": Blockly.Msg.CONTROLS_REPEAT_TITLE,
|
||||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "TIMES",
|
||||
"check": "Number"
|
||||
}
|
||||
],
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
"colour": Blockly.Blocks.loops.HUE,
|
||||
"tooltip": Blockly.Msg.CONTROLS_REPEAT_TOOLTIP,
|
||||
"helpUrl": Blockly.Msg.CONTROLS_REPEAT_HELPURL
|
||||
});
|
||||
this.appendStatementInput('DO')
|
||||
.appendField(Blockly.Msg.CONTROLS_REPEAT_INPUT_DO);
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['controls_repeat'] = {
|
||||
/**
|
||||
* Block for repeat n times (internal number).
|
||||
* The 'controls_repeat_ext' block is preferred as it is more flexible.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
init: function() {
|
||||
this.jsonInit({
|
||||
"message0": Blockly.Msg.CONTROLS_REPEAT_TITLE,
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_number",
|
||||
"name": "TIMES",
|
||||
"value": 10,
|
||||
"min": 0,
|
||||
"precision": 1
|
||||
}
|
||||
],
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
"colour": Blockly.Blocks.loops.HUE,
|
||||
"tooltip": Blockly.Msg.CONTROLS_REPEAT_TOOLTIP,
|
||||
"helpUrl": Blockly.Msg.CONTROLS_REPEAT_HELPURL
|
||||
});
|
||||
this.appendStatementInput('DO')
|
||||
.appendField(Blockly.Msg.CONTROLS_REPEAT_INPUT_DO);
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['controls_whileUntil'] = {
|
||||
/**
|
||||
* Block for 'do while/until' loop.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
init: function() {
|
||||
var OPERATORS =
|
||||
[[Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_WHILE, 'WHILE'],
|
||||
[Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_UNTIL, 'UNTIL']];
|
||||
this.setHelpUrl(Blockly.Msg.CONTROLS_WHILEUNTIL_HELPURL);
|
||||
this.setColour(Blockly.Blocks.loops.HUE);
|
||||
this.appendValueInput('BOOL')
|
||||
.setCheck('Boolean')
|
||||
.appendField(new Blockly.FieldDropdown(OPERATORS), 'MODE');
|
||||
this.appendStatementInput('DO')
|
||||
.appendField(Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO);
|
||||
this.setPreviousStatement(true);
|
||||
this.setNextStatement(true);
|
||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
||||
var thisBlock = this;
|
||||
this.setTooltip(function() {
|
||||
var op = thisBlock.getFieldValue('MODE');
|
||||
var TOOLTIPS = {
|
||||
'WHILE': Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE,
|
||||
'UNTIL': Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL
|
||||
};
|
||||
return TOOLTIPS[op];
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['controls_for'] = {
|
||||
/**
|
||||
* Block for 'for' loop.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
init: function() {
|
||||
this.jsonInit({
|
||||
"message0": Blockly.Msg.CONTROLS_FOR_TITLE,
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_variable",
|
||||
"name": "VAR",
|
||||
"variable": null
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "FROM",
|
||||
"check": "Number",
|
||||
"align": "RIGHT"
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "TO",
|
||||
"check": "Number",
|
||||
"align": "RIGHT"
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "BY",
|
||||
"check": "Number",
|
||||
"align": "RIGHT"
|
||||
}
|
||||
],
|
||||
"inputsInline": true,
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
"colour": Blockly.Blocks.loops.HUE,
|
||||
"helpUrl": Blockly.Msg.CONTROLS_FOR_HELPURL
|
||||
});
|
||||
this.appendStatementInput('DO')
|
||||
.appendField(Blockly.Msg.CONTROLS_FOR_INPUT_DO);
|
||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
||||
var thisBlock = this;
|
||||
this.setTooltip(function() {
|
||||
return Blockly.Msg.CONTROLS_FOR_TOOLTIP.replace('%1',
|
||||
thisBlock.getFieldValue('VAR'));
|
||||
});
|
||||
Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT
|
||||
// Block for repeat n times (external number).
|
||||
{
|
||||
"type": "controls_repeat_ext",
|
||||
"message0": "%{BKY_CONTROLS_REPEAT_TITLE}",
|
||||
"args0": [{
|
||||
"type": "input_value",
|
||||
"name": "TIMES",
|
||||
"check": "Number"
|
||||
}],
|
||||
"message1": "%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",
|
||||
"args1": [{
|
||||
"type": "input_statement",
|
||||
"name": "DO"
|
||||
}],
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
"colour": "%{BKY_LOOPS_HUE}",
|
||||
"tooltip": "%{BKY_CONTROLS_REPEAT_TOOLTIP}",
|
||||
"helpUrl": "%{BKY_CONTROLS_REPEAT_HELPURL}"
|
||||
},
|
||||
// Block for repeat n times (internal number).
|
||||
// The 'controls_repeat_ext' block is preferred as it is more flexible.
|
||||
{
|
||||
"type": "controls_repeat",
|
||||
"message0": "%{BKY_CONTROLS_REPEAT_TITLE}",
|
||||
"args0": [{
|
||||
"type": "field_number",
|
||||
"name": "TIMES",
|
||||
"value": 10,
|
||||
"min": 0,
|
||||
"precision": 1
|
||||
}],
|
||||
"message1": "%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",
|
||||
"args1": [{
|
||||
"type": "input_statement",
|
||||
"name": "DO"
|
||||
}],
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
"colour": "%{BKY_LOOPS_HUE}",
|
||||
"tooltip": "%{BKY_CONTROLS_REPEAT_TOOLTIP}",
|
||||
"helpUrl": "%{BKY_CONTROLS_REPEAT_HELPURL}"
|
||||
},
|
||||
// Block for 'do while/until' loop.
|
||||
{
|
||||
"type": "controls_whileUntil",
|
||||
"message0": "%1 %2",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_dropdown",
|
||||
"name": "MODE",
|
||||
"options": [
|
||||
["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_WHILE}", "WHILE"],
|
||||
["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_UNTIL}", "UNTIL"]
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "BOOL",
|
||||
"check": "Boolean"
|
||||
}
|
||||
],
|
||||
"message1": "%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",
|
||||
"args1": [{
|
||||
"type": "input_statement",
|
||||
"name": "DO"
|
||||
}],
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
"colour": "%{BKY_LOOPS_HUE}",
|
||||
"helpUrl": "%{BKY_CONTROLS_WHILEUNTIL_HELPURL}",
|
||||
"extensions": ["controls_whileUntil_tooltip"]
|
||||
},
|
||||
// Block for 'for' loop.
|
||||
{
|
||||
"type": "controls_for",
|
||||
"message0": "%{BKY_CONTROLS_FOR_TITLE}",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_variable",
|
||||
"name": "VAR",
|
||||
"variable": null
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "FROM",
|
||||
"check": "Number",
|
||||
"align": "RIGHT"
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "TO",
|
||||
"check": "Number",
|
||||
"align": "RIGHT"
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "BY",
|
||||
"check": "Number",
|
||||
"align": "RIGHT"
|
||||
}
|
||||
],
|
||||
"message1": "%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",
|
||||
"args1": [{
|
||||
"type": "input_statement",
|
||||
"name": "DO"
|
||||
}],
|
||||
"inputsInline": true,
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
"colour": "%{BKY_LOOPS_HUE}",
|
||||
"helpUrl": "%{BKY_CONTROLS_FOR_HELPURL}",
|
||||
"extensions": [
|
||||
"contextMenu_newGetVariableBlock",
|
||||
"controls_for_tooltip"
|
||||
]
|
||||
},
|
||||
// Block for 'for each' loop.
|
||||
{
|
||||
"type": "controls_forEach",
|
||||
"message0": "%{BKY_CONTROLS_FOREACH_TITLE}",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_variable",
|
||||
"name": "VAR",
|
||||
"variable": null
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "LIST",
|
||||
"check": "Array"
|
||||
}
|
||||
],
|
||||
"message1": "%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",
|
||||
"args1": [{
|
||||
"type": "input_statement",
|
||||
"name": "DO"
|
||||
}],
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
"colour": "%{BKY_LOOPS_HUE}",
|
||||
"helpUrl": "%{BKY_CONTROLS_FOREACH_HELPURL}",
|
||||
"extensions": [
|
||||
"contextMenu_newGetVariableBlock",
|
||||
"controls_forEach_tooltip"
|
||||
]
|
||||
},
|
||||
// Block for flow statements: continue, break.
|
||||
{
|
||||
"type": "controls_flow_statements",
|
||||
"message0": "%1",
|
||||
"args0": [{
|
||||
"type": "field_dropdown",
|
||||
"name": "FLOW",
|
||||
"options": [
|
||||
["%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK}", "BREAK"],
|
||||
["%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE}", "CONTINUE"]
|
||||
]
|
||||
}],
|
||||
"previousStatement": null,
|
||||
"colour": "%{BKY_LOOPS_HUE}",
|
||||
"helpUrl": "%{BKY_CONTROLS_FLOW_STATEMENTS_HELPURL}",
|
||||
"extensions": [
|
||||
"controls_flow_tooltip",
|
||||
"controls_flow_in_loop_check"
|
||||
]
|
||||
}
|
||||
]); // END JSON EXTRACT (Do not delete this comment.)
|
||||
|
||||
/**
|
||||
* Tooltips for the 'controls_whileUntil' block, keyed by MODE value.
|
||||
* @see {Blockly.Extensions#buildTooltipForDropdown}
|
||||
* @package
|
||||
* @readonly
|
||||
*/
|
||||
Blockly.Constants.Loops.WHILE_UNTIL_TOOLTIPS = {
|
||||
'WHILE': '%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_WHILE}',
|
||||
'UNTIL': '%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL}'
|
||||
};
|
||||
|
||||
Blockly.Extensions.register('controls_whileUntil_tooltip',
|
||||
Blockly.Extensions.buildTooltipForDropdown(
|
||||
'MODE', Blockly.Constants.Loops.WHILE_UNTIL_TOOLTIPS));
|
||||
|
||||
/**
|
||||
* Tooltips for the 'controls_flow_statements' block, keyed by FLOW value.
|
||||
* @see {Blockly.Extensions#buildTooltipForDropdown}
|
||||
* @package
|
||||
* @readonly
|
||||
*/
|
||||
Blockly.Constants.Loops.BREAK_CONTINUE_TOOLTIPS = {
|
||||
'BREAK': '%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK}',
|
||||
'CONTINUE': '%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE}'
|
||||
};
|
||||
|
||||
Blockly.Extensions.register('controls_flow_tooltip',
|
||||
Blockly.Extensions.buildTooltipForDropdown(
|
||||
'FLOW', Blockly.Constants.Loops.BREAK_CONTINUE_TOOLTIPS));
|
||||
|
||||
/**
|
||||
* Mixin to add a context menu item to create a 'variables_get' block.
|
||||
* Used by blocks 'controls_for' and 'controls_forEach'.
|
||||
* @mixin
|
||||
* @augments Blockly.Block
|
||||
* @package
|
||||
* @readonly
|
||||
*/
|
||||
Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN = {
|
||||
/**
|
||||
* Add menu option to create getter block for loop variable.
|
||||
* 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 varName = this.getFieldValue('VAR');
|
||||
if (!this.isCollapsed() && varName != null) {
|
||||
var option = {enabled: true};
|
||||
var name = this.getFieldValue('VAR');
|
||||
option.text = Blockly.Msg.VARIABLES_SET_CREATE_GET.replace('%1', name);
|
||||
var xmlField = goog.dom.createDom('field', null, name);
|
||||
option.text =
|
||||
Blockly.Msg.VARIABLES_SET_CREATE_GET.replace('%1', varName);
|
||||
var xmlField = goog.dom.createDom('field', null, varName);
|
||||
xmlField.setAttribute('name', 'VAR');
|
||||
var xmlBlock = goog.dom.createDom('block', null, xmlField);
|
||||
xmlBlock.setAttribute('type', 'variables_get');
|
||||
@@ -188,68 +275,34 @@ Blockly.Blocks['controls_for'] = {
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['controls_forEach'] = {
|
||||
/**
|
||||
* Block for 'for each' loop.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
init: function() {
|
||||
this.jsonInit({
|
||||
"message0": Blockly.Msg.CONTROLS_FOREACH_TITLE,
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_variable",
|
||||
"name": "VAR",
|
||||
"variable": null
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "LIST",
|
||||
"check": "Array"
|
||||
}
|
||||
],
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
"colour": Blockly.Blocks.loops.HUE,
|
||||
"helpUrl": Blockly.Msg.CONTROLS_FOREACH_HELPURL
|
||||
});
|
||||
this.appendStatementInput('DO')
|
||||
.appendField(Blockly.Msg.CONTROLS_FOREACH_INPUT_DO);
|
||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
||||
var thisBlock = this;
|
||||
this.setTooltip(function() {
|
||||
return Blockly.Msg.CONTROLS_FOREACH_TOOLTIP.replace('%1',
|
||||
thisBlock.getFieldValue('VAR'));
|
||||
});
|
||||
},
|
||||
customContextMenu: Blockly.Blocks['controls_for'].customContextMenu
|
||||
};
|
||||
Blockly.Extensions.registerMixin('contextMenu_newGetVariableBlock',
|
||||
Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN);
|
||||
|
||||
Blockly.Blocks['controls_flow_statements'] = {
|
||||
Blockly.Extensions.register('controls_for_tooltip',
|
||||
Blockly.Extensions.buildTooltipWithFieldValue(
|
||||
Blockly.Msg.CONTROLS_FOR_TOOLTIP, 'VAR'));
|
||||
|
||||
Blockly.Extensions.register('controls_forEach_tooltip',
|
||||
Blockly.Extensions.buildTooltipWithFieldValue(
|
||||
Blockly.Msg.CONTROLS_FOREACH_TOOLTIP, 'VAR'));
|
||||
|
||||
/**
|
||||
* This mixin adds a check to make sure the 'controls_flow_statements' block
|
||||
* is contained in a loop. Otherwise a warning is added to the block.
|
||||
* @mixin
|
||||
* @augments Blockly.Block
|
||||
* @package
|
||||
* @readonly
|
||||
*/
|
||||
Blockly.Constants.Loops.CONTROL_FLOW_CHECK_IN_LOOP_MIXIN = {
|
||||
/**
|
||||
* Block for flow statements: continue, break.
|
||||
* @this Blockly.Block
|
||||
* List of block types that are loops and thus do not need warnings.
|
||||
* To add a new loop type add this to your code:
|
||||
* Blockly.Blocks['controls_flow_statements'].LOOP_TYPES.push('custom_loop');
|
||||
*/
|
||||
init: function() {
|
||||
var OPERATORS =
|
||||
[[Blockly.Msg.CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK, 'BREAK'],
|
||||
[Blockly.Msg.CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE, 'CONTINUE']];
|
||||
this.setHelpUrl(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_HELPURL);
|
||||
this.setColour(Blockly.Blocks.loops.HUE);
|
||||
this.appendDummyInput()
|
||||
.appendField(new Blockly.FieldDropdown(OPERATORS), 'FLOW');
|
||||
this.setPreviousStatement(true);
|
||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
||||
var thisBlock = this;
|
||||
this.setTooltip(function() {
|
||||
var op = thisBlock.getFieldValue('FLOW');
|
||||
var TOOLTIPS = {
|
||||
'BREAK': Blockly.Msg.CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK,
|
||||
'CONTINUE': Blockly.Msg.CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE
|
||||
};
|
||||
return TOOLTIPS[op];
|
||||
});
|
||||
},
|
||||
LOOP_TYPES: ['controls_repeat', 'controls_repeat_ext', 'controls_forEach',
|
||||
'controls_for', 'controls_whileUntil'],
|
||||
|
||||
/**
|
||||
* Called whenever anything on the workspace changes.
|
||||
* Add warning if this flow block is not nested inside a loop.
|
||||
@@ -281,12 +334,8 @@ Blockly.Blocks['controls_flow_statements'] = {
|
||||
this.setDisabled(true);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* List of block types that are loops and thus do not need warnings.
|
||||
* To add a new loop type add this to your code:
|
||||
* Blockly.Blocks['controls_flow_statements'].LOOP_TYPES.push('custom_loop');
|
||||
*/
|
||||
LOOP_TYPES: ['controls_repeat', 'controls_repeat_ext', 'controls_forEach',
|
||||
'controls_for', 'controls_whileUntil']
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Extensions.registerMixin('controls_flow_in_loop_check',
|
||||
Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN);
|
||||
|
||||
@@ -379,9 +379,7 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT
|
||||
/**
|
||||
* Mapping of math block OP value to tooltip message for blocks
|
||||
* math_arithmetic, math_simple, math_trig, and math_on_lists.
|
||||
*
|
||||
* Messages are not dereferenced here in order to capture possible language
|
||||
* changes.
|
||||
* @see {Blockly.Extensions#buildTooltipForDropdown}
|
||||
* @package
|
||||
* @readonly
|
||||
*/
|
||||
@@ -430,6 +428,8 @@ Blockly.Extensions.register('math_op_tooltip',
|
||||
* Mixin for mutator functions in the 'math_is_divisibleby_mutator'
|
||||
* extension.
|
||||
* @mixin
|
||||
* @augments Blockly.Block
|
||||
* @package
|
||||
*/
|
||||
Blockly.Constants.Math.IS_DIVISIBLEBY_MUTATOR_MIXIN = {
|
||||
/**
|
||||
@@ -476,8 +476,7 @@ Blockly.Constants.Math.IS_DIVISIBLEBY_MUTATOR_MIXIN = {
|
||||
* 'math_is_divisibleby_mutator' extension to the 'math_property' block that
|
||||
* can update the block shape (add/remove divisor input) based on whether
|
||||
* property is "divisble by".
|
||||
* @this {Blockly.Block}
|
||||
* @mixes Blockly.Constants.Math.IS_DIVISIBLEBY_MUTATOR_MIXIN
|
||||
* @this Blockly.Block
|
||||
* @package
|
||||
*/
|
||||
Blockly.Constants.Math.IS_DIVISIBLE_MUTATOR_EXTENSION = function() {
|
||||
@@ -493,7 +492,7 @@ Blockly.Extensions.register('math_is_divisibleby_mutator',
|
||||
|
||||
/**
|
||||
* Update the tooltip of 'math_change' block to reference the variable.
|
||||
* @this {Blockly.Block}
|
||||
* @this Blockly.Block
|
||||
* @package
|
||||
*/
|
||||
Blockly.Constants.Math.CHANGE_TOOLTIP_EXTENSION = function() {
|
||||
@@ -504,12 +503,14 @@ Blockly.Constants.Math.CHANGE_TOOLTIP_EXTENSION = function() {
|
||||
};
|
||||
|
||||
Blockly.Extensions.register('math_change_tooltip',
|
||||
Blockly.Constants.Math.CHANGE_TOOLTIP_EXTENSION);
|
||||
Blockly.Extensions.buildTooltipWithFieldValue(
|
||||
Blockly.Msg.MATH_CHANGE_TOOLTIP, 'VAR'));
|
||||
|
||||
/**
|
||||
* Mixin with mutator methods to support alternate output based if the
|
||||
* 'math_on_list' block uses the 'MODE' operation.
|
||||
* @mixin
|
||||
* @augments Blockly.Block
|
||||
* @package
|
||||
* @readonly
|
||||
*/
|
||||
@@ -550,8 +551,7 @@ Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN = {
|
||||
/**
|
||||
* Extension to 'math_on_list' blocks that allows support of
|
||||
* modes operation (outputs a list of numbers).
|
||||
* @this {Blockly.Block}
|
||||
* @mixes Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN
|
||||
* @this Blockly.Block
|
||||
* @package
|
||||
*/
|
||||
Blockly.Constants.Math.LIST_MODES_MUTATOR_EXTENSION = function() {
|
||||
|
||||
@@ -92,9 +92,18 @@ Blockly.Extensions.apply = function(name, block) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds an extension function that will map a dropdown value to a tooltip string.
|
||||
* Tooltip strings will be passed through Blockly.utils.checkMessageReferences(..)
|
||||
* immediately and Blockly.utils.replaceMessageReferences(..) at display time.
|
||||
* Builds an extension function that will map a dropdown value to a tooltip
|
||||
* string.
|
||||
*
|
||||
* This method includes multiple checks to ensure tooltips, dropdown options,
|
||||
* and message references are aligned. This aims to catch errors as early as
|
||||
* possible, without requiring developers to manually test tooltips under each
|
||||
* option. After the page is loaded, each tooltip text string will be checked
|
||||
* for matching message keys in the internationalized string table. Deferring
|
||||
* this until the page is loaded decouples loading dependencies. Later, upon
|
||||
* loading the first block of any given type, the extension will validate every
|
||||
* dropdown option has a matching tooltip in the lookupTable. Errors are
|
||||
* reported as warnings in the console, and are never fatal.
|
||||
* @param {string} dropdownName The name of the field whose value is the key
|
||||
* to the lookup table.
|
||||
* @param {!Object<string, string>} lookupTable The table of field values to
|
||||
@@ -112,6 +121,7 @@ Blockly.Extensions.buildTooltipForDropdown = function(dropdownName, lookupTable)
|
||||
if (document) { // Relies on document.readyState
|
||||
Blockly.utils.runAfterPageLoad(function() {
|
||||
for (var key in lookupTable) {
|
||||
// Will print warnings is reference is missing.
|
||||
Blockly.utils.checkMessageReferences(lookupTable[key]);
|
||||
}
|
||||
});
|
||||
@@ -122,8 +132,6 @@ Blockly.Extensions.buildTooltipForDropdown = function(dropdownName, lookupTable)
|
||||
* @this {Blockly.Block}
|
||||
*/
|
||||
var extensionFn = function() {
|
||||
var thisBlock = this;
|
||||
|
||||
if (this.type && blockTypesChecked.indexOf(this.type) === -1) {
|
||||
Blockly.Extensions.checkDropdownOptionsInTable_(
|
||||
this, dropdownName, lookupTable);
|
||||
@@ -131,15 +139,15 @@ Blockly.Extensions.buildTooltipForDropdown = function(dropdownName, lookupTable)
|
||||
}
|
||||
|
||||
this.setTooltip(function() {
|
||||
var value = thisBlock.getFieldValue(dropdownName);
|
||||
var value = this.getFieldValue(dropdownName);
|
||||
var tooltip = lookupTable[value];
|
||||
if (tooltip == null) {
|
||||
if (blockTypesChecked.indexOf(thisBlock.type) === -1) {
|
||||
if (blockTypesChecked.indexOf(this.type) === -1) {
|
||||
// Warn for missing values on generated tooltips
|
||||
var warning = 'No tooltip mapping for value ' + value +
|
||||
' of field ' + dropdownName;
|
||||
if (thisBlock.type != null) {
|
||||
warning += (' of block type ' + thisBlock.type);
|
||||
if (this.type != null) {
|
||||
warning += (' of block type ' + this.type);
|
||||
}
|
||||
console.warn(warning + '.');
|
||||
}
|
||||
@@ -147,7 +155,7 @@ Blockly.Extensions.buildTooltipForDropdown = function(dropdownName, lookupTable)
|
||||
tooltip = Blockly.utils.replaceMessageReferences(tooltip);
|
||||
}
|
||||
return tooltip;
|
||||
});
|
||||
}.bind(this));
|
||||
};
|
||||
return extensionFn;
|
||||
};
|
||||
@@ -158,6 +166,7 @@ Blockly.Extensions.buildTooltipForDropdown = function(dropdownName, lookupTable)
|
||||
* @param {!Blockly.Block} block The block containing the dropdown
|
||||
* @param {string} dropdownName The name of the dropdown
|
||||
* @param {!Object<string, string>} lookupTable The string lookup table
|
||||
* @private
|
||||
*/
|
||||
Blockly.Extensions.checkDropdownOptionsInTable_ =
|
||||
function(block, dropdownName, lookupTable) {
|
||||
@@ -175,6 +184,41 @@ Blockly.Extensions.checkDropdownOptionsInTable_ =
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds an extension function that will install a dynamic tooltip. The
|
||||
* tooltip message should include the string '%1' and that string will be
|
||||
* replaced with the value of the named field.
|
||||
* @param {string} msgTemplate The template form to of the message text, with
|
||||
* %1 placeholder.
|
||||
* @param {string} fieldName The field with the replacement value.
|
||||
* @returns {Function} The extension function.
|
||||
*/
|
||||
Blockly.Extensions.buildTooltipWithFieldValue =
|
||||
function(msgTemplate, fieldName) {
|
||||
// Check the tooltip string messages for invalid references.
|
||||
// Wait for load, in case Blockly.Msg is not yet populated.
|
||||
// runAfterPageLoad() does not run in a Node.js environment due to lack of
|
||||
// document object, in which case skip the validation.
|
||||
if (document) { // Relies on document.readyState
|
||||
Blockly.utils.runAfterPageLoad(function() {
|
||||
// Will print warnings is reference is missing.
|
||||
Blockly.utils.checkMessageReferences(msgTemplate);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual extension.
|
||||
* @this {Blockly.Block}
|
||||
*/
|
||||
var extensionFn = function() {
|
||||
this.setTooltip(function() {
|
||||
return Blockly.utils.replaceMessageReferences(msgTemplate)
|
||||
.replace('%1', this.getFieldValue(fieldName));
|
||||
}.bind(this));
|
||||
};
|
||||
return extensionFn;
|
||||
};
|
||||
|
||||
/**
|
||||
* Configures the tooltip to mimic the parent block when connected. Otherwise,
|
||||
* uses the tooltip text at the time this extension is initialized. This takes
|
||||
|
||||
@@ -5,10 +5,15 @@
|
||||
<mutation name="test repeat"></mutation>
|
||||
<next>
|
||||
<block type="procedures_callnoreturn">
|
||||
<mutation name="test while"></mutation>
|
||||
<mutation name="test repeat_ext"></mutation>
|
||||
<next>
|
||||
<block type="procedures_callnoreturn">
|
||||
<mutation name="test foreach"></mutation>
|
||||
<mutation name="test while"></mutation>
|
||||
<next>
|
||||
<block type="procedures_callnoreturn">
|
||||
<mutation name="test foreach"></mutation>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
@@ -236,9 +241,57 @@
|
||||
</block>
|
||||
</statement>
|
||||
</block>
|
||||
<block type="procedures_defnoreturn" x="397" y="628">
|
||||
<block type="procedures_defnoreturn" x="397" y="448">
|
||||
<mutation></mutation>
|
||||
<field name="NAME">test repeat</field>
|
||||
<statement name="STACK">
|
||||
<block type="variables_set" inline="false">
|
||||
<field name="VAR">count</field>
|
||||
<value name="VALUE">
|
||||
<block type="math_number">
|
||||
<field name="NUM">0</field>
|
||||
</block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="controls_repeat" inline="true">
|
||||
<field name="TIMES">10</field>
|
||||
<statement name="DO">
|
||||
<block type="math_change" inline="false">
|
||||
<field name="VAR">count</field>
|
||||
<value name="DELTA">
|
||||
<block type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</statement>
|
||||
<next>
|
||||
<block type="unittest_assertequals" inline="false">
|
||||
<value name="MESSAGE">
|
||||
<block type="text">
|
||||
<field name="TEXT">repeat 10</field>
|
||||
</block>
|
||||
</value>
|
||||
<value name="ACTUAL">
|
||||
<block type="variables_get">
|
||||
<field name="VAR">count</field>
|
||||
</block>
|
||||
</value>
|
||||
<value name="EXPECTED">
|
||||
<block type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</statement>
|
||||
</block>
|
||||
<block type="procedures_defnoreturn" x="397" y="668">
|
||||
<mutation></mutation>
|
||||
<field name="NAME">test repeat_ext</field>
|
||||
<statement name="STACK">
|
||||
<block type="variables_set" inline="false">
|
||||
<field name="VAR">count</field>
|
||||
|
||||
Reference in New Issue
Block a user