Add ordered option to getDescendants and all uses.

"
This commit is contained in:
Rachel Fenichel 2018-05-16 12:51:41 -07:00
parent e91e29c8d8
commit af4060c75b
16 changed files with 83 additions and 38 deletions

View file

@ -509,12 +509,30 @@ Blockly.Block.prototype.getRootBlock = function() {
/** /**
* Find all the blocks that are directly nested inside this one. * Find all the blocks that are directly nested inside this one.
* Includes value and block inputs, as well as any following statement. * Includes value and statement inputs, as well as any following statement.
* Excludes any connection on an output tab or any preceding statement. * Excludes any connection on an output tab or any preceding statement.
* Blocks are optionally sorted by position; top to bottom.
* @param {boolean} ordered Sort the list if true.
* @return {!Array.<!Blockly.Block>} Array of blocks. * @return {!Array.<!Blockly.Block>} Array of blocks.
*/ */
Blockly.Block.prototype.getChildren = function() { Blockly.Block.prototype.getChildren = function(ordered) {
return this.childBlocks_; if (!ordered) {
return this.childBlocks_;
}
var blocks = [];
for (var i = 0, input; input = this.inputList[i]; i++) {
if (input.connection) {
var child = input.connection.targetBlock();
if (child) {
blocks.push(child);
}
}
}
var next = this.getNextBlock();
if (next) {
blocks.push(next);
}
return blocks;
}; };
/** /**
@ -556,16 +574,20 @@ Blockly.Block.prototype.setParent = function(newParent) {
/** /**
* Find all the blocks that are directly or indirectly nested inside this one. * Find all the blocks that are directly or indirectly nested inside this one.
* Includes this block in the list. * Includes this block in the list.
* Includes value and block inputs, as well as any following statements. * Includes value and statement inputs, as well as any following statements.
* Excludes any connection on an output tab or any preceding statements. * Excludes any connection on an output tab or any preceding statements.
* Blocks are optionally sorted by position, top to bottom.
* @param {boolean} ordered Sort the list if true.
* @param {boolean=} opt_ignoreShadows If set, don't include shadow blocks. * @param {boolean=} opt_ignoreShadows If set, don't include shadow blocks.
* @return {!Array.<!Blockly.Block>} Flattened array of blocks. * @return {!Array.<!Blockly.Block>} Flattened array of blocks.
*/ */
Blockly.Block.prototype.getDescendants = function(opt_ignoreShadows) { Blockly.Block.prototype.getDescendants = function(ordered, opt_ignoreShadows) {
var blocks = [this]; var blocks = [this];
for (var child, x = 0; child = this.childBlocks_[x]; x++) { var childBlocks = this.getChildren(ordered);
for (var child, i = 0; child = childBlocks[i]; i++) {
if (!opt_ignoreShadows || !child.isShadow_) { if (!opt_ignoreShadows || !child.isShadow_) {
blocks.push.apply(blocks, child.getDescendants(opt_ignoreShadows)); blocks.push.apply(
blocks, child.getDescendants(ordered, opt_ignoreShadows));
} }
} }
return blocks; return blocks;

View file

@ -135,7 +135,7 @@ Blockly.BlockDragger.prototype.dispose = function() {
Blockly.BlockDragger.initIconData_ = function(block) { Blockly.BlockDragger.initIconData_ = function(block) {
// Build a list of icons that need to be moved and where they started. // Build a list of icons that need to be moved and where they started.
var dragIconData = []; var dragIconData = [];
var descendants = block.getDescendants(); var descendants = block.getDescendants(false);
for (var i = 0, descendant; descendant = descendants[i]; i++) { for (var i = 0, descendant; descendant = descendants[i]; i++) {
var icons = descendant.getIcons(); var icons = descendant.getIcons();
for (var j = 0; j < icons.length; j++) { for (var j = 0; j < icons.length; j++) {

View file

@ -42,7 +42,6 @@ goog.require('goog.Timer');
goog.require('goog.asserts'); goog.require('goog.asserts');
goog.require('goog.dom'); goog.require('goog.dom');
goog.require('goog.math.Coordinate'); goog.require('goog.math.Coordinate');
goog.require('goog.userAgent');
/** /**
@ -551,7 +550,7 @@ Blockly.BlockSvg.prototype.setCollapsed = function(collapsed) {
var COLLAPSED_INPUT_NAME = '_TEMP_COLLAPSED_INPUT'; var COLLAPSED_INPUT_NAME = '_TEMP_COLLAPSED_INPUT';
if (collapsed) { if (collapsed) {
var icons = this.getIcons(); var icons = this.getIcons();
for (i = 0; i < icons.length; i++) { for (var i = 0; i < icons.length; i++) {
icons[i].setVisible(false); icons[i].setVisible(false);
} }
var text = this.toString(Blockly.COLLAPSE_CHARS); var text = this.toString(Blockly.COLLAPSE_CHARS);

View file

@ -214,12 +214,13 @@ Blockly.ContextMenu.callbackFactory = function(block, xml) {
*/ */
Blockly.ContextMenu.blockDeleteOption = function(block) { Blockly.ContextMenu.blockDeleteOption = function(block) {
// Option to delete this block but not blocks lower in the stack. // Option to delete this block but not blocks lower in the stack.
// Count the number of blocks that are nested in this block. // Count the number of blocks that are nested in this block,
var descendantCount = block.getDescendants(true).length; // ignoring shadows and without ordering.
var descendantCount = block.getDescendants(false, true).length;
var nextBlock = block.getNextBlock(); var nextBlock = block.getNextBlock();
if (nextBlock) { if (nextBlock) {
// Blocks in the current stack would survive this block's deletion. // Blocks in the current stack would survive this block's deletion.
descendantCount -= nextBlock.getDescendants(true).length; descendantCount -= nextBlock.getDescendants(false, true).length;
} }
var deleteOption = { var deleteOption = {
text: descendantCount == 1 ? Blockly.Msg.DELETE_BLOCK : text: descendantCount == 1 ? Blockly.Msg.DELETE_BLOCK :

View file

@ -299,7 +299,7 @@ Blockly.Events.setGroup = function(state) {
*/ */
Blockly.Events.getDescendantIds_ = function(block) { Blockly.Events.getDescendantIds_ = function(block) {
var ids = []; var ids = [];
var descendants = block.getDescendants(); var descendants = block.getDescendants(false);
for (var i = 0, descendant; descendant = descendants[i]; i++) { for (var i = 0, descendant; descendant = descendants[i]; i++) {
ids[i] = descendant.id; ids[i] = descendant.id;
} }
@ -368,7 +368,7 @@ Blockly.Events.disableOrphans = function(event) {
var block = workspace.getBlockById(event.blockId); var block = workspace.getBlockById(event.blockId);
if (block) { if (block) {
if (block.getParent() && !block.getParent().disabled) { if (block.getParent() && !block.getParent().disabled) {
var children = block.getDescendants(); var children = block.getDescendants(false);
for (var i = 0, child; child = children[i]; i++) { for (var i = 0, child; child = children[i]; i++) {
child.setDisabled(false); child.setDisabled(false);
} }

View file

@ -328,7 +328,7 @@ Blockly.HorizontalFlyout.prototype.layout_ = function(contents, gaps) {
for (var i = 0, item; item = contents[i]; i++) { for (var i = 0, item; item = contents[i]; i++) {
if (item.type == 'block') { if (item.type == 'block') {
var block = item.block; var block = item.block;
var allBlocks = block.getDescendants(); var allBlocks = block.getDescendants(false);
for (var j = 0, child; child = allBlocks[j]; j++) { for (var j = 0, child; child = allBlocks[j]; j++) {
// Mark blocks as being inside a flyout. This is used to detect and // Mark blocks as being inside a flyout. This is used to detect and
// prevent the closure of the flyout if the user right-clicks on such a // prevent the closure of the flyout if the user right-clicks on such a

View file

@ -437,7 +437,7 @@ Blockly.VerticalFlyout.prototype.layout_ = function(contents, gaps) {
for (var i = 0, item; item = contents[i]; i++) { for (var i = 0, item; item = contents[i]; i++) {
if (item.type == 'block') { if (item.type == 'block') {
var block = item.block; var block = item.block;
var allBlocks = block.getDescendants(); var allBlocks = block.getDescendants(false);
for (var j = 0, child; child = allBlocks[j]; j++) { for (var j = 0, child; child = allBlocks[j]; j++) {
// Mark blocks as being inside a flyout. This is used to detect and // Mark blocks as being inside a flyout. This is used to detect and
// prevent the closure of the flyout if the user right-clicks on such a // prevent the closure of the flyout if the user right-clicks on such a

View file

@ -142,7 +142,7 @@ Blockly.Generator.prototype.prefixLines = function(text, prefix) {
*/ */
Blockly.Generator.prototype.allNestedComments = function(block) { Blockly.Generator.prototype.allNestedComments = function(block) {
var comments = []; var comments = [];
var blocks = block.getDescendants(); var blocks = block.getDescendants(true);
for (var i = 0; i < blocks.length; i++) { for (var i = 0; i < blocks.length; i++) {
var comment = blocks[i].getCommentText(); var comment = blocks[i].getCommentText();
if (comment) { if (comment) {
@ -378,9 +378,11 @@ Blockly.Generator.prototype.provideFunction_ = function(desiredName, code) {
* Hook for code to run before code generation starts. * Hook for code to run before code generation starts.
* Subclasses may override this, e.g. to initialise the database of variable * Subclasses may override this, e.g. to initialise the database of variable
* names. * names.
* @param {!Blockly.Workspace} workspace Workspace to generate code from. * @param {!Blockly.Workspace} _workspace Workspace to generate code from.
*/ */
Blockly.Generator.prototype.init = undefined; Blockly.Generator.prototype.init = function(_workspace) {
// Optionally override
};
/** /**
* Common tasks for generating code from blocks. This is called from * Common tasks for generating code from blocks. This is called from
@ -388,12 +390,15 @@ Blockly.Generator.prototype.init = undefined;
* Subclasses may override this, e.g. to generate code for statements following * Subclasses may override this, e.g. to generate code for statements following
* the block, or to handle comments for the specified block and any connected * the block, or to handle comments for the specified block and any connected
* value blocks. * value blocks.
* @param {!Blockly.Block} block The current block. * @param {!Blockly.Block} _block The current block.
* @param {string} code The JavaScript code created for this block. * @param {string} code The JavaScript code created for this block.
* @return {string} JavaScript code with comments and subsequent blocks added. * @return {string} JavaScript code with comments and subsequent blocks added.
* @private * @private
*/ */
Blockly.Generator.prototype.scrub_ = undefined; Blockly.Generator.prototype.scrub_ = function(_block, code) {
// Optionally override
return code;
};
/** /**
* Hook for code to run at end of code generation. * Hook for code to run at end of code generation.
@ -402,7 +407,10 @@ Blockly.Generator.prototype.scrub_ = undefined;
* @param {string} code Generated code. * @param {string} code Generated code.
* @return {string} Completed code. * @return {string} Completed code.
*/ */
Blockly.Generator.prototype.finish = undefined; Blockly.Generator.prototype.finish = function(code) {
// Optionally override
return code;
};
/** /**
* Naked values are top-level blocks with outputs that aren't plugged into * Naked values are top-level blocks with outputs that aren't plugged into
@ -412,4 +420,7 @@ Blockly.Generator.prototype.finish = undefined;
* @param {string} line Line of generated code. * @param {string} line Line of generated code.
* @return {string} Legal line of code. * @return {string} Legal line of code.
*/ */
Blockly.Generator.prototype.scrubNakedValue = undefined; Blockly.Generator.prototype.scrubNakedValue = function(line) {
// Optionally override
return line;
};

View file

@ -255,7 +255,7 @@ Blockly.Mutator.prototype.setVisible = function(visible) {
} }
this.rootBlock_ = this.block_.decompose(this.workspace_); this.rootBlock_ = this.block_.decompose(this.workspace_);
var blocks = this.rootBlock_.getDescendants(); var blocks = this.rootBlock_.getDescendants(false);
for (var i = 0, child; child = blocks[i]; i++) { for (var i = 0, child; child = blocks[i]; i++) {
child.render(); child.render();
} }

View file

@ -288,7 +288,7 @@ Blockly.Procedures.getCallers = function(name, ws, definitionRoot,
if (block.id == definitionRoot.id && !allowRecursive) { if (block.id == definitionRoot.id && !allowRecursive) {
continue; continue;
} }
allBlocks.push.apply(allBlocks, block.getDescendants()); allBlocks.push.apply(allBlocks, block.getDescendants(false));
} }
var callers = []; var callers = [];

View file

@ -280,7 +280,7 @@ Blockly.RenderedConnection.prototype.setHidden = function(hidden) {
Blockly.RenderedConnection.prototype.hideAll = function() { Blockly.RenderedConnection.prototype.hideAll = function() {
this.setHidden(true); this.setHidden(true);
if (this.targetConnection) { if (this.targetConnection) {
var blocks = this.targetBlock().getDescendants(); var blocks = this.targetBlock().getDescendants(false);
for (var i = 0; i < blocks.length; i++) { for (var i = 0; i < blocks.length; i++) {
var block = blocks[i]; var block = blocks[i];
// Hide all connections of all children. // Hide all connections of all children.

View file

@ -71,7 +71,7 @@ Blockly.scratchBlocksUtils.encodeEntities = function(rawStr) {
* @package * @package
*/ */
Blockly.scratchBlocksUtils.changeObscuredShadowIds = function(block) { Blockly.scratchBlocksUtils.changeObscuredShadowIds = function(block) {
var blocks = block.getDescendants(); var blocks = block.getDescendants(false);
for (var i = blocks.length - 1; i >= 0; i--) { for (var i = blocks.length - 1; i >= 0; i--) {
var descendant = blocks[i]; var descendant = blocks[i];
for (var j = 0; j < descendant.inputList.length; j++) { for (var j = 0; j < descendant.inputList.length; j++) {

View file

@ -54,7 +54,7 @@ Blockly.Variables.allUsedVariables = function(root) {
var blocks; var blocks;
if (root instanceof Blockly.Block) { if (root instanceof Blockly.Block) {
// Root is Block. // Root is Block.
blocks = root.getDescendants(); blocks = root.getDescendants(false);
} else if (root instanceof Blockly.Workspace || } else if (root instanceof Blockly.Workspace ||
root instanceof Blockly.WorkspaceSvg) { root instanceof Blockly.WorkspaceSvg) {
// Root is Workspace. // Root is Workspace.

View file

@ -260,19 +260,31 @@ Blockly.Workspace.prototype.getTopComments = function(ordered) {
}; };
/** /**
* Find all blocks in workspace. No particular order. * Find all blocks in workspace. Blocks are optionally sorted
* by position; top to bottom (with slight LTR or RTL bias).
* @param {boolean} ordered Sort the list if true.
* @return {!Array.<!Blockly.Block>} Array of blocks. * @return {!Array.<!Blockly.Block>} Array of blocks.
*/ */
Blockly.Workspace.prototype.getAllBlocks = function() { Blockly.Workspace.prototype.getAllBlocks = function(ordered) {
var blocks = this.getTopBlocks(false); if (ordered) {
for (var i = 0; i < blocks.length; i++) { // Slow, but ordered.
blocks.push.apply(blocks, blocks[i].getChildren()); var topBlocks = this.getTopBlocks(true);
var blocks = [];
for (var i = 0; i < topBlocks.length; i++) {
blocks.push.apply(blocks, topBlocks[i].getDescendants(true));
}
} else {
// Fast, but in no particular order.
var blocks = this.getTopBlocks(false);
for (var i = 0; i < blocks.length; i++) {
blocks.push.apply(blocks, blocks[i].getChildren(false));
}
} }
return blocks; return blocks;
}; };
/** /**
* Dispose of all blocks in workspace. * Dispose of all blocks and comments in workspace.
*/ */
Blockly.Workspace.prototype.clear = function() { Blockly.Workspace.prototype.clear = function() {
this.isClearing = true; this.isClearing = true;

View file

@ -1493,7 +1493,7 @@ Blockly.WorkspaceSvg.buildDeleteList_ = function(topBlocks) {
var deleteList = []; var deleteList = [];
function addDeletableBlocks(block) { function addDeletableBlocks(block) {
if (block.isDeletable()) { if (block.isDeletable()) {
deleteList = deleteList.concat(block.getDescendants()); deleteList = deleteList.concat(block.getDescendants(false));
} else { } else {
var children = block.getChildren(); var children = block.getChildren();
for (var i = 0; i < children.length; i++) { for (var i = 0; i < children.length; i++) {

View file

@ -541,7 +541,7 @@ Blockly.Xml.domToBlock = function(xmlBlock, workspace) {
try { try {
var topBlock = Blockly.Xml.domToBlockHeadless_(xmlBlock, workspace); var topBlock = Blockly.Xml.domToBlockHeadless_(xmlBlock, workspace);
// Generate list of all blocks. // Generate list of all blocks.
var blocks = topBlock.getDescendants(); var blocks = topBlock.getDescendants(false);
if (workspace.rendered) { if (workspace.rendered) {
// Hide connections to speed up assembly. // Hide connections to speed up assembly.
topBlock.setConnectionsHidden(true); topBlock.setConnectionsHidden(true);
@ -761,7 +761,7 @@ Blockly.Xml.domToBlockHeadless_ = function(xmlBlock, workspace) {
} }
if (xmlBlock.nodeName.toLowerCase() == 'shadow') { if (xmlBlock.nodeName.toLowerCase() == 'shadow') {
// Ensure all children are also shadows. // Ensure all children are also shadows.
var children = block.getChildren(); var children = block.getChildren(false);
for (var i = 0, child; child = children[i]; i++) { for (var i = 0, child; child = children[i]; i++) {
goog.asserts.assert( goog.asserts.assert(
child.isShadow(), 'Shadow block not allowed non-shadow child.'); child.isShadow(), 'Shadow block not allowed non-shadow child.');