From 662d79443f4e9a59334e19e2802399e6f427812c Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Tue, 17 Apr 2018 11:41:03 -0700 Subject: [PATCH] Add 'ordered' option to descendant getting functions. (#1786) --- core/block.js | 35 ++++++++++++++++++++++++++++------- core/block_dragger.js | 2 +- core/block_svg.js | 2 +- core/contextmenu.js | 7 ++++--- core/events/events.js | 4 ++-- core/flyout_base.js | 2 +- core/flyout_horizontal.js | 2 +- core/flyout_vertical.js | 2 +- core/generator.js | 2 +- core/mutator.js | 2 +- core/rendered_connection.js | 2 +- core/toolbox.js | 2 +- core/workspace.js | 22 +++++++++++++++++----- core/workspace_svg.js | 4 ++-- core/xml.js | 4 ++-- 15 files changed, 64 insertions(+), 30 deletions(-) diff --git a/core/block.js b/core/block.js index 249e48125..55536627e 100644 --- a/core/block.js +++ b/core/block.js @@ -457,12 +457,30 @@ Blockly.Block.prototype.getRootBlock = function() { /** * 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. + * Blocks are optionally sorted by position; top to bottom. + * @param {boolean} ordered Sort the list if true. * @return {!Array.} Array of blocks. */ -Blockly.Block.prototype.getChildren = function() { - return this.childBlocks_; +Blockly.Block.prototype.getChildren = function(ordered) { + 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; }; /** @@ -504,14 +522,17 @@ Blockly.Block.prototype.setParent = function(newParent) { /** * Find all the blocks that are directly or indirectly nested inside this one. * 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. + * Blocks are optionally sorted by position; top to bottom. + * @param {boolean} ordered Sort the list if true. * @return {!Array.} Flattened array of blocks. */ -Blockly.Block.prototype.getDescendants = function() { +Blockly.Block.prototype.getDescendants = function(ordered) { var blocks = [this]; - for (var child, x = 0; child = this.childBlocks_[x]; x++) { - blocks.push.apply(blocks, child.getDescendants()); + var childBlocks = this.getChildren(ordered); + for (var child, i = 0; child = childBlocks[i]; i++) { + blocks.push.apply(blocks, child.getDescendants(ordered)); } return blocks; }; diff --git a/core/block_dragger.js b/core/block_dragger.js index dd1feba98..7a423403d 100644 --- a/core/block_dragger.js +++ b/core/block_dragger.js @@ -124,7 +124,7 @@ Blockly.BlockDragger.prototype.dispose = function() { Blockly.BlockDragger.initIconData_ = function(block) { // Build a list of icons that need to be moved and where they started. var dragIconData = []; - var descendants = block.getDescendants(); + var descendants = block.getDescendants(false); for (var i = 0, descendant; descendant = descendants[i]; i++) { var icons = descendant.getIcons(); for (var j = 0; j < icons.length; j++) { diff --git a/core/block_svg.js b/core/block_svg.js index 2e5727367..a74cd9889 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -1070,7 +1070,7 @@ Blockly.BlockSvg.prototype.updateDisabled = function() { this.updateColour(); } } - var children = this.getChildren(); + var children = this.getChildren(false); for (var i = 0, child; child = children[i]; i++) { child.updateDisabled(); } diff --git a/core/contextmenu.js b/core/contextmenu.js index 904dfe9c7..bcece8129 100644 --- a/core/contextmenu.js +++ b/core/contextmenu.js @@ -215,11 +215,11 @@ Blockly.ContextMenu.callbackFactory = function(block, xml) { Blockly.ContextMenu.blockDeleteOption = function(block) { // Option to delete this block but not blocks lower in the stack. // Count the number of blocks that are nested in this block. - var descendantCount = block.getDescendants(true).length; + var descendantCount = block.getDescendants(false).length; var nextBlock = block.getNextBlock(); if (nextBlock) { // Blocks in the current stack would survive this block's deletion. - descendantCount -= nextBlock.getDescendants(true).length; + descendantCount -= nextBlock.getDescendants(false).length; } var deleteOption = { text: descendantCount == 1 ? Blockly.Msg.DELETE_BLOCK : @@ -260,7 +260,8 @@ Blockly.ContextMenu.blockHelpOption = function(block) { */ Blockly.ContextMenu.blockDuplicateOption = function(block) { var enabled = true; - if (block.getDescendants().length > block.workspace.remainingCapacity()) { + if (block.getDescendants(false).length > + block.workspace.remainingCapacity()) { enabled = false; } var duplicateOption = { diff --git a/core/events/events.js b/core/events/events.js index b68dd778d..446e49153 100644 --- a/core/events/events.js +++ b/core/events/events.js @@ -287,7 +287,7 @@ Blockly.Events.setGroup = function(state) { */ Blockly.Events.getDescendantIds_ = function(block) { var ids = []; - var descendants = block.getDescendants(); + var descendants = block.getDescendants(false); for (var i = 0, descendant; descendant = descendants[i]; i++) { ids[i] = descendant.id; } @@ -350,7 +350,7 @@ Blockly.Events.disableOrphans = function(event) { var block = workspace.getBlockById(event.blockId); if (block) { if (block.getParent() && !block.getParent().disabled) { - var children = block.getDescendants(); + var children = block.getDescendants(false); for (var i = 0, child; child = children[i]; i++) { child.setDisabled(false); } diff --git a/core/flyout_base.js b/core/flyout_base.js index f27c6f541..8a3aeaaf6 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -717,7 +717,7 @@ Blockly.Flyout.prototype.filterForCapacity_ = function() { var blocks = this.workspace_.getTopBlocks(false); for (var i = 0, block; block = blocks[i]; i++) { if (this.permanentlyDisabled_.indexOf(block) == -1) { - var allBlocks = block.getDescendants(); + var allBlocks = block.getDescendants(false); block.setDisabled(allBlocks.length > remainingCapacity); } } diff --git a/core/flyout_horizontal.js b/core/flyout_horizontal.js index 9243c0ef7..8dd8032f6 100644 --- a/core/flyout_horizontal.js +++ b/core/flyout_horizontal.js @@ -261,7 +261,7 @@ Blockly.HorizontalFlyout.prototype.layout_ = function(contents, gaps) { for (var i = 0, item; item = contents[i]; i++) { if (item.type == 'block') { var block = item.block; - var allBlocks = block.getDescendants(); + var allBlocks = block.getDescendants(false); for (var j = 0, child; child = allBlocks[j]; j++) { // 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 diff --git a/core/flyout_vertical.js b/core/flyout_vertical.js index c95de2607..78057a643 100644 --- a/core/flyout_vertical.js +++ b/core/flyout_vertical.js @@ -247,7 +247,7 @@ Blockly.VerticalFlyout.prototype.layout_ = function(contents, gaps) { for (var i = 0, item; item = contents[i]; i++) { if (item.type == 'block') { var block = item.block; - var allBlocks = block.getDescendants(); + var allBlocks = block.getDescendants(false); for (var j = 0, child; child = allBlocks[j]; j++) { // 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 diff --git a/core/generator.js b/core/generator.js index 6ec0c7a32..3c8e397a9 100644 --- a/core/generator.js +++ b/core/generator.js @@ -142,7 +142,7 @@ Blockly.Generator.prototype.prefixLines = function(text, prefix) { */ Blockly.Generator.prototype.allNestedComments = function(block) { var comments = []; - var blocks = block.getDescendants(); + var blocks = block.getDescendants(true); for (var i = 0; i < blocks.length; i++) { var comment = blocks[i].getCommentText(); if (comment) { diff --git a/core/mutator.js b/core/mutator.js index 575da3137..1ffb1fd0b 100644 --- a/core/mutator.js +++ b/core/mutator.js @@ -258,7 +258,7 @@ Blockly.Mutator.prototype.setVisible = function(visible) { } 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++) { child.render(); } diff --git a/core/rendered_connection.js b/core/rendered_connection.js index cfd0473d1..c68dc490d 100644 --- a/core/rendered_connection.js +++ b/core/rendered_connection.js @@ -286,7 +286,7 @@ Blockly.RenderedConnection.prototype.setHidden = function(hidden) { Blockly.RenderedConnection.prototype.hideAll = function() { this.setHidden(true); if (this.targetConnection) { - var blocks = this.targetBlock().getDescendants(); + var blocks = this.targetBlock().getDescendants(false); for (var i = 0; i < blocks.length; i++) { var block = blocks[i]; // Hide all connections of all children. diff --git a/core/toolbox.js b/core/toolbox.js index 149b4e159..4ea684723 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -396,7 +396,7 @@ Blockly.Toolbox.prototype.syncTrees_ = function(treeIn, treeOut, pathToMedia) { */ Blockly.Toolbox.prototype.addColour_ = function(opt_tree) { var tree = opt_tree || this.tree_; - var children = tree.getChildren(); + var children = tree.getChildren(false); for (var i = 0, child; child = children[i]; i++) { var element = child.getRowElement(); if (element) { diff --git a/core/workspace.js b/core/workspace.js index 436cf135b..c9459fa69 100644 --- a/core/workspace.js +++ b/core/workspace.js @@ -171,13 +171,25 @@ Blockly.Workspace.prototype.getTopBlocks = 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.} Array of blocks. */ -Blockly.Workspace.prototype.getAllBlocks = function() { - var blocks = this.getTopBlocks(false); - for (var i = 0; i < blocks.length; i++) { - blocks.push.apply(blocks, blocks[i].getChildren()); +Blockly.Workspace.prototype.getAllBlocks = function(ordered) { + if (ordered) { + // Slow, but ordered. + 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; }; diff --git a/core/workspace_svg.js b/core/workspace_svg.js index c3e10c987..fe7273e95 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -1268,9 +1268,9 @@ Blockly.WorkspaceSvg.prototype.showContextMenu_ = function(e) { var deleteList = []; function addDeletableBlocks(block) { if (block.isDeletable()) { - deleteList = deleteList.concat(block.getDescendants()); + deleteList = deleteList.concat(block.getDescendants(false)); } else { - var children = block.getChildren(); + var children = block.getChildren(false); for (var i = 0; i < children.length; i++) { addDeletableBlocks(children[i]); } diff --git a/core/xml.js b/core/xml.js index ec6089075..9af982ca2 100644 --- a/core/xml.js +++ b/core/xml.js @@ -517,7 +517,7 @@ Blockly.Xml.domToBlock = function(xmlBlock, workspace) { try { var topBlock = Blockly.Xml.domToBlockHeadless_(xmlBlock, workspace); // Generate list of all blocks. - var blocks = topBlock.getDescendants(); + var blocks = topBlock.getDescendants(false); if (workspace.rendered) { // Hide connections to speed up assembly. topBlock.setConnectionsHidden(true); @@ -735,7 +735,7 @@ Blockly.Xml.domToBlockHeadless_ = function(xmlBlock, workspace) { } if (xmlBlock.nodeName.toLowerCase() == 'shadow') { // Ensure all children are also shadows. - var children = block.getChildren(); + var children = block.getChildren(false); for (var i = 0, child; child = children[i]; i++) { goog.asserts.assert( child.isShadow(), 'Shadow block not allowed non-shadow child.');