From fd916fdb9b791c1f4b5ed4e32043f18b1dc490b3 Mon Sep 17 00:00:00 2001 From: Sam El-Husseini Date: Tue, 19 May 2020 18:06:11 -0700 Subject: [PATCH] Bounding Box interface (#3906) * Add an interface describing a bounding box registered on the workspace * Clear the bounding box array * PR comments * Update chromedriver --- core/block_svg.js | 2 + core/connection.js | 2 +- core/flyout_base.js | 4 +- core/interfaces/i_bounding_box.js | 31 ++++++++++++ core/utils/toolbox.js | 2 +- core/variable_map.js | 2 +- core/variables.js | 2 +- core/workspace.js | 10 ++-- core/workspace_comment_svg.js | 2 + core/workspace_svg.js | 75 ++++++++++++++++++++++++++++-- scripts/gulpfiles/package_tasks.js | 4 +- tests/blocks/test_blocks.js | 2 +- tests/scripts/selenium-config.js | 2 +- 13 files changed, 122 insertions(+), 18 deletions(-) create mode 100644 core/interfaces/i_bounding_box.js diff --git a/core/block_svg.js b/core/block_svg.js index f359aeda3..cc8a5f6c8 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -32,6 +32,7 @@ goog.require('Blockly.utils.dom'); goog.require('Blockly.utils.object'); goog.require('Blockly.utils.Rect'); +goog.requireType('Blockly.IBoundingBox'); goog.requireType('Blockly.ICopyable'); /** @@ -43,6 +44,7 @@ goog.requireType('Blockly.ICopyable'); * @param {string=} opt_id Optional ID. Use this ID if provided, otherwise * create a new ID. * @extends {Blockly.Block} + * @implements {Blockly.IBoundingBox} * @implements {Blockly.ICopyable} * @constructor */ diff --git a/core/connection.js b/core/connection.js index 88d183157..1b309a4c4 100644 --- a/core/connection.js +++ b/core/connection.js @@ -639,7 +639,7 @@ Blockly.Connection.prototype.onCheckChanged_ = function() { /** * Change a connection's compatibility. - * @param {?(string|!Array)} check Compatible value type or list of + * @param {?(string|!Array.)} check Compatible value type or list of * value types. Null if all types are compatible. * @return {!Blockly.Connection} The connection being modified * (to allow chaining). diff --git a/core/flyout_base.js b/core/flyout_base.js index 13020a307..fe7659386 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -454,7 +454,7 @@ Blockly.Flyout.prototype.show = function(flyoutDef) { // Parse the Array or NodeList passed in into an Array of // Blockly.utils.toolbox.Toolbox. var parsedContent = Blockly.utils.toolbox.convertToolboxToJSON(flyoutDef); - var flyoutInfo = /** @type {{contents:Array, gaps:Array}} */ + var flyoutInfo = /** @type {{contents:Array., gaps:Array.}} */ (this.createFlyoutInfo_(parsedContent)); this.setVisible(true); @@ -495,7 +495,7 @@ Blockly.Flyout.prototype.show = function(flyoutDef) { * the flyout. * @param {Array.} parsedContent The array * of objects to show in the flyout. - * @return {{contents:Array, gaps:Array}} The list of contents + * @return {{contents:Array., gaps:Array.}} The list of contents * and gaps needed to lay out the flyout. * @private */ diff --git a/core/interfaces/i_bounding_box.js b/core/interfaces/i_bounding_box.js new file mode 100644 index 000000000..790aefbee --- /dev/null +++ b/core/interfaces/i_bounding_box.js @@ -0,0 +1,31 @@ +/** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview The interface for a bounding box. + * @author samelh@google.com (Sam El-Husseini) + */ + +'use strict'; + +goog.provide('Blockly.IBoundingBox'); + +goog.requireType('Blockly.utils.Rect'); + + +/** + * A bounding box interface. + * @interface + */ +Blockly.IBoundingBox = function() {}; + +/** + * Returns the coordinates of a bounding box describing the dimensions of this + * element. + * Coordinate system: workspace coordinates. + * @return {!Blockly.utils.Rect} Object with coordinates of the bounding box. + */ +Blockly.IBoundingBox.prototype.getBoundingRectangle; diff --git a/core/utils/toolbox.js b/core/utils/toolbox.js index c5e1fb71a..5509cac9a 100644 --- a/core/utils/toolbox.js +++ b/core/utils/toolbox.js @@ -104,7 +104,7 @@ Blockly.utils.toolbox.convertToolboxToJSON = function(toolboxDef) { /** * Convert the xml for a toolbox to JSON. - * @param {!NodeList|!Node|!Array} toolboxDef The + * @param {!NodeList|!Node|!Array.} toolboxDef The * definition of the toolbox in one of its many forms. * @return {!Array.} A list of objects in the * toolbox. diff --git a/core/variable_map.js b/core/variable_map.js index 4b6059ead..e2dcafaea 100644 --- a/core/variable_map.js +++ b/core/variable_map.js @@ -378,7 +378,7 @@ Blockly.VariableMap.prototype.getAllVariables = function() { /** * Returns all of the variable names of all types. - * @return {!Array} All of the variable names of all types. + * @return {!Array.} All of the variable names of all types. */ Blockly.VariableMap.prototype.getAllVariableNames = function() { var allNames = []; diff --git a/core/variables.js b/core/variables.js index 8af54d8ae..6aa9374d5 100644 --- a/core/variables.js +++ b/core/variables.js @@ -217,7 +217,7 @@ Blockly.Variables.generateUniqueName = function(workspace) { * will try to generate single letter names in the range a -> z (skip l). It * will start with the character passed to startChar. * @param {string} startChar The character to start the search at. - * @param {!Array} usedNames A list of all of the used names. + * @param {!Array.} usedNames A list of all of the used names. * @return {string} A unique name that is not present in the usedNames array. */ Blockly.Variables.generateUniqueNameFromOptions = function(startChar, usedNames) { diff --git a/core/workspace.js b/core/workspace.js index 28a0ab817..f16a2850e 100644 --- a/core/workspace.js +++ b/core/workspace.js @@ -167,7 +167,7 @@ Blockly.Workspace.prototype.sortObjects_ = function(a, b) { }; /** - * Add a block to the list of top blocks. + * Adds a block to the list of top blocks. * @param {!Blockly.Block} block Block to add. */ Blockly.Workspace.prototype.addTopBlock = function(block) { @@ -175,7 +175,7 @@ Blockly.Workspace.prototype.addTopBlock = function(block) { }; /** - * Remove a block from the list of top blocks. + * Removes a block from the list of top blocks. * @param {!Blockly.Block} block Block to remove. */ Blockly.Workspace.prototype.removeTopBlock = function(block) { @@ -251,7 +251,7 @@ Blockly.Workspace.prototype.getBlocksByType = function(type, ordered) { }; /** - * Add a comment to the list of top comments. + * Adds a comment to the list of top comments. * @param {!Blockly.WorkspaceComment} comment comment to add. * @package */ @@ -268,7 +268,7 @@ Blockly.Workspace.prototype.addTopComment = function(comment) { }; /** - * Remove a comment from the list of top comments. + * Removes a comment from the list of top comments. * @param {!Blockly.WorkspaceComment} comment comment to remove. * @package */ @@ -485,7 +485,7 @@ Blockly.Workspace.prototype.getAllVariables = function() { /** * Returns all variable names of all types. - * @return {!Array} List of all variable names of all types. + * @return {!Array.} List of all variable names of all types. */ Blockly.Workspace.prototype.getAllVariableNames = function() { return this.variableMap_.getAllVariableNames(); diff --git a/core/workspace_comment_svg.js b/core/workspace_comment_svg.js index b9fa05a4c..0926c73a5 100644 --- a/core/workspace_comment_svg.js +++ b/core/workspace_comment_svg.js @@ -25,6 +25,7 @@ goog.require('Blockly.utils.object'); goog.require('Blockly.utils.Rect'); goog.require('Blockly.WorkspaceComment'); +goog.requireType('Blockly.IBoundingBox'); goog.requireType('Blockly.ICopyable'); /** @@ -36,6 +37,7 @@ goog.requireType('Blockly.ICopyable'); * @param {string=} opt_id Optional ID. Use this ID if provided, otherwise * create a new ID. * @extends {Blockly.WorkspaceComment} + * @implements {Blockly.IBoundingBox} * @implements {Blockly.ICopyable} * @constructor */ diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 47b7445a5..4f4f87da2 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -39,6 +39,7 @@ goog.require('Blockly.WorkspaceDragSurfaceSvg'); goog.require('Blockly.Xml'); goog.requireType('Blockly.blockRendering.Renderer'); +goog.requireType('Blockly.IBoundingBox'); /** @@ -167,6 +168,13 @@ Blockly.WorkspaceSvg = function(options, * @type {boolean} */ this.keyboardAccessibilityMode = false; + + /** + * The list of top-level bounding boxes on the workspace. + * @type {!Array.} + * @private + */ + this.topBoundingBoxes_ = []; }; Blockly.utils.object.inherits(Blockly.WorkspaceSvg, Blockly.Workspace); @@ -1613,9 +1621,7 @@ Blockly.WorkspaceSvg.prototype.onMouseWheel_ = function(e) { * bounding box containing the blocks on the workspace. */ Blockly.WorkspaceSvg.prototype.getBlocksBoundingBox = function() { - var topBlocks = this.getTopBlocks(false); - var topComments = this.getTopComments(false); - var topElements = topBlocks.concat(topComments); + var topElements = this.getTopBoundingBoxes(); // There are no blocks, return empty rectangle. if (!topElements.length) { return new Blockly.utils.Rect(0, 0, 0, 0); @@ -2486,6 +2492,68 @@ Blockly.WorkspaceSvg.prototype.getTopBlocks = function(ordered) { return Blockly.WorkspaceSvg.superClass_.getTopBlocks.call(this, ordered); }; +/** + * Adds a block to the list of top blocks. + * @param {!Blockly.Block} block Block to add. + */ +Blockly.WorkspaceSvg.prototype.addTopBlock = function(block) { + this.addTopBoundingBox(/** @type {!Blockly.BlockSvg} */ (block)); + Blockly.WorkspaceSvg.superClass_.addTopBlock.call(this, block); +}; + +/** + * Removes a block from the list of top blocks. + * @param {!Blockly.Block} block Block to remove. + */ +Blockly.WorkspaceSvg.prototype.removeTopBlock = function(block) { + this.removeTopBoundingBox(/** @type {!Blockly.BlockSvg} */ (block)); + Blockly.WorkspaceSvg.superClass_.removeTopBlock.call(this, block); +}; + +/** + * Adds a comment to the list of top comments. + * @param {!Blockly.WorkspaceComment} comment comment to add. + */ +Blockly.WorkspaceSvg.prototype.addTopComment = function(comment) { + this.addTopBoundingBox( + /** @type {!Blockly.WorkspaceCommentSvg} */ (comment)); + Blockly.WorkspaceSvg.superClass_.addTopComment.call(this, comment); +}; + +/** + * Removes a comment from the list of top comments. + * @param {!Blockly.WorkspaceComment} comment comment to remove. + */ +Blockly.WorkspaceSvg.prototype.removeTopComment = function(comment) { + this.removeTopBoundingBox( + /** @type {!Blockly.WorkspaceCommentSvg} */ (comment)); + Blockly.WorkspaceSvg.superClass_.removeTopComment.call(this, comment); +}; + +/** + * Adds a bounding box to the list of top bounding boxes. + * @param {!Blockly.IBoundingBox} element Bounding box to add. + */ +Blockly.WorkspaceSvg.prototype.addTopBoundingBox = function(element) { + this.topBoundingBoxes_.push(element); +}; + +/** + * Removes a bounding box from the list of top bounding boxes. + * @param {!Blockly.IBoundingBox} element Bounding box to remove. + */ +Blockly.WorkspaceSvg.prototype.removeTopBoundingBox = function(element) { + Blockly.utils.arrayRemove(this.topBoundingBoxes_, element); +}; + +/** + * Finds the top-level bounding box and returns them. + * @return {!Array.} The top-level bounding boxes. + */ +Blockly.WorkspaceSvg.prototype.getTopBoundingBoxes = function() { + return [].concat(this.topBoundingBoxes_); +}; + /** * Update whether this workspace has resizes enabled. * If enabled, workspace will resize when appropriate. @@ -2508,6 +2576,7 @@ Blockly.WorkspaceSvg.prototype.setResizesEnabled = function(enabled) { Blockly.WorkspaceSvg.prototype.clear = function() { this.setResizesEnabled(false); Blockly.WorkspaceSvg.superClass_.clear.call(this); + this.topBoundingBoxes_ = []; this.setResizesEnabled(true); }; diff --git a/scripts/gulpfiles/package_tasks.js b/scripts/gulpfiles/package_tasks.js index 5aeb9fa2a..bafa63d1e 100644 --- a/scripts/gulpfiles/package_tasks.js +++ b/scripts/gulpfiles/package_tasks.js @@ -29,7 +29,7 @@ const packageDistribution = 'dist'; /** * A helper method for wrapping a file into a Universal Module Definition. * @param {string} namespace The export namespace. - * @param {Array} dependencies An array of dependencies to inject. + * @param {Array.} dependencies An array of dependencies to inject. */ function packageUMD(namespace, dependencies) { return gulp.umd({ @@ -43,7 +43,7 @@ function packageUMD(namespace, dependencies) { /** * A helper method for wrapping a file into a CommonJS module for Node.js. * @param {string} namespace The export namespace. - * @param {Array} dependencies An array of dependencies to inject. + * @param {Array.} dependencies An array of dependencies to inject. */ function packageCommonJS(namespace, dependencies) { return gulp.umd({ diff --git a/tests/blocks/test_blocks.js b/tests/blocks/test_blocks.js index 5cf7ec685..a82feaf76 100644 --- a/tests/blocks/test_blocks.js +++ b/tests/blocks/test_blocks.js @@ -1753,7 +1753,7 @@ Blockly.Blocks['test_dropdowns_dynamic'] = { /** * An array of options for the dynamic dropdown. - * @type {!Array} + * @type {!Array.} * @private */ Blockly.TestBlocks.dynamicDropdownOptions_ = []; diff --git a/tests/scripts/selenium-config.js b/tests/scripts/selenium-config.js index 84a4168c3..daf1a964d 100644 --- a/tests/scripts/selenium-config.js +++ b/tests/scripts/selenium-config.js @@ -9,7 +9,7 @@ module.exports = { chrome: { // check for more recent versions of chrome driver here: // https://chromedriver.storage.googleapis.com/index.html - version: '81.0.4044.20', + version: '83.0.4103.39', arch: process.arch, baseURL: 'https://chromedriver.storage.googleapis.com' },