From ef8a5a1fe166ae2d1b7287d60f0089cfcceefcf6 Mon Sep 17 00:00:00 2001 From: Monica Kozbial Date: Thu, 4 Mar 2021 16:51:06 -0800 Subject: [PATCH] Add PluginManager (#4672) * Add PluginManager. --- blockly_uncompressed.js | 7 +- core/interfaces/i_plugin.js | 22 +++++ core/interfaces/i_positionable.js | 12 +-- core/interfaces/i_workspace_plugin.js | 20 +++++ core/metrics_manager.js | 22 +++++ core/plugin_manager.js | 122 ++++++++++++++++++++++++++ core/trashcan.js | 21 ++--- core/workspace_svg.js | 57 +++++++----- core/zoom_controls.js | 19 ++-- 9 files changed, 247 insertions(+), 55 deletions(-) create mode 100644 core/interfaces/i_plugin.js create mode 100644 core/interfaces/i_workspace_plugin.js create mode 100644 core/plugin_manager.js diff --git a/blockly_uncompressed.js b/blockly_uncompressed.js index e5c54697e..8c7093638 100644 --- a/blockly_uncompressed.js +++ b/blockly_uncompressed.js @@ -95,13 +95,15 @@ goog.addDependency('../../core/interfaces/i_deletearea.js', ['Blockly.IDeleteAre goog.addDependency('../../core/interfaces/i_flyout.js', ['Blockly.IFlyout'], [], {}); goog.addDependency('../../core/interfaces/i_metrics_manager.js', ['Blockly.IMetricsManager'], [], {}); goog.addDependency('../../core/interfaces/i_movable.js', ['Blockly.IMovable'], [], {}); -goog.addDependency('../../core/interfaces/i_positionable.js', ['Blockly.IPositionable'], [], {}); +goog.addDependency('../../core/interfaces/i_plugin.js', ['Blockly.IPlugin'], [], {}); +goog.addDependency('../../core/interfaces/i_positionable.js', ['Blockly.IPositionable'], ['Blockly.IPlugin'], {}); goog.addDependency('../../core/interfaces/i_registrable.js', ['Blockly.IRegistrable'], [], {}); goog.addDependency('../../core/interfaces/i_registrable_field.js', ['Blockly.IRegistrableField'], [], {}); goog.addDependency('../../core/interfaces/i_selectable.js', ['Blockly.ISelectable'], [], {}); goog.addDependency('../../core/interfaces/i_styleable.js', ['Blockly.IStyleable'], [], {}); goog.addDependency('../../core/interfaces/i_toolbox.js', ['Blockly.IToolbox'], [], {}); goog.addDependency('../../core/interfaces/i_toolbox_item.js', ['Blockly.ICollapsibleToolboxItem', 'Blockly.ISelectableToolboxItem', 'Blockly.IToolboxItem'], [], {}); +goog.addDependency('../../core/interfaces/i_workspace_plugin.js', ['Blockly.IWorkspacePlugin'], [], {}); goog.addDependency('../../core/keyboard_nav/ast_node.js', ['Blockly.ASTNode'], ['Blockly.connectionTypes', 'Blockly.constants', 'Blockly.utils.Coordinate'], {'lang': 'es5'}); goog.addDependency('../../core/keyboard_nav/basic_cursor.js', ['Blockly.BasicCursor'], ['Blockly.ASTNode', 'Blockly.Cursor', 'Blockly.registry'], {'lang': 'es5'}); goog.addDependency('../../core/keyboard_nav/cursor.js', ['Blockly.Cursor'], ['Blockly.ASTNode', 'Blockly.Marker', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es5'}); @@ -115,6 +117,7 @@ goog.addDependency('../../core/msg.js', ['Blockly.Msg'], ['Blockly.utils.global' goog.addDependency('../../core/mutator.js', ['Blockly.Mutator'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.BubbleOpen', 'Blockly.Icon', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'Blockly.utils', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.global', 'Blockly.utils.object', 'Blockly.utils.toolbox', 'Blockly.utils.xml'], {}); goog.addDependency('../../core/names.js', ['Blockly.Names'], ['Blockly.Msg', 'Blockly.constants'], {}); goog.addDependency('../../core/options.js', ['Blockly.Options'], ['Blockly.Theme', 'Blockly.Themes.Classic', 'Blockly.Xml', 'Blockly.registry', 'Blockly.utils.IdGenerator', 'Blockly.utils.Metrics', 'Blockly.utils.toolbox', 'Blockly.utils.userAgent'], {}); +goog.addDependency('../../core/plugin_manager.js', ['Blockly.PluginManager'], [], {}); goog.addDependency('../../core/procedures.js', ['Blockly.Procedures'], ['Blockly.Blocks', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.Msg', 'Blockly.Names', 'Blockly.Workspace', 'Blockly.Xml', 'Blockly.constants', 'Blockly.utils.xml'], {}); goog.addDependency('../../core/registry.js', ['Blockly.registry'], [], {}); goog.addDependency('../../core/rendered_connection.js', ['Blockly.RenderedConnection'], ['Blockly.Connection', 'Blockly.Events', 'Blockly.connectionTypes', 'Blockly.constants', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.deprecation', 'Blockly.utils.dom', 'Blockly.utils.object'], {}); @@ -212,7 +215,7 @@ goog.addDependency('../../core/workspace_comment_render_svg.js', ['Blockly.Works goog.addDependency('../../core/workspace_comment_svg.js', ['Blockly.WorkspaceCommentSvg'], ['Blockly.Css', 'Blockly.Events', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove', 'Blockly.Events.Selected', 'Blockly.WorkspaceComment', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object'], {}); goog.addDependency('../../core/workspace_drag_surface_svg.js', ['Blockly.WorkspaceDragSurfaceSvg'], ['Blockly.utils', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {}); goog.addDependency('../../core/workspace_dragger.js', ['Blockly.WorkspaceDragger'], ['Blockly.utils.Coordinate'], {}); -goog.addDependency('../../core/workspace_svg.js', ['Blockly.WorkspaceSvg'], ['Blockly.BlockSvg', 'Blockly.ConnectionDB', 'Blockly.ContextMenu', 'Blockly.ContextMenuRegistry', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.ThemeChange', 'Blockly.Events.ViewportChange', 'Blockly.Gesture', 'Blockly.Grid', 'Blockly.MarkerManager', 'Blockly.MetricsManager', 'Blockly.Msg', 'Blockly.Options', 'Blockly.ThemeManager', 'Blockly.Themes.Classic', 'Blockly.TouchGesture', 'Blockly.Workspace', 'Blockly.WorkspaceAudio', 'Blockly.WorkspaceDragSurfaceSvg', 'Blockly.Xml', 'Blockly.blockRendering', 'Blockly.browserEvents', 'Blockly.constants', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Metrics', 'Blockly.utils.Rect', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {'lang': 'es5'}); +goog.addDependency('../../core/workspace_svg.js', ['Blockly.WorkspaceSvg'], ['Blockly.BlockSvg', 'Blockly.ConnectionDB', 'Blockly.ContextMenu', 'Blockly.ContextMenuRegistry', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.ThemeChange', 'Blockly.Events.ViewportChange', 'Blockly.Gesture', 'Blockly.Grid', 'Blockly.IPositionable', 'Blockly.MarkerManager', 'Blockly.MetricsManager', 'Blockly.Msg', 'Blockly.Options', 'Blockly.PluginManager', 'Blockly.ThemeManager', 'Blockly.Themes.Classic', 'Blockly.TouchGesture', 'Blockly.Workspace', 'Blockly.WorkspaceAudio', 'Blockly.WorkspaceDragSurfaceSvg', 'Blockly.Xml', 'Blockly.blockRendering', 'Blockly.browserEvents', 'Blockly.constants', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Metrics', 'Blockly.utils.Rect', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {'lang': 'es5'}); goog.addDependency('../../core/xml.js', ['Blockly.Xml'], ['Blockly.Events', 'Blockly.constants', 'Blockly.inputTypes', 'Blockly.utils', 'Blockly.utils.Size', 'Blockly.utils.dom', 'Blockly.utils.global', 'Blockly.utils.xml'], {}); goog.addDependency('../../core/zoom_controls.js', ['Blockly.ZoomControls'], ['Blockly.Css', 'Blockly.Events', 'Blockly.Events.Click', 'Blockly.IPositionable', 'Blockly.Scrollbar', 'Blockly.Touch', 'Blockly.browserEvents', 'Blockly.constants', 'Blockly.utils.Rect', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es5'}); goog.addDependency("base.js", [], []); diff --git a/core/interfaces/i_plugin.js b/core/interfaces/i_plugin.js new file mode 100644 index 000000000..4fc9b8c51 --- /dev/null +++ b/core/interfaces/i_plugin.js @@ -0,0 +1,22 @@ +/** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Interface for a plugin. + * @author kozbial@google.com (Monica Kozbial) + */ + +'use strict'; + +goog.provide('Blockly.IPlugin'); + + +/** + * The interface for a workspace plugin. + * @interface + */ +Blockly.IPlugin = function() {}; + diff --git a/core/interfaces/i_positionable.js b/core/interfaces/i_positionable.js index a48ff5a33..00be26398 100644 --- a/core/interfaces/i_positionable.js +++ b/core/interfaces/i_positionable.js @@ -13,21 +13,19 @@ goog.provide('Blockly.IPositionable'); +goog.require('Blockly.IPlugin'); + /** * Interface for a component that is positioned on top of the workspace. + * @extends {Blockly.IPlugin} * @interface */ Blockly.IPositionable = function() {}; /** * Positions the element. Called when the window is resized. - * @param {!Blockly.MetricsManager.ContainerRegion} viewMetrics The workspace - * viewMetrics. - * @param {!Blockly.MetricsManager.AbsoluteMetrics} absoluteMetrics The absolute - * metrics for the workspace. - * @param {!Blockly.MetricsManager.ToolboxMetrics} toolboxMetrics The toolbox - * metrics for the workspace. + * @param {!Blockly.MetricsManager.UiMetrics} metrics The workspace metrics. * @param {!Array} savedPositions List of rectangles that * are already on the workspace. */ @@ -39,5 +37,3 @@ Blockly.IPositionable.prototype.position; * @returns {!Blockly.utils.Rect} The plugin’s bounding box. */ Blockly.IPositionable.prototype.getBoundingRectangle; - - diff --git a/core/interfaces/i_workspace_plugin.js b/core/interfaces/i_workspace_plugin.js new file mode 100644 index 000000000..dea62c122 --- /dev/null +++ b/core/interfaces/i_workspace_plugin.js @@ -0,0 +1,20 @@ +/** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Interface for plugins that can be registered on the workspace. + * @author kozbial@google.com (Monica Kozbial) + */ + +'use strict'; + +goog.provide('Blockly.IWorkspacePlugin'); + +/** + * Base interface for a plugin that can be registered on the workspace. + * @interface + */ +Blockly.IWorkspacePlugin = function() {}; diff --git a/core/metrics_manager.js b/core/metrics_manager.js index 7f6b106e6..2ee7821e5 100644 --- a/core/metrics_manager.js +++ b/core/metrics_manager.js @@ -82,6 +82,16 @@ Blockly.MetricsManager.ContainerRegion; */ Blockly.MetricsManager.FixedEdges; +/** + * Common metrics used for ui elements. + * @typedef {{ + * viewMetrics: !Blockly.MetricsManager.ContainerRegion, + * absoluteMetrics: !Blockly.MetricsManager.AbsoluteMetrics, + * toolboxMetrics: !Blockly.MetricsManager.ToolboxMetrics + * }} + */ +Blockly.MetricsManager.UiMetrics; + /** * Gets the dimensions of the given workspace component, in pixel coordinates. * @param {?Blockly.IToolbox|?Blockly.IFlyout} elem The element to get the @@ -373,6 +383,18 @@ Blockly.MetricsManager.prototype.getScrollMetrics = function( }; }; +/** + * Returns common metrics used by ui elements. + * @return {!Blockly.MetricsManager.UiMetrics} The ui metrics. + */ +Blockly.MetricsManager.prototype.getUiMetrics = function() { + return { + viewMetrics: this.getViewMetrics(), + absoluteMetrics: this.getAbsoluteMetrics(), + toolboxMetrics: this.getToolboxMetrics() + }; +}; + /** * Returns an object with all the metrics required to size scrollbars for a * top level workspace. The following properties are computed: diff --git a/core/plugin_manager.js b/core/plugin_manager.js new file mode 100644 index 000000000..186d9c4b1 --- /dev/null +++ b/core/plugin_manager.js @@ -0,0 +1,122 @@ +/** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Manager for all items registered with the workspace. + * @author kozbial@google.com (Monica Kozbial) + */ + +'use strict'; + +goog.provide('Blockly.PluginManager'); + + +/** + * Manager for all items registered with the workspace. + * @constructor + */ +Blockly.PluginManager = function() { + /** + * A map of the plugins registered with the workspace, mapped to id. + * @type {!Object} + */ + this.pluginData_ = {}; + + /** + * A map of types to plugin ids. + * @type {!Object>} + */ + this.typeToPluginId_ = {}; +}; + +/** + * An object storing plugin information. + * @typedef {{ + * id: string, + * plugin: !Blockly.IPlugin, + * types: !Array>, + * weight: number + * }} + */ +Blockly.PluginManager.PluginData; + +/** + * Adds a plugin. + * @param {!Blockly.PluginManager.PluginData} pluginDataObject The plugin. + * @template T + */ +Blockly.PluginManager.prototype.addPlugin = function(pluginDataObject) { + this.pluginData_[pluginDataObject.id] = pluginDataObject; + for (var i = 0, type; (type = pluginDataObject.types[i]); i++) { + var typeKey = String(type).toLowerCase(); + if (this.typeToPluginId_[typeKey] === undefined) { + this.typeToPluginId_[typeKey] = [pluginDataObject.id]; + } else { + this.typeToPluginId_[typeKey].push(pluginDataObject.id); + } + } +}; + +/** + * Gets the plugin with the given id and the given type. + * @param {string} id The id of the plugin to get. + * @return {!Blockly.IPlugin|undefined} The plugin with the given name + * or undefined if not found. + */ +Blockly.PluginManager.prototype.getPlugin = function(id) { + return this.pluginData_[id] && this.pluginData_[id].plugin; +}; + +/** + * Gets all the plugins of the specified type. + * @param {!Blockly.PluginManager.Type} type The type of the plugin. + * @param {boolean} sorted Whether to return list ordered by weights. + * @return {!Array} The plugins that match the + * specified type. + * @template T + */ +Blockly.PluginManager.prototype.getPlugins = function(type, sorted) { + var plugins = []; + var typeKey = String(type).toLowerCase(); + var pluginIds = this.typeToPluginId_[typeKey]; + for (var i = 0, id; pluginIds && (id = pluginIds[i]); i++) { + plugins.push(this.pluginData_[id].plugin); + } + if (sorted) { + plugins.sort(function(a, b) { + return a.weight - b.weight; + }); + } + return plugins; +}; + +/** + * A name with the type of the element stored in the generic. + * @param {string} name The name of the plugin type. + * @constructor + * @template T + */ +Blockly.PluginManager.Type = function(name) { + /** + * @type {string} + * @private + */ + this.name_ = name; +}; + +/** + * Returns the name of the type. + * @return {string} The name. + * @override + */ +Blockly.PluginManager.Type.prototype.toString = function() { + return this.name_; +}; + +/** @type {!Blockly.PluginManager.Type} */ +Blockly.PluginManager.Type.POSITIONABLE = + new Blockly.PluginManager.Type('positionable'); + diff --git a/core/trashcan.js b/core/trashcan.js index 8c1a82d2e..fb86257af 100644 --- a/core/trashcan.js +++ b/core/trashcan.js @@ -434,26 +434,20 @@ Blockly.Trashcan.prototype.emptyContents = function() { * Position the trashcan. * It is positioned in the opposite corner to the corner the * categories/toolbox starts at. - * @param {!Blockly.MetricsManager.ContainerRegion} viewMetrics The workspace - * viewMetrics. - * @param {!Blockly.MetricsManager.AbsoluteMetrics} absoluteMetrics The absolute - * metrics for the workspace. - * @param {!Blockly.MetricsManager.ToolboxMetrics} toolboxMetrics The toolbox - * metrics for the workspace. + * @param {!Blockly.MetricsManager.UiMetrics} metrics The workspace metrics. * @param {!Array} savedPositions List of rectangles that * are already on the workspace. */ -Blockly.Trashcan.prototype.position = function( - viewMetrics, absoluteMetrics, toolboxMetrics, savedPositions) { +Blockly.Trashcan.prototype.position = function(metrics, savedPositions) { // Not yet initialized. if (!this.verticalSpacing_) { return; } - if (toolboxMetrics.position == Blockly.TOOLBOX_AT_LEFT || + if (metrics.toolboxMetrics.position == Blockly.TOOLBOX_AT_LEFT || (this.workspace_.horizontalLayout && !this.workspace_.RTL)) { // Toolbox starts in the left corner. - this.left_ = viewMetrics.width + absoluteMetrics.left - + this.left_ = metrics.viewMetrics.width + metrics.absoluteMetrics.left - this.WIDTH_ - this.MARGIN_SIDE_ - Blockly.Scrollbar.scrollbarThickness; } else { // Toolbox starts in the right corner. @@ -464,9 +458,10 @@ Blockly.Trashcan.prototype.position = function( // Upper corner placement var minTop = this.top_ = this.verticalSpacing_; // Bottom corner placement - var maxTop = viewMetrics.height + absoluteMetrics.top - height - - this.verticalSpacing_; - var placeBottom = toolboxMetrics.position !== Blockly.TOOLBOX_AT_BOTTOM; + var maxTop = metrics.viewMetrics.height + metrics.absoluteMetrics.top - + height - this.verticalSpacing_; + var placeBottom = + metrics.toolboxMetrics.position !== Blockly.TOOLBOX_AT_BOTTOM; this.top_ = placeBottom ? maxTop : minTop; // Check for collision and bump if needed. diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 94bd09fe4..8f5533e12 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -25,10 +25,12 @@ goog.require('Blockly.Events.ThemeChange'); goog.require('Blockly.Events.ViewportChange'); goog.require('Blockly.Gesture'); goog.require('Blockly.Grid'); +goog.require('Blockly.IPositionable'); goog.require('Blockly.MarkerManager'); goog.require('Blockly.MetricsManager'); goog.require('Blockly.Msg'); goog.require('Blockly.Options'); +goog.require('Blockly.PluginManager'); goog.require('Blockly.registry'); goog.require('Blockly.ThemeManager'); goog.require('Blockly.Themes.Classic'); @@ -55,7 +57,6 @@ goog.requireType('Blockly.IASTNodeLocationSvg'); goog.requireType('Blockly.IBoundedElement'); goog.requireType('Blockly.IFlyout'); goog.requireType('Blockly.IMetricsManager'); -goog.requireType('Blockly.IPositionable'); goog.requireType('Blockly.IToolbox'); goog.requireType('Blockly.Marker'); goog.requireType('Blockly.ScrollbarPair'); @@ -107,6 +108,12 @@ Blockly.WorkspaceSvg = function( this.setMetrics = options.setMetrics || Blockly.WorkspaceSvg.setTopLevelWorkspaceMetrics_; + /** + * @type {!Blockly.PluginManager} + * @private + */ + this.pluginManager_ = new Blockly.PluginManager(); + this.connectionDBList = Blockly.ConnectionDB.init(this.connectionChecker); if (opt_blockDragSurface) { @@ -511,6 +518,15 @@ Blockly.WorkspaceSvg.prototype.getMetricsManager = function() { return this.metricsManager_; }; +/** + * Gets the plugin manager for this workspace. + * @return {!Blockly.PluginManager} The plugin manager. + * @public + */ +Blockly.WorkspaceSvg.prototype.getPluginManager = function() { + return this.pluginManager_; +}; + /** * Add the cursor svg to this workspaces svg group. * @param {SVGElement} cursorSvg The svg root of the cursor to be added to the @@ -953,6 +969,12 @@ Blockly.WorkspaceSvg.prototype.addTrashcan = function() { this.trashcan = new Blockly.Trashcan(this); var svgTrashcan = this.trashcan.createDom(); this.svgGroup_.insertBefore(svgTrashcan, this.svgBlockCanvas_); + this.pluginManager_.addPlugin({ + id: 'trashcan', + plugin: this.trashcan, + weight: 0, + types: [Blockly.PluginManager.Type.POSITIONABLE] + }); }; /** @@ -967,6 +989,12 @@ Blockly.WorkspaceSvg.prototype.addZoomControls = function() { this.zoomControls_ = new Blockly.ZoomControls(this); var svgZoomControls = this.zoomControls_.createDom(); this.svgGroup_.appendChild(svgZoomControls); + this.pluginManager_.addPlugin({ + id: 'zoomControls', + plugin: this.zoomControls_, + weight: 0, + types: [Blockly.PluginManager.Type.POSITIONABLE] + }); }; /** @@ -1078,25 +1106,14 @@ Blockly.WorkspaceSvg.prototype.resize = function() { if (this.flyout_) { this.flyout_.position(); } - /** @type {Array} */ - var positionableEls = []; - if (this.trashcan) { - positionableEls.push(this.trashcan); - } - if (this.zoomControls_) { - positionableEls.push(this.zoomControls_); - } - if (positionableEls) { - var metricsManager = this.getMetricsManager(); - var viewMetrics = metricsManager.getViewMetrics(); - var absoluteMetrics = metricsManager.getAbsoluteMetrics(); - var toolboxMetrics = metricsManager.getToolboxMetrics(); - var savedPositions = []; - for (var i = 0, uiElement; (uiElement = positionableEls[i]); i++) { - uiElement.position( - viewMetrics, absoluteMetrics, toolboxMetrics, savedPositions); - savedPositions.push(uiElement.getBoundingRectangle()); - } + + var positionables = this.pluginManager_.getPlugins( + Blockly.PluginManager.Type.POSITIONABLE, true); + var metrics = this.getMetricsManager().getUiMetrics(); + var savedPositions = []; + for (var i = 0, positionable; (positionable = positionables[i]); i++) { + positionable.position(metrics, savedPositions); + savedPositions.push(positionable.getBoundingRectangle()); } if (this.scrollbar) { diff --git a/core/zoom_controls.js b/core/zoom_controls.js index 6479e24ec..88b066975 100644 --- a/core/zoom_controls.js +++ b/core/zoom_controls.js @@ -213,25 +213,19 @@ Blockly.ZoomControls.prototype.getBoundingRectangle = function() { * Position the zoom controls. * It is positioned in the opposite corner to the corner the * categories/toolbox starts at. - * @param {!Blockly.MetricsManager.ContainerRegion} viewMetrics The workspace - * viewMetrics. - * @param {!Blockly.MetricsManager.AbsoluteMetrics} absoluteMetrics The absolute - * metrics for the workspace. - * @param {!Blockly.MetricsManager.ToolboxMetrics} toolboxMetrics The toolbox - * metrics for the workspace. + * @param {!Blockly.MetricsManager.UiMetrics} metrics The workspace metrics. * @param {!Array} savedPositions List of rectangles that * are already on the workspace. */ -Blockly.ZoomControls.prototype.position = function( - viewMetrics, absoluteMetrics, toolboxMetrics, savedPositions) { +Blockly.ZoomControls.prototype.position = function(metrics, savedPositions) { // Not yet initialized. if (!this.verticalSpacing_) { return; } - if (toolboxMetrics.position == Blockly.TOOLBOX_AT_LEFT || + if (metrics.toolboxMetrics.position == Blockly.TOOLBOX_AT_LEFT || (this.workspace_.horizontalLayout && !this.workspace_.RTL)) { // Toolbox starts in the left corner. - this.left_ = viewMetrics.width + absoluteMetrics.left - + this.left_ = metrics.viewMetrics.width + metrics.absoluteMetrics.left - this.WIDTH_ - this.MARGIN_SIDE_ - Blockly.Scrollbar.scrollbarThickness; } else { // Toolbox starts in the right corner. @@ -241,9 +235,10 @@ Blockly.ZoomControls.prototype.position = function( // Upper corner placement var minTop = this.top_ = this.verticalSpacing_; // Bottom corner placement - var maxTop = viewMetrics.height + absoluteMetrics.top - + var maxTop = metrics.viewMetrics.height + metrics.absoluteMetrics.top - this.HEIGHT_ - this.verticalSpacing_; - var placeBottom = toolboxMetrics.position !== Blockly.TOOLBOX_AT_BOTTOM; + var placeBottom = + metrics.toolboxMetrics.position !== Blockly.TOOLBOX_AT_BOTTOM; this.top_ = placeBottom ? maxTop : minTop; if (placeBottom) { this.zoomInGroup_.setAttribute('transform', 'translate(0, 43)');