From e3babee1f345bf3d18773977079ac4f59a0aa527 Mon Sep 17 00:00:00 2001 From: Sam El-Husseini Date: Thu, 21 May 2020 15:03:17 -0700 Subject: [PATCH] Add Blockly.utils.Metrics @record (#3913) * Add Blockly.utils.Metrics --- blockly_uncompressed.js | 14 +- core/blockly.js | 9 +- core/bubble.js | 18 +-- core/flyout_base.js | 121 ++++++++++++----- core/flyout_dragger.js | 2 +- core/flyout_horizontal.js | 37 +++--- core/flyout_vertical.js | 37 +++--- core/gesture.js | 2 +- core/inject.js | 2 + core/mutator.js | 19 ++- core/options.js | 9 +- core/scrollbar.js | 117 +++++++++-------- core/utils/metrics.js | 124 ++++++++++++++++++ core/workspace_svg.js | 32 ++--- demos/minimap/minimap.js | 4 +- scripts/gulpfiles/typings.js | 3 +- typings/templates/blockly-interfaces.template | 35 +++-- 17 files changed, 398 insertions(+), 187 deletions(-) create mode 100644 core/utils/metrics.js diff --git a/blockly_uncompressed.js b/blockly_uncompressed.js index 04a368a0a..6ea018add 100644 --- a/blockly_uncompressed.js +++ b/blockly_uncompressed.js @@ -21,17 +21,16 @@ this.BLOCKLY_DIR = (function(root) { this.BLOCKLY_BOOT = function(root) { // Execute after Closure has loaded. -goog.addDependency('../../core/block.js', ['Blockly.Block'], ['Blockly.Blocks', 'Blockly.Connection', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.BlockCreate', 'Blockly.Events.BlockDelete', 'Blockly.Events.BlockMove', 'Blockly.Extensions', 'Blockly.Input', 'Blockly.Workspace', 'Blockly.fieldRegistry', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.object', 'Blockly.utils.string'], {}); +goog.addDependency('../../core/block.js', ['Blockly.Block'], ['Blockly.ASTNode', 'Blockly.Blocks', 'Blockly.Connection', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.BlockCreate', 'Blockly.Events.BlockDelete', 'Blockly.Events.BlockMove', 'Blockly.Extensions', 'Blockly.Input', 'Blockly.Workspace', 'Blockly.fieldRegistry', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.object', 'Blockly.utils.string'], {'lang': 'es5'}); goog.addDependency('../../core/block_animations.js', ['Blockly.blockAnimations'], ['Blockly.utils.dom'], {}); goog.addDependency('../../core/block_drag_surface.js', ['Blockly.BlockDragSurfaceSvg'], ['Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom'], {}); goog.addDependency('../../core/block_dragger.js', ['Blockly.BlockDragger'], ['Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Events.Ui', 'Blockly.InsertionMarkerManager', 'Blockly.blockAnimations', 'Blockly.utils.Coordinate', 'Blockly.utils.dom'], {}); goog.addDependency('../../core/block_events.js', ['Blockly.Events.BlockBase', 'Blockly.Events.BlockChange', 'Blockly.Events.BlockCreate', 'Blockly.Events.BlockDelete', 'Blockly.Events.BlockMove', 'Blockly.Events.Change', 'Blockly.Events.Create', 'Blockly.Events.Delete', 'Blockly.Events.Move'], ['Blockly.Events', 'Blockly.Events.Abstract', 'Blockly.utils.Coordinate', 'Blockly.utils.object', 'Blockly.utils.xml'], {}); goog.addDependency('../../core/block_svg.js', ['Blockly.BlockSvg'], ['Blockly.ASTNode', 'Blockly.Block', 'Blockly.ContextMenu', 'Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Events.Ui', 'Blockly.Msg', 'Blockly.RenderedConnection', 'Blockly.TabNavigateCursor', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.blockAnimations', 'Blockly.blockRendering.IPathObject', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.dom', 'Blockly.utils.object'], {}); -goog.addDependency('../../core/blockly.js', ['Blockly'], ['Blockly.Events', 'Blockly.Events.Ui', 'Blockly.Procedures', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.Variables', 'Blockly.WidgetDiv', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'Blockly.constants', 'Blockly.inject', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.colour'], {}); +goog.addDependency('../../core/blockly.js', ['Blockly'], ['Blockly.Events', 'Blockly.Events.Ui', 'Blockly.Procedures', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.Variables', 'Blockly.WidgetDiv', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'Blockly.constants', 'Blockly.inject', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.Size', 'Blockly.utils.colour'], {}); goog.addDependency('../../core/blocks.js', ['Blockly.Blocks'], [], {}); goog.addDependency('../../core/bubble.js', ['Blockly.Bubble'], ['Blockly.Scrollbar', 'Blockly.Touch', 'Blockly.Workspace', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.math', 'Blockly.utils.userAgent'], {}); goog.addDependency('../../core/bubble_dragger.js', ['Blockly.BubbleDragger'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.CommentMove', 'Blockly.utils', 'Blockly.utils.Coordinate'], {}); -goog.addDependency('../../core/category_registry.js', ['Blockly.categoryRegistry'], [], {}); goog.addDependency('../../core/comment.js', ['Blockly.Comment'], ['Blockly.Bubble', 'Blockly.Css', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.Ui', 'Blockly.Icon', 'Blockly.Warning', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {}); goog.addDependency('../../core/components/component.js', ['Blockly.Component', 'Blockly.Component.Error'], ['Blockly.utils.IdGenerator', 'Blockly.utils.dom', 'Blockly.utils.style'], {}); goog.addDependency('../../core/components/tree/basenode.js', ['Blockly.tree.BaseNode'], ['Blockly.Component', 'Blockly.utils.KeyCodes', 'Blockly.utils.aria', 'Blockly.utils.object', 'Blockly.utils.style'], {}); @@ -71,6 +70,7 @@ goog.addDependency('../../core/icon.js', ['Blockly.Icon'], ['Blockly.utils', 'Bl goog.addDependency('../../core/inject.js', ['Blockly.inject'], ['Blockly.BlockDragSurfaceSvg', 'Blockly.Component', 'Blockly.Css', 'Blockly.DropDownDiv', 'Blockly.Events', 'Blockly.Grid', 'Blockly.Msg', 'Blockly.Options', 'Blockly.ScrollbarPair', 'Blockly.Tooltip', 'Blockly.WorkspaceDragSurfaceSvg', 'Blockly.WorkspaceSvg', 'Blockly.user.keyMap', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.userAgent'], {}); goog.addDependency('../../core/input.js', ['Blockly.Input'], ['Blockly.Connection', 'Blockly.FieldLabel'], {}); goog.addDependency('../../core/insertion_marker_manager.js', ['Blockly.InsertionMarkerManager'], ['Blockly.Events', 'Blockly.blockAnimations'], {'lang': 'es5'}); +goog.addDependency('../../core/interfaces/i_bounded_element.js', ['Blockly.IBoundedElement'], [], {}); goog.addDependency('../../core/interfaces/i_copyable.js', ['Blockly.ICopyable'], ['Blockly.ISelectable'], {}); goog.addDependency('../../core/interfaces/i_deletable.js', ['Blockly.IDeletable'], [], {}); goog.addDependency('../../core/interfaces/i_movable.js', ['Blockly.IMovable'], [], {}); @@ -90,7 +90,7 @@ goog.addDependency('../../core/menuitem.js', ['Blockly.MenuItem'], ['Blockly.uti 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.Ui', 'Blockly.Icon', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.global', 'Blockly.utils.object', 'Blockly.utils.xml'], {}); goog.addDependency('../../core/names.js', ['Blockly.Names'], ['Blockly.Msg'], {}); -goog.addDependency('../../core/options.js', ['Blockly.Options'], ['Blockly.Theme', 'Blockly.Themes.Classic', 'Blockly.Xml', 'Blockly.user.keyMap', 'Blockly.utils.toolbox', 'Blockly.utils.userAgent'], {}); +goog.addDependency('../../core/options.js', ['Blockly.Options'], ['Blockly.Theme', 'Blockly.Themes.Classic', 'Blockly.Xml', 'Blockly.user.keyMap', 'Blockly.utils.Metrics', 'Blockly.utils.toolbox', 'Blockly.utils.userAgent'], {}); 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/rendered_connection.js', ['Blockly.RenderedConnection'], ['Blockly.Connection', 'Blockly.Events', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.object'], {}); goog.addDependency('../../core/renderers/common/block_rendering.js', ['Blockly.blockRendering'], ['Blockly.utils.object'], {}); @@ -132,7 +132,7 @@ goog.addDependency('../../core/renderers/zelos/measurables/rows.js', ['Blockly.z goog.addDependency('../../core/renderers/zelos/path_object.js', ['Blockly.zelos.PathObject'], ['Blockly.blockRendering.PathObject', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.zelos.ConstantProvider'], {}); goog.addDependency('../../core/renderers/zelos/renderer.js', ['Blockly.zelos.Renderer'], ['Blockly.InsertionMarkerManager', 'Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.utils.object', 'Blockly.zelos.ConstantProvider', 'Blockly.zelos.Drawer', 'Blockly.zelos.MarkerSvg', 'Blockly.zelos.PathObject', 'Blockly.zelos.RenderInfo'], {}); goog.addDependency('../../core/requires.js', ['Blockly.requires'], ['Blockly', 'Blockly.Comment', 'Blockly.FieldAngle', 'Blockly.FieldCheckbox', 'Blockly.FieldColour', 'Blockly.FieldDropdown', 'Blockly.FieldImage', 'Blockly.FieldLabelSerializable', 'Blockly.FieldMultilineInput', 'Blockly.FieldNumber', 'Blockly.FieldTextInput', 'Blockly.FieldVariable', 'Blockly.FlyoutButton', 'Blockly.Generator', 'Blockly.HorizontalFlyout', 'Blockly.Mutator', 'Blockly.Themes.Classic', 'Blockly.Themes.Dark', 'Blockly.Themes.Deuteranopia', 'Blockly.Themes.HighContrast', 'Blockly.Themes.Tritanopia', 'Blockly.Toolbox', 'Blockly.Trashcan', 'Blockly.VariablesDynamic', 'Blockly.VerticalFlyout', 'Blockly.Warning', 'Blockly.ZoomControls', 'Blockly.geras.Renderer', 'Blockly.thrasos.Renderer', 'Blockly.zelos.Renderer'], {}); -goog.addDependency('../../core/scrollbar.js', ['Blockly.Scrollbar', 'Blockly.ScrollbarPair'], ['Blockly.Touch', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom'], {}); +goog.addDependency('../../core/scrollbar.js', ['Blockly.Scrollbar', 'Blockly.ScrollbarPair'], ['Blockly.Touch', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Metrics', 'Blockly.utils.dom'], {}); goog.addDependency('../../core/theme.js', ['Blockly.Theme'], ['Blockly.utils', 'Blockly.utils.colour', 'Blockly.utils.object'], {}); goog.addDependency('../../core/theme/classic.js', ['Blockly.Themes.Classic'], ['Blockly.Theme'], {}); goog.addDependency('../../core/theme/dark.js', ['Blockly.Themes.Dark'], ['Blockly.Theme'], {}); @@ -143,7 +143,6 @@ goog.addDependency('../../core/theme/tritanopia.js', ['Blockly.Themes.Tritanopia goog.addDependency('../../core/theme/zelos.js', ['Blockly.Themes.Zelos'], ['Blockly.Theme'], {}); goog.addDependency('../../core/theme_manager.js', ['Blockly.ThemeManager'], ['Blockly.Theme'], {}); goog.addDependency('../../core/toolbox.js', ['Blockly.Toolbox'], ['Blockly.Css', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.Touch', 'Blockly.navigation', 'Blockly.tree.TreeControl', 'Blockly.tree.TreeNode', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.aria', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {}); -goog.addDependency('../../core/toolbox_category.js', ['Blockly.ToolboxCategory'], ['Blockly.Toolbox'], {}); goog.addDependency('../../core/tooltip.js', ['Blockly.Tooltip'], ['Blockly.utils.string'], {}); goog.addDependency('../../core/touch.js', ['Blockly.Touch'], ['Blockly.utils', 'Blockly.utils.global', 'Blockly.utils.string'], {}); goog.addDependency('../../core/touch_gesture.js', ['Blockly.TouchGesture'], ['Blockly.Gesture', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.object'], {}); @@ -158,6 +157,7 @@ goog.addDependency('../../core/utils/global.js', ['Blockly.utils.global'], [], { goog.addDependency('../../core/utils/idgenerator.js', ['Blockly.utils.IdGenerator'], [], {}); goog.addDependency('../../core/utils/keycodes.js', ['Blockly.utils.KeyCodes'], [], {}); goog.addDependency('../../core/utils/math.js', ['Blockly.utils.math'], [], {}); +goog.addDependency('../../core/utils/metrics.js', ['Blockly.utils.Metrics'], [], {}); goog.addDependency('../../core/utils/object.js', ['Blockly.utils.object'], [], {}); goog.addDependency('../../core/utils/rect.js', ['Blockly.utils.Rect'], [], {}); goog.addDependency('../../core/utils/size.js', ['Blockly.utils.Size'], [], {}); @@ -182,7 +182,7 @@ goog.addDependency('../../core/workspace_comment_svg.js', ['Blockly.WorkspaceCom goog.addDependency('../../core/workspace_drag_surface_svg.js', ['Blockly.WorkspaceDragSurfaceSvg'], ['Blockly.utils', 'Blockly.utils.dom'], {}); goog.addDependency('../../core/workspace_dragger.js', ['Blockly.WorkspaceDragger'], ['Blockly.utils.Coordinate'], {}); goog.addDependency('../../core/workspace_events.js', ['Blockly.Events.FinishedLoading'], ['Blockly.Events', 'Blockly.Events.Ui', 'Blockly.utils.object'], {'lang': 'es5'}); -goog.addDependency('../../core/workspace_svg.js', ['Blockly.WorkspaceSvg'], ['Blockly.BlockSvg', 'Blockly.ConnectionDB', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Gesture', 'Blockly.Grid', 'Blockly.MarkerManager', 'Blockly.Msg', 'Blockly.Options', 'Blockly.ThemeManager', 'Blockly.Themes.Classic', 'Blockly.TouchGesture', 'Blockly.Workspace', 'Blockly.WorkspaceAudio', 'Blockly.WorkspaceDragSurfaceSvg', 'Blockly.Xml', 'Blockly.blockRendering', 'Blockly.constants', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {}); +goog.addDependency('../../core/workspace_svg.js', ['Blockly.WorkspaceSvg'], ['Blockly.BlockSvg', 'Blockly.ConnectionDB', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Gesture', 'Blockly.Grid', 'Blockly.MarkerManager', 'Blockly.Msg', 'Blockly.Options', 'Blockly.ThemeManager', 'Blockly.Themes.Classic', 'Blockly.TouchGesture', 'Blockly.Workspace', 'Blockly.WorkspaceAudio', 'Blockly.WorkspaceDragSurfaceSvg', 'Blockly.Xml', 'Blockly.blockRendering', 'Blockly.constants', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Metrics', 'Blockly.utils.Rect', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {}); goog.addDependency('../../core/ws_comment_events.js', ['Blockly.Events.CommentBase', 'Blockly.Events.CommentChange', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove'], ['Blockly.Events', 'Blockly.Events.Abstract', 'Blockly.utils.Coordinate', 'Blockly.utils.object', 'Blockly.utils.xml'], {}); goog.addDependency('../../core/xml.js', ['Blockly.Xml'], ['Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.FinishedLoading', 'Blockly.Events.VarCreate', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.global', 'Blockly.utils.xml'], {}); goog.addDependency('../../core/zoom_controls.js', ['Blockly.ZoomControls'], ['Blockly.Css', 'Blockly.Scrollbar', 'Blockly.Touch', 'Blockly.utils.dom'], {'lang': 'es5'}); diff --git a/core/blockly.js b/core/blockly.js index edd13d71f..62bedf785 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -26,6 +26,7 @@ goog.require('Blockly.Tooltip'); goog.require('Blockly.Touch'); goog.require('Blockly.utils'); goog.require('Blockly.utils.colour'); +goog.require('Blockly.utils.Size'); goog.require('Blockly.Variables'); goog.require('Blockly.WidgetDiv'); goog.require('Blockly.WorkspaceSvg'); @@ -107,13 +108,11 @@ Blockly.EventData; /** * Returns the dimensions of the specified SVG image. * @param {!SVGElement} svg SVG image. - * @return {!Object} Contains width and height properties. + * @return {!Blockly.utils.Size} Contains width and height properties. */ Blockly.svgSize = function(svg) { - return { - width: svg.cachedWidth_, - height: svg.cachedHeight_ - }; + svg = /** @type {?} */ (svg); + return new Blockly.utils.Size(svg.cachedWidth_, svg.cachedHeight_); }; /** diff --git a/core/bubble.js b/core/bubble.js index 7916541bf..7208d8492 100644 --- a/core/bubble.js +++ b/core/bubble.js @@ -21,6 +21,8 @@ goog.require('Blockly.utils.math'); goog.require('Blockly.utils.userAgent'); goog.require('Blockly.Workspace'); +goog.requireType('Blockly.utils.Metrics'); + /** * Class for UI bubble. @@ -489,8 +491,8 @@ Blockly.Bubble.prototype.layoutBubble_ = function() { * workspace (what percentage of the bubble is visible). * @param {!{x: number, y: number}} relativeMin The position of the top-left * corner of the bubble relative to the anchor point. - * @param {!Object} metrics The metrics of the workspace the bubble will - * appear in. + * @param {!Blockly.utils.Metrics} metrics The metrics of the workspace the + * bubble will appear in. * @return {number} The percentage of the bubble that is visible. * @private */ @@ -535,10 +537,10 @@ Blockly.Bubble.prototype.getOverlap_ = function(relativeMin, metrics) { * Calculate what the optimal horizontal position of the top-left corner of the * bubble is (relative to the anchor point) so that the most area of the * bubble is shown. - * @param {!Object} metrics The metrics of the workspace the bubble will - * appear in. + * @param {!Blockly.utils.Metrics} metrics The metrics of the workspace the + * bubble will appear in. * @return {number} The optimal horizontal position of the top-left corner - * of the bubble. + * of the bubble. * @private */ Blockly.Bubble.prototype.getOptimalRelativeLeft_ = function(metrics) { @@ -593,10 +595,10 @@ Blockly.Bubble.prototype.getOptimalRelativeLeft_ = function(metrics) { * Calculate what the optimal vertical position of the top-left corner of * the bubble is (relative to the anchor point) so that the most area of the * bubble is shown. - * @param {!Object} metrics The metrics of the workspace the bubble will - * appear in. + * @param {!Blockly.utils.Metrics} metrics The metrics of the workspace the + * bubble will appear in. * @return {number} The optimal vertical position of the top-left corner - * of the bubble. + * of the bubble. * @private */ Blockly.Bubble.prototype.getOptimalRelativeTop_ = function(metrics) { diff --git a/core/flyout_base.js b/core/flyout_base.js index 5d1bd2b23..c38fc5ef5 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -30,6 +30,7 @@ goog.require('Blockly.WorkspaceSvg'); goog.require('Blockly.Xml'); goog.requireType('Blockly.IBlocklyActionable'); +goog.requireType('Blockly.utils.Metrics'); /** @@ -37,10 +38,13 @@ goog.requireType('Blockly.IBlocklyActionable'); * @param {!Blockly.Options} workspaceOptions Dictionary of options for the * workspace. * @constructor + * @abstract * @implements {Blockly.IBlocklyActionable} */ Blockly.Flyout = function(workspaceOptions) { - workspaceOptions.getMetrics = this.getMetrics_.bind(this); + workspaceOptions.getMetrics = + /** @type {function():!Blockly.utils.Metrics} */ ( + this.getMetrics_.bind(this)); workspaceOptions.setMetrics = this.setMetrics_.bind(this); /** @@ -108,6 +112,13 @@ Blockly.Flyout = function(workspaceOptions) { * @const */ this.tabWidth_ = this.workspace_.getRenderer().getConstants().TAB_WIDTH; + + /** + * The target workspace + * @type {?Blockly.WorkspaceSvg} + * @package + */ + this.targetWorkspace = null; }; /** @@ -235,11 +246,14 @@ Blockly.Flyout.prototype.createDom = function(tagName) { * create new blocks. */ Blockly.Flyout.prototype.init = function(targetWorkspace) { - this.targetWorkspace_ = targetWorkspace; + this.targetWorkspace = targetWorkspace; this.workspace_.targetWorkspace = targetWorkspace; - // Add scrollbar. - this.scrollbar_ = new Blockly.Scrollbar(this.workspace_, + /** + * @type {!Blockly.Scrollbar} + * @package + */ + this.scrollbar = new Blockly.Scrollbar(this.workspace_, this.horizontalLayout_, false, 'blocklyFlyoutScrollbar'); this.hide(); @@ -248,7 +262,7 @@ Blockly.Flyout.prototype.init = function(targetWorkspace) { Blockly.bindEventWithChecks_(this.svgGroup_, 'wheel', this, this.wheel_)); if (!this.autoClose) { this.filterWrapper_ = this.filterForCapacity_.bind(this); - this.targetWorkspace_.addChangeListener(this.filterWrapper_); + this.targetWorkspace.addChangeListener(this.filterWrapper_); } // Dragging the flyout up and down. @@ -258,10 +272,10 @@ Blockly.Flyout.prototype.init = function(targetWorkspace) { // A flyout connected to a workspace doesn't have its own current gesture. this.workspace_.getGesture = - this.targetWorkspace_.getGesture.bind(this.targetWorkspace_); + this.targetWorkspace.getGesture.bind(this.targetWorkspace); // Get variables from the main workspace rather than the target workspace. - this.workspace_.setVariableMap(this.targetWorkspace_.getVariableMap()); + this.workspace_.setVariableMap(this.targetWorkspace.getVariableMap()); this.workspace_.createPotentialVariableMap(); }; @@ -275,12 +289,12 @@ Blockly.Flyout.prototype.dispose = function() { this.hide(); Blockly.unbindEvent_(this.eventWrappers_); if (this.filterWrapper_) { - this.targetWorkspace_.removeChangeListener(this.filterWrapper_); + this.targetWorkspace.removeChangeListener(this.filterWrapper_); this.filterWrapper_ = null; } - if (this.scrollbar_) { - this.scrollbar_.dispose(); - this.scrollbar_ = null; + if (this.scrollbar) { + this.scrollbar.dispose(); + this.scrollbar = null; } if (this.workspace_) { this.workspace_.getThemeManager().unsubscribe(this.svgBackground_); @@ -293,7 +307,7 @@ Blockly.Flyout.prototype.dispose = function() { this.svgGroup_ = null; } this.svgBackground_ = null; - this.targetWorkspace_ = null; + this.targetWorkspace = null; }; /** @@ -370,7 +384,7 @@ Blockly.Flyout.prototype.updateDisplay_ = function() { this.svgGroup_.style.display = show ? 'block' : 'none'; // Update the scrollbar's visibility too since it should mimic the // flyout's visibility. - this.scrollbar_.setContainerVisible(show); + this.scrollbar.setContainerVisible(show); }; /** @@ -395,14 +409,14 @@ Blockly.Flyout.prototype.positionAt_ = function(width, height, x, y) { } // Update the scrollbar (if one exists). - if (this.scrollbar_) { + if (this.scrollbar) { // Set the scrollbars origin to be the top left of the flyout. - this.scrollbar_.setOrigin(x, y); - this.scrollbar_.resize(); + this.scrollbar.setOrigin(x, y); + this.scrollbar.resize(); // Set the position again so that if the metrics were the same (and the // resize failed) our position is still updated. - this.scrollbar_.setPosition_( - this.scrollbar_.position_.x, this.scrollbar_.position_.y); + this.scrollbar.setPosition( + this.scrollbar.position.x, this.scrollbar.position.y); } }; @@ -556,7 +570,8 @@ Blockly.Flyout.prototype.createButton_ = function(btnInfo, isLabel) { throw Error('Missing require for Blockly.FlyoutButton'); } var curButton = new Blockly.FlyoutButton(this.workspace_, - this.targetWorkspace_, btnInfo, isLabel); + /** @type {!Blockly.WorkspaceSvg} */ (this.targetWorkspace), btnInfo, + isLabel); return curButton; }; @@ -564,11 +579,12 @@ Blockly.Flyout.prototype.createButton_ = function(btnInfo, isLabel) { * Create a block from the xml and permanently disable any blocks that were * defined as disabled. * @param {!Element} blockXml The xml of the block. - * @return {!Blockly.Block} The block created from the blockXml. + * @return {!Blockly.BlockSvg} The block created from the blockXml. * @private */ Blockly.Flyout.prototype.createBlock_ = function(blockXml) { - var curBlock = Blockly.Xml.domToBlock(blockXml, this.workspace_); + var curBlock = /** @type {!Blockly.BlockSvg} */ ( + Blockly.Xml.domToBlock(blockXml, this.workspace_)); if (!curBlock.isEnabled()) { // Record blocks that were initially disabled. // Do not enable these blocks as a result of capacity filtering. @@ -651,7 +667,7 @@ Blockly.Flyout.prototype.clearOldBlocks_ = function() { /** * Add listeners to a block that has been added to the flyout. * @param {!SVGElement} root The root node of the SVG group the block is in. - * @param {!Blockly.Block} block The block to add listeners for. + * @param {!Blockly.BlockSvg} block The block to add listeners for. * @param {!SVGElement} rect The invisible rectangle under the block that acts * as a mat for that block. * @protected @@ -673,14 +689,14 @@ Blockly.Flyout.prototype.addBlockListeners_ = function(root, block, rect) { /** * Handle a mouse-down on an SVG block in a non-closing flyout. - * @param {!Blockly.Block} block The flyout block to copy. + * @param {!Blockly.BlockSvg} block The flyout block to copy. * @return {!Function} Function to call when block is clicked. * @private */ Blockly.Flyout.prototype.blockMouseDown_ = function(block) { var flyout = this; return function(e) { - var gesture = flyout.targetWorkspace_.getGesture(e); + var gesture = flyout.targetWorkspace.getGesture(e); if (gesture) { gesture.setStartBlock(block); gesture.handleFlyoutStart(e, flyout); @@ -694,7 +710,7 @@ Blockly.Flyout.prototype.blockMouseDown_ = function(block) { * @private */ Blockly.Flyout.prototype.onMouseDown_ = function(e) { - var gesture = this.targetWorkspace_.getGesture(e); + var gesture = this.targetWorkspace.getGesture(e); if (gesture) { gesture.handleFlyoutStart(e, this); } @@ -722,8 +738,8 @@ Blockly.Flyout.prototype.isBlockCreatable_ = function(block) { Blockly.Flyout.prototype.createBlock = function(originalBlock) { var newBlock = null; Blockly.Events.disable(); - var variablesBeforeCreation = this.targetWorkspace_.getAllVariables(); - this.targetWorkspace_.setResizesEnabled(false); + var variablesBeforeCreation = this.targetWorkspace.getAllVariables(); + this.targetWorkspace.setResizesEnabled(false); try { newBlock = this.placeNewBlock_(originalBlock); // Close the flyout. @@ -732,7 +748,7 @@ Blockly.Flyout.prototype.createBlock = function(originalBlock) { Blockly.Events.enable(); } - var newVariables = Blockly.Variables.getAddedVariables(this.targetWorkspace_, + var newVariables = Blockly.Variables.getAddedVariables(this.targetWorkspace, variablesBeforeCreation); if (Blockly.Events.isEnabled()) { @@ -775,7 +791,7 @@ Blockly.Flyout.prototype.initFlyoutButton_ = function(button, x, y) { /** * Create and place a rectangle corresponding to the given block. - * @param {!Blockly.Block} block The block to associate the rect to. + * @param {!Blockly.BlockSvg} block The block to associate the rect to. * @param {number} x The x position of the cursor during this layout pass. * @param {number} y The y position of the cursor during this layout pass. * @param {!{height: number, width: number}} blockHW The height and width of the @@ -834,7 +850,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 enable = this.targetWorkspace_ + var enable = this.targetWorkspace .isCapacityAvailable(Blockly.utils.getBlockTypeCounts(block)); while (block) { block.setEnabled(enable); @@ -863,7 +879,7 @@ Blockly.Flyout.prototype.reflow = function() { * @package */ Blockly.Flyout.prototype.isScrollable = function() { - return this.scrollbar_ ? this.scrollbar_.isVisible() : false; + return this.scrollbar ? this.scrollbar.isVisible() : false; }; /** @@ -873,7 +889,7 @@ Blockly.Flyout.prototype.isScrollable = function() { * @private */ Blockly.Flyout.prototype.placeNewBlock_ = function(oldBlock) { - var targetWorkspace = this.targetWorkspace_; + var targetWorkspace = this.targetWorkspace; var svgRootOld = oldBlock.getSvgRoot(); if (!svgRootOld) { throw Error('oldBlock is not rendered.'); @@ -935,3 +951,44 @@ Blockly.Flyout.prototype.onBlocklyAction = function(action) { var cursor = this.workspace_.getCursor(); return cursor.onBlocklyAction(action); }; + +/** + * Return the deletion rectangle for this flyout in viewport coordinates. + * @return {Blockly.utils.Rect} Rectangle in which to delete. + */ +Blockly.Flyout.prototype.getClientRect; + +/** + * Position the flyout. + * @return {void} + */ +Blockly.Flyout.prototype.position; + +/** + * Determine if a drag delta is toward the workspace, based on the position + * and orientation of the flyout. This is used in determineDragIntention_ to + * determine if a new block should be created or if the flyout should scroll. + * @param {!Blockly.utils.Coordinate} currentDragDeltaXY How far the pointer has + * moved from the position at mouse down, in pixel units. + * @return {boolean} True if the drag is toward the workspace. + * @package + */ +Blockly.Flyout.prototype.isDragTowardWorkspace; + +/** + * Return an object with all the metrics required to size scrollbars for the + * flyout. + * @return {Blockly.utils.Metrics} Contains size and position metrics of the + * flyout. + * @protected + */ +Blockly.Flyout.prototype.getMetrics_; + +/** + * Sets the translation of the flyout to match the scrollbars. + * @param {!{x:number,y:number}} xyRatio Contains a y property which is a float + * between 0 and 1 specifying the degree of scrolling and a + * similar x property. + * @protected + */ +Blockly.Flyout.prototype.setMetrics_; diff --git a/core/flyout_dragger.js b/core/flyout_dragger.js index 3b0828e8c..4709bd37d 100644 --- a/core/flyout_dragger.js +++ b/core/flyout_dragger.js @@ -37,7 +37,7 @@ Blockly.FlyoutDragger = function(flyout) { * @type {!Blockly.Scrollbar} * @private */ - this.scrollbar_ = flyout.scrollbar_; + this.scrollbar_ = flyout.scrollbar; /** * Whether the flyout scrolls horizontally. If false, the flyout scrolls diff --git a/core/flyout_horizontal.js b/core/flyout_horizontal.js index ec757a26b..b7a5725bb 100644 --- a/core/flyout_horizontal.js +++ b/core/flyout_horizontal.js @@ -20,6 +20,8 @@ goog.require('Blockly.utils.object'); goog.require('Blockly.utils.Rect'); goog.require('Blockly.WidgetDiv'); +goog.requireType('Blockly.utils.Metrics'); + /** * Class for a flyout. @@ -29,10 +31,6 @@ goog.require('Blockly.WidgetDiv'); * @constructor */ Blockly.HorizontalFlyout = function(workspaceOptions) { - workspaceOptions.getMetrics = /** @type {function():!Object} */ ( - this.getMetrics_.bind(this)); - workspaceOptions.setMetrics = this.setMetrics_.bind(this); - Blockly.HorizontalFlyout.superClass_.constructor.call(this, workspaceOptions); /** * Flyout should be laid out horizontally. @@ -56,8 +54,9 @@ Blockly.utils.object.inherits(Blockly.HorizontalFlyout, Blockly.Flyout); * .viewLeft: Offset of the left edge of visible rectangle from parent, * .contentLeft: Offset of the left-most content from the x=0 coordinate, * .absoluteLeft: Left-edge of view. - * @return {Object} Contains size and position metrics of the flyout. - * @private + * @return {Blockly.utils.Metrics} Contains size and position metrics of the + * flyout. + * @protected */ Blockly.HorizontalFlyout.prototype.getMetrics_ = function() { if (!this.isVisible()) { @@ -84,14 +83,16 @@ Blockly.HorizontalFlyout.prototype.getMetrics_ = function() { var viewWidth = this.width_ - 2 * this.SCROLLBAR_PADDING; var metrics = { - viewHeight: viewHeight, - viewWidth: viewWidth, contentHeight: (optionBox.height + 2 * this.MARGIN) * this.workspace_.scale, contentWidth: (optionBox.width + 2 * this.MARGIN) * this.workspace_.scale, - viewTop: -this.workspace_.scrollY, - viewLeft: -this.workspace_.scrollX, contentTop: 0, contentLeft: 0, + + viewHeight: viewHeight, + viewWidth: viewWidth, + viewTop: -this.workspace_.scrollY, + viewLeft: -this.workspace_.scrollX, + absoluteTop: absoluteTop, absoluteLeft: absoluteLeft }; @@ -100,10 +101,10 @@ Blockly.HorizontalFlyout.prototype.getMetrics_ = function() { /** * Sets the translation of the flyout to match the scrollbars. - * @param {!Object} xyRatio Contains a y property which is a float + * @param {!{x:number,y:number}} xyRatio Contains a y property which is a float * between 0 and 1 specifying the degree of scrolling and a * similar x property. - * @private + * @protected */ Blockly.HorizontalFlyout.prototype.setMetrics_ = function(xyRatio) { var metrics = this.getMetrics_(); @@ -127,7 +128,7 @@ Blockly.HorizontalFlyout.prototype.position = function() { if (!this.isVisible()) { return; } - var targetWorkspaceMetrics = this.targetWorkspace_.getMetrics(); + var targetWorkspaceMetrics = this.targetWorkspace.getMetrics(); if (!targetWorkspaceMetrics) { // Hidden components will return null. return; @@ -142,7 +143,7 @@ Blockly.HorizontalFlyout.prototype.position = function() { // X is always 0 since this is a horizontal flyout. var x = 0; // If this flyout is the toolbox flyout. - if (this.targetWorkspace_.toolboxPosition == this.toolboxPosition_) { + if (this.targetWorkspace.toolboxPosition == this.toolboxPosition_) { // If there is a toolbox. if (targetWorkspaceMetrics.toolboxHeight) { if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP) { @@ -220,7 +221,7 @@ Blockly.HorizontalFlyout.prototype.setBackgroundPath_ = function(width, * Scroll the flyout to the top. */ Blockly.HorizontalFlyout.prototype.scrollToStart = function() { - this.scrollbar_.set(this.RTL ? Infinity : 0); + this.scrollbar.set(this.RTL ? Infinity : 0); }; /** @@ -238,7 +239,7 @@ Blockly.HorizontalFlyout.prototype.wheel_ = function(e) { var limit = metrics.contentWidth - metrics.viewWidth; pos = Math.min(pos, limit); pos = Math.max(pos, 0); - this.scrollbar_.set(pos); + this.scrollbar.set(pos); // When the flyout moves from a wheel event, hide WidgetDiv and DropDownDiv. Blockly.WidgetDiv.hide(); Blockly.DropDownDiv.hideWithoutAnimation(); @@ -257,7 +258,7 @@ Blockly.HorizontalFlyout.prototype.wheel_ = function(e) { * @private */ Blockly.HorizontalFlyout.prototype.layout_ = function(contents, gaps) { - this.workspace_.scale = this.targetWorkspace_.scale; + this.workspace_.scale = this.targetWorkspace.scale; var margin = this.MARGIN; var cursorX = margin + this.tabWidth_; var cursorY = margin; @@ -354,7 +355,7 @@ Blockly.HorizontalFlyout.prototype.getClientRect = function() { * @private */ Blockly.HorizontalFlyout.prototype.reflowInternal_ = function() { - this.workspace_.scale = this.targetWorkspace_.scale; + this.workspace_.scale = this.targetWorkspace.scale; var flyoutHeight = 0; var blocks = this.workspace_.getTopBlocks(false); for (var i = 0, block; (block = blocks[i]); i++) { diff --git a/core/flyout_vertical.js b/core/flyout_vertical.js index b589158cd..cdafba161 100644 --- a/core/flyout_vertical.js +++ b/core/flyout_vertical.js @@ -21,6 +21,8 @@ goog.require('Blockly.utils.Rect'); goog.require('Blockly.utils.userAgent'); goog.require('Blockly.WidgetDiv'); +goog.requireType('Blockly.utils.Metrics'); + /** * Class for a flyout. @@ -30,10 +32,6 @@ goog.require('Blockly.WidgetDiv'); * @constructor */ Blockly.VerticalFlyout = function(workspaceOptions) { - workspaceOptions.getMetrics = /** @type {function():!Object} */ ( - this.getMetrics_.bind(this)); - workspaceOptions.setMetrics = this.setMetrics_.bind(this); - Blockly.VerticalFlyout.superClass_.constructor.call(this, workspaceOptions); /** * Flyout should be laid out vertically. @@ -57,8 +55,9 @@ Blockly.utils.object.inherits(Blockly.VerticalFlyout, Blockly.Flyout); * .viewLeft: Offset of the left edge of visible rectangle from parent, * .contentLeft: Offset of the left-most content from the x=0 coordinate, * .absoluteLeft: Left-edge of view. - * @return {Object} Contains size and position metrics of the flyout. - * @private + * @return {Blockly.utils.Metrics} Contains size and position metrics of the + * flyout. + * @protected */ Blockly.VerticalFlyout.prototype.getMetrics_ = function() { if (!this.isVisible()) { @@ -84,14 +83,16 @@ Blockly.VerticalFlyout.prototype.getMetrics_ = function() { } var metrics = { - viewHeight: viewHeight, - viewWidth: viewWidth, contentHeight: optionBox.height * this.workspace_.scale + 2 * this.MARGIN, contentWidth: optionBox.width * this.workspace_.scale + 2 * this.MARGIN, - viewTop: -this.workspace_.scrollY + optionBox.y, - viewLeft: -this.workspace_.scrollX, contentTop: optionBox.y, contentLeft: optionBox.x, + + viewHeight: viewHeight, + viewWidth: viewWidth, + viewTop: -this.workspace_.scrollY + optionBox.y, + viewLeft: -this.workspace_.scrollX, + absoluteTop: absoluteTop, absoluteLeft: absoluteLeft }; @@ -100,10 +101,10 @@ Blockly.VerticalFlyout.prototype.getMetrics_ = function() { /** * Sets the translation of the flyout to match the scrollbars. - * @param {!Object} xyRatio Contains a y property which is a float + * @param {!{x:number,y:number}} xyRatio Contains a y property which is a float * between 0 and 1 specifying the degree of scrolling and a * similar x property. - * @private + * @protected */ Blockly.VerticalFlyout.prototype.setMetrics_ = function(xyRatio) { var metrics = this.getMetrics_(); @@ -125,7 +126,7 @@ Blockly.VerticalFlyout.prototype.position = function() { if (!this.isVisible()) { return; } - var targetWorkspaceMetrics = this.targetWorkspace_.getMetrics(); + var targetWorkspaceMetrics = this.targetWorkspace.getMetrics(); if (!targetWorkspaceMetrics) { // Hidden components will return null. return; @@ -140,7 +141,7 @@ Blockly.VerticalFlyout.prototype.position = function() { // Y is always 0 since this is a vertical flyout. var y = 0; // If this flyout is the toolbox flyout. - if (this.targetWorkspace_.toolboxPosition == this.toolboxPosition_) { + if (this.targetWorkspace.toolboxPosition == this.toolboxPosition_) { // If there is a category toolbox. if (targetWorkspaceMetrics.toolboxWidth) { if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) { @@ -208,7 +209,7 @@ Blockly.VerticalFlyout.prototype.setBackgroundPath_ = function(width, height) { * Scroll the flyout to the top. */ Blockly.VerticalFlyout.prototype.scrollToStart = function() { - this.scrollbar_.set(0); + this.scrollbar.set(0); }; /** @@ -225,7 +226,7 @@ Blockly.VerticalFlyout.prototype.wheel_ = function(e) { var limit = metrics.contentHeight - metrics.viewHeight; pos = Math.min(pos, limit); pos = Math.max(pos, 0); - this.scrollbar_.set(pos); + this.scrollbar.set(pos); // When the flyout moves from a wheel event, hide WidgetDiv and DropDownDiv. Blockly.WidgetDiv.hide(); Blockly.DropDownDiv.hideWithoutAnimation(); @@ -244,7 +245,7 @@ Blockly.VerticalFlyout.prototype.wheel_ = function(e) { * @private */ Blockly.VerticalFlyout.prototype.layout_ = function(contents, gaps) { - this.workspace_.scale = this.targetWorkspace_.scale; + this.workspace_.scale = this.targetWorkspace.scale; var margin = this.MARGIN; var cursorX = this.RTL ? margin : margin + this.tabWidth_; var cursorY = margin; @@ -333,7 +334,7 @@ Blockly.VerticalFlyout.prototype.getClientRect = function() { * @private */ Blockly.VerticalFlyout.prototype.reflowInternal_ = function() { - this.workspace_.scale = this.targetWorkspace_.scale; + this.workspace_.scale = this.targetWorkspace.scale; var flyoutWidth = 0; var blocks = this.workspace_.getTopBlocks(false); for (var i = 0, block; (block = blocks[i]); i++) { diff --git a/core/gesture.js b/core/gesture.js index e7f99102f..bf9a68d15 100644 --- a/core/gesture.js +++ b/core/gesture.js @@ -314,7 +314,7 @@ Blockly.Gesture.prototype.updateIsDraggingFromFlyout_ = function() { } if (!this.flyout_.isScrollable() || this.flyout_.isDragTowardWorkspace(this.currentDragDeltaXY_)) { - this.startWorkspace_ = this.flyout_.targetWorkspace_; + this.startWorkspace_ = this.flyout_.targetWorkspace; this.startWorkspace_.updateScreenCalculationsIfScrolled(); // Start the event group now, so that the same event group is used for block // creation and block dragging. diff --git a/core/inject.js b/core/inject.js index a8c483bb3..93651f834 100644 --- a/core/inject.js +++ b/core/inject.js @@ -29,6 +29,8 @@ goog.require('Blockly.utils.userAgent'); goog.require('Blockly.WorkspaceDragSurfaceSvg'); goog.require('Blockly.WorkspaceSvg'); +goog.requireType('Blockly.utils.Metrics'); + /** * Inject a Blockly editor into the specified container element (usually a div). diff --git a/core/mutator.js b/core/mutator.js index dd5c844e8..352648495 100644 --- a/core/mutator.js +++ b/core/mutator.js @@ -27,6 +27,8 @@ goog.require('Blockly.utils.xml'); goog.require('Blockly.WorkspaceSvg'); goog.require('Blockly.Xml'); +goog.requireType('Blockly.utils.Metrics'); + /** * Class for a mutator dialog. @@ -405,15 +407,26 @@ Blockly.Mutator.prototype.workspaceChanged_ = function(e) { * .viewWidth: Width of the visible rectangle, * .absoluteTop: Top-edge of view. * .absoluteLeft: Left-edge of view. - * @return {!Object} Contains size and position metrics of mutator dialog's - * workspace. + * @return {!Blockly.utils.Metrics} Contains size and position metrics of + * mutator dialog's workspace. * @private */ Blockly.Mutator.prototype.getFlyoutMetrics_ = function() { + // The mutator workspace only uses a subset of Blockly.utils.Metrics + // properties as features such as scroll and zoom are unsupported. + var unsupported = 0; return { + contentHeight: unsupported, + contentWidth: unsupported, + contentTop: unsupported, + contentLeft: unsupported, + viewHeight: this.workspaceHeight_, viewWidth: this.workspaceWidth_ - this.workspace_.getFlyout().getWidth(), - absoluteTop: 0, + viewTop: unsupported, + viewLeft: unsupported, + + absoluteTop: unsupported, absoluteLeft: this.workspace_.RTL ? 0 : this.workspace_.getFlyout().getWidth() }; diff --git a/core/options.js b/core/options.js index cbb60edba..6a8853e49 100644 --- a/core/options.js +++ b/core/options.js @@ -15,8 +15,9 @@ goog.provide('Blockly.Options'); goog.require('Blockly.Theme'); goog.require('Blockly.Themes.Classic'); goog.require('Blockly.user.keyMap'); -goog.require('Blockly.utils.userAgent'); +goog.require('Blockly.utils.Metrics'); goog.require('Blockly.utils.toolbox'); +goog.require('Blockly.utils.userAgent'); goog.require('Blockly.Xml'); @@ -185,15 +186,15 @@ Blockly.BlocklyOptions = function() {}; /** * If set, sets the translation of the workspace to match the scrollbars. - * @param {!Object} xyRatio Contains an x and/or y property which is a float - * between 0 and 1 specifying the degree of scrolling. + * @param {!{x:number,y:number}} xyRatio Contains an x and/or y property which + * is a float between 0 and 1 specifying the degree of scrolling. * @return {void} */ Blockly.Options.prototype.setMetrics; /** * Return an object with the metrics required to size the workspace. - * @return {!Object} Contains size and position metrics. + * @return {!Blockly.utils.Metrics} Contains size and position metrics. */ Blockly.Options.prototype.getMetrics; diff --git a/core/scrollbar.js b/core/scrollbar.js index eaa522c5b..753e219de 100644 --- a/core/scrollbar.js +++ b/core/scrollbar.js @@ -17,6 +17,7 @@ goog.require('Blockly.Touch'); goog.require('Blockly.utils'); goog.require('Blockly.utils.Coordinate'); goog.require('Blockly.utils.dom'); +goog.require('Blockly.utils.Metrics'); /** @@ -26,7 +27,7 @@ goog.require('Blockly.utils.dom'); /** * Class for a pair of scrollbars. Horizontal and vertical. - * @param {!Blockly.Workspace} workspace Workspace to bind the scrollbars to. + * @param {!Blockly.WorkspaceSvg} workspace Workspace to bind the scrollbars to. * @constructor */ Blockly.ScrollbarPair = function(workspace) { @@ -44,14 +45,14 @@ Blockly.ScrollbarPair = function(workspace) { }, null); Blockly.utils.dom.insertAfter(this.corner_, workspace.getBubbleCanvas()); -}; -/** - * Previously recorded metrics from the workspace. - * @type {Object} - * @private - */ -Blockly.ScrollbarPair.prototype.oldHostMetrics_ = null; + /** + * Previously recorded metrics from the workspace. + * @type {?Blockly.utils.Metrics} + * @private + */ + this.oldHostMetrics_ = null; +}; /** * Dispose of this pair of scrollbars. @@ -117,12 +118,12 @@ Blockly.ScrollbarPair.prototype.resize = function() { if (!this.oldHostMetrics_ || this.oldHostMetrics_.viewWidth != hostMetrics.viewWidth || this.oldHostMetrics_.absoluteLeft != hostMetrics.absoluteLeft) { - this.corner_.setAttribute('x', this.vScroll.position_.x); + this.corner_.setAttribute('x', this.vScroll.position.x); } if (!this.oldHostMetrics_ || this.oldHostMetrics_.viewHeight != hostMetrics.viewHeight || this.oldHostMetrics_.absoluteTop != hostMetrics.absoluteTop) { - this.corner_.setAttribute('y', this.hScroll.position_.y); + this.corner_.setAttribute('y', this.hScroll.position.y); } // Cache the current metrics to potentially short-cut the next resize event. @@ -144,8 +145,8 @@ Blockly.ScrollbarPair.prototype.set = function(x, y) { // Combining them speeds up rendering. var xyRatio = {}; - var hHandlePosition = x * this.hScroll.ratio_; - var vHandlePosition = y * this.vScroll.ratio_; + var hHandlePosition = x * this.hScroll.ratio; + var vHandlePosition = y * this.vScroll.ratio; var hBarLength = this.hScroll.scrollViewSize_; var vBarLength = this.vScroll.scrollViewSize_; @@ -179,7 +180,7 @@ Blockly.ScrollbarPair.prototype.getRatio_ = function(handlePosition, viewSize) { * Class for a pure SVG scrollbar. * This technique offers a scrollbar that is guaranteed to work, but may not * look or behave like the system's scrollbars. - * @param {!Blockly.Workspace} workspace Workspace to bind the scrollbar to. + * @param {!Blockly.WorkspaceSvg} workspace Workspace to bind the scrollbar to. * @param {boolean} horizontal True if horizontal, false if vertical. * @param {boolean=} opt_pair True if scrollbar is part of a horiz/vert pair. * @param {string=} opt_class A class to be applied to this scrollbar. @@ -191,6 +192,12 @@ Blockly.Scrollbar = function(workspace, horizontal, opt_pair, opt_class) { this.horizontal_ = horizontal; this.oldHostMetrics_ = null; + /** + * @type {?number} + * @package + */ + this.ratio = null; + this.createDom_(opt_class); /** @@ -198,9 +205,9 @@ Blockly.Scrollbar = function(workspace, horizontal, opt_pair, opt_class) { * to the scrollbar's origin. This is usually relative to the injection div * origin. * @type {Blockly.utils.Coordinate} - * @private + * @package */ - this.position_ = new Blockly.utils.Coordinate(0, 0); + this.position = new Blockly.utils.Coordinate(0, 0); // Store the thickness in a temp variable for readability. var scrollbarThickness = Blockly.Scrollbar.scrollbarThickness; @@ -295,10 +302,10 @@ if (Blockly.Touch.TOUCH_ENABLED) { } /** - * @param {Object} first An object containing computed measurements of a - * workspace. - * @param {Object} second Another object containing computed measurements of a - * workspace. + * @param {Blockly.utils.Metrics} first An object containing computed + * measurements of a workspace. + * @param {Blockly.utils.Metrics} second Another object containing computed + * measurements of a workspace. * @return {boolean} Whether the two sets of metrics are equivalent. * @private */ @@ -392,23 +399,23 @@ Blockly.ScrollbarPair.prototype.setContainerVisible = function(visible) { * scrollbar's origin. This sets the scrollbar's location within the workspace. * @param {number} x The new x coordinate. * @param {number} y The new y coordinate. - * @private + * @package */ -Blockly.Scrollbar.prototype.setPosition_ = function(x, y) { - this.position_.x = x; - this.position_.y = y; +Blockly.Scrollbar.prototype.setPosition = function(x, y) { + this.position.x = x; + this.position.y = y; - var tempX = this.position_.x + this.origin_.x; - var tempY = this.position_.y + this.origin_.y; + var tempX = this.position.x + this.origin_.x; + var tempY = this.position.y + this.origin_.y; var transform = 'translate(' + tempX + 'px,' + tempY + 'px)'; Blockly.utils.dom.setCssTransform(this.outerSvg_, transform); }; /** * Recalculate the scrollbar's location and its length. - * @param {Object=} opt_metrics A data structure of from the describing all the - * required dimensions. If not provided, it will be fetched from the host - * object. + * @param {Blockly.utils.Metrics=} opt_metrics A data structure of from the + * describing all the required dimensions. If not provided, it will be + * fetched from the host object. */ Blockly.Scrollbar.prototype.resize = function(opt_metrics) { // Determine the location, height and width of the host element. @@ -450,8 +457,8 @@ Blockly.Scrollbar.prototype.resize = function(opt_metrics) { /** * Recalculate a horizontal scrollbar's location and length. - * @param {!Object} hostMetrics A data structure describing all the - * required dimensions, possibly fetched from the host object. + * @param {!Blockly.utils.Metrics} hostMetrics A data structure describing all + * the required dimensions, possibly fetched from the host object. * @private */ Blockly.Scrollbar.prototype.resizeHorizontal_ = function(hostMetrics) { @@ -463,8 +470,8 @@ Blockly.Scrollbar.prototype.resizeHorizontal_ = function(hostMetrics) { /** * Recalculate a horizontal scrollbar's location on the screen and path length. * This should be called when the layout or size of the window has changed. - * @param {!Object} hostMetrics A data structure describing all the - * required dimensions, possibly fetched from the host object. + * @param {!Blockly.utils.Metrics} hostMetrics A data structure describing all + * the required dimensions, possibly fetched from the host object. */ Blockly.Scrollbar.prototype.resizeViewHorizontal = function(hostMetrics) { var viewSize = hostMetrics.viewWidth - 1; @@ -482,7 +489,7 @@ Blockly.Scrollbar.prototype.resizeViewHorizontal = function(hostMetrics) { // Horizontal toolbar should always be just above the bottom of the workspace. var yCoordinate = hostMetrics.absoluteTop + hostMetrics.viewHeight - Blockly.Scrollbar.scrollbarThickness - 0.5; - this.setPosition_(xCoordinate, yCoordinate); + this.setPosition(xCoordinate, yCoordinate); // If the view has been resized, a content resize will also be necessary. The // reverse is not true. @@ -492,8 +499,8 @@ Blockly.Scrollbar.prototype.resizeViewHorizontal = function(hostMetrics) { /** * Recalculate a horizontal scrollbar's location within its path and length. * This should be called when the contents of the workspace have changed. - * @param {!Object} hostMetrics A data structure describing all the - * required dimensions, possibly fetched from the host object. + * @param {!Blockly.utils.Metrics} hostMetrics A data structure describing all + * the required dimensions, possibly fetched from the host object. */ Blockly.Scrollbar.prototype.resizeContentHorizontal = function(hostMetrics) { if (!this.pair_) { @@ -503,24 +510,24 @@ Blockly.Scrollbar.prototype.resizeContentHorizontal = function(hostMetrics) { this.setVisible(this.scrollViewSize_ < hostMetrics.contentWidth); } - this.ratio_ = this.scrollViewSize_ / hostMetrics.contentWidth; - if (this.ratio_ == -Infinity || this.ratio_ == Infinity || - isNaN(this.ratio_)) { - this.ratio_ = 0; + this.ratio = this.scrollViewSize_ / hostMetrics.contentWidth; + if (this.ratio == -Infinity || this.ratio == Infinity || + isNaN(this.ratio)) { + this.ratio = 0; } - var handleLength = hostMetrics.viewWidth * this.ratio_; + var handleLength = hostMetrics.viewWidth * this.ratio; this.setHandleLength_(Math.max(0, handleLength)); var handlePosition = (hostMetrics.viewLeft - hostMetrics.contentLeft) * - this.ratio_; + this.ratio; this.setHandlePosition(this.constrainHandle_(handlePosition)); }; /** * Recalculate a vertical scrollbar's location and length. - * @param {!Object} hostMetrics A data structure describing all the - * required dimensions, possibly fetched from the host object. + * @param {!Blockly.utils.Metrics} hostMetrics A data structure describing all + * the required dimensions, possibly fetched from the host object. * @private */ Blockly.Scrollbar.prototype.resizeVertical_ = function(hostMetrics) { @@ -532,8 +539,8 @@ Blockly.Scrollbar.prototype.resizeVertical_ = function(hostMetrics) { /** * Recalculate a vertical scrollbar's location on the screen and path length. * This should be called when the layout or size of the window has changed. - * @param {!Object} hostMetrics A data structure describing all the - * required dimensions, possibly fetched from the host object. + * @param {!Blockly.utils.Metrics} hostMetrics A data structure describing all + * the required dimensions, possibly fetched from the host object. */ Blockly.Scrollbar.prototype.resizeViewVertical = function(hostMetrics) { var viewSize = hostMetrics.viewHeight - 1; @@ -549,7 +556,7 @@ Blockly.Scrollbar.prototype.resizeViewVertical = function(hostMetrics) { Blockly.Scrollbar.scrollbarThickness - 1; } var yCoordinate = hostMetrics.absoluteTop + 0.5; - this.setPosition_(xCoordinate, yCoordinate); + this.setPosition(xCoordinate, yCoordinate); // If the view has been resized, a content resize will also be necessary. The // reverse is not true. @@ -559,8 +566,8 @@ Blockly.Scrollbar.prototype.resizeViewVertical = function(hostMetrics) { /** * Recalculate a vertical scrollbar's location within its path and length. * This should be called when the contents of the workspace have changed. - * @param {!Object} hostMetrics A data structure describing all the - * required dimensions, possibly fetched from the host object. + * @param {!Blockly.utils.Metrics} hostMetrics A data structure describing all + * the required dimensions, possibly fetched from the host object. */ Blockly.Scrollbar.prototype.resizeContentVertical = function(hostMetrics) { if (!this.pair_) { @@ -568,17 +575,17 @@ Blockly.Scrollbar.prototype.resizeContentVertical = function(hostMetrics) { this.setVisible(this.scrollViewSize_ < hostMetrics.contentHeight); } - this.ratio_ = this.scrollViewSize_ / hostMetrics.contentHeight; - if (this.ratio_ == -Infinity || this.ratio_ == Infinity || - isNaN(this.ratio_)) { - this.ratio_ = 0; + this.ratio = this.scrollViewSize_ / hostMetrics.contentHeight; + if (this.ratio == -Infinity || this.ratio == Infinity || + isNaN(this.ratio)) { + this.ratio = 0; } - var handleLength = hostMetrics.viewHeight * this.ratio_; + var handleLength = hostMetrics.viewHeight * this.ratio; this.setHandleLength_(Math.max(0, handleLength)); var handlePosition = (hostMetrics.viewTop - hostMetrics.contentTop) * - this.ratio_; + this.ratio; this.setHandlePosition(this.constrainHandle_(handlePosition)); }; @@ -844,7 +851,7 @@ Blockly.Scrollbar.prototype.onScroll_ = function() { * scrollbar handle. */ Blockly.Scrollbar.prototype.set = function(value) { - this.setHandlePosition(this.constrainHandle_(value * this.ratio_)); + this.setHandlePosition(this.constrainHandle_(value * this.ratio)); this.onScroll_(); }; diff --git a/core/utils/metrics.js b/core/utils/metrics.js new file mode 100644 index 000000000..274c382c6 --- /dev/null +++ b/core/utils/metrics.js @@ -0,0 +1,124 @@ +/** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Workspace metrics definitions. + * @author samelh@google.com (Sam El-Husseini) + */ +'use strict'; + +goog.provide('Blockly.utils.Metrics'); + + +/** + * @record + */ +Blockly.utils.Metrics = function() {}; + +/** + * Height of the visible portion of the workspace. + * @type {number} + */ +Blockly.utils.Metrics.prototype.viewHeight; + +/** + * Width of the visible portion of the workspace. + * @type {number} + */ +Blockly.utils.Metrics.prototype.viewWidth; + +/** + * Height of the content. + * @type {number} + */ +Blockly.utils.Metrics.prototype.contentHeight; + +/** + * Width of the content. + * @type {number} + */ +Blockly.utils.Metrics.prototype.contentWidth; + +/** + * Top-edge of the visible portion of the workspace, relative to the workspace + * origin. + * @type {number} + */ +Blockly.utils.Metrics.prototype.viewTop; + +/** + * Left-edge of the visible portion of the workspace, relative to the workspace + * origin. + * @type {number} + */ +Blockly.utils.Metrics.prototype.viewLeft; + +/** + * Top-edge of the content, relative to the workspace origin. + * @type {number} + */ +Blockly.utils.Metrics.prototype.contentTop; + +/** + * Left-edge of the content relative to the workspace origin. + * @type {number} + */ +Blockly.utils.Metrics.prototype.contentLeft; + +/** + * Top-edge of the visible portion of the workspace, relative to the blocklyDiv. + * @type {number} + */ +Blockly.utils.Metrics.prototype.absoluteTop; + +/** + * Left-edge of the visible portion of the workspace, relative to the + * blocklyDiv. + * @type {number} + */ +Blockly.utils.Metrics.prototype.absoluteLeft; + +/** + * Height of the Blockly div (the view + the toolbox, simple of otherwise). + * @type {number|undefined} + */ +Blockly.utils.Metrics.prototype.svgHeight; + +/** + * Width of the Blockly div (the view + the toolbox, simple or otherwise). + * @type {number|undefined} + */ +Blockly.utils.Metrics.prototype.svgWidth; + +/** + * Width of the toolbox, if it exists. Otherwise zero. + * @type {number|undefined} + */ +Blockly.utils.Metrics.prototype.toolboxWidth; + +/** + * Height of the toolbox, if it exists. Otherwise zero. + * @type {number|undefined} + */ +Blockly.utils.Metrics.prototype.toolboxHeight; + +/** + * Top, bottom, left or right. Use TOOLBOX_AT constants to compare. + * @type {number|undefined} + */ +Blockly.utils.Metrics.prototype.toolboxPosition; + +/** + * Width of the flyout if it is always open. Otherwise zero. + * @type {number|undefined} + */ +Blockly.utils.Metrics.prototype.flyoutWidth; + +/** + * Height of the flyout if it is always open. Otherwise zero. + * @type {number|undefined} + */ +Blockly.utils.Metrics.prototype.flyoutHeight; diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 3037e2c9f..819ef2407 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -30,6 +30,7 @@ goog.require('Blockly.TouchGesture'); goog.require('Blockly.utils'); goog.require('Blockly.utils.Coordinate'); goog.require('Blockly.utils.dom'); +goog.require('Blockly.utils.Metrics'); goog.require('Blockly.utils.object'); goog.require('Blockly.utils.Rect'); goog.require('Blockly.utils.toolbox'); @@ -58,10 +59,10 @@ goog.requireType('Blockly.IBoundedElement'); Blockly.WorkspaceSvg = function(options, opt_blockDragSurface, opt_wsDragSurface) { Blockly.WorkspaceSvg.superClass_.constructor.call(this, options); - /** @type {function():!Object} */ + /** @type {function():!Blockly.utils.Metrics} */ this.getMetrics = options.getMetrics || Blockly.WorkspaceSvg.getTopLevelWorkspaceMetrics_; - /** @type {function(!Object):void} */ + /** @type {function(!{x:number, y:number}):void} */ this.setMetrics = options.setMetrics || Blockly.WorkspaceSvg.setTopLevelWorkspaceMetrics_; @@ -1967,8 +1968,8 @@ Blockly.WorkspaceSvg.prototype.zoomCenter = function(type) { // when the size of the flyout increases) you need the center of the // *blockly div* to stay in the same pixel-position. // Note: This only works because of how scrollCenter positions blocks. - var x = metrics.svgWidth / 2; - var y = metrics.svgHeight / 2; + var x = metrics.svgWidth ? metrics.svgWidth / 2 : 0; + var y = metrics.svgHeight ? metrics.svgHeight / 2 : 0; } else { var x = (metrics.viewWidth / 2) + metrics.absoluteLeft; var y = (metrics.viewHeight / 2) + metrics.absoluteTop; @@ -2217,11 +2218,10 @@ Blockly.WorkspaceSvg.prototype.scroll = function(x, y) { // the content's top-left to the view's top-left, matching the // directionality of the scrollbars. - // TODO (#2299): Change these to not use the internal ratio_ property. this.scrollbar.hScroll.setHandlePosition(-(x + metrics.contentLeft) * - this.scrollbar.hScroll.ratio_); + this.scrollbar.hScroll.ratio); this.scrollbar.vScroll.setHandlePosition(-(y + metrics.contentTop) * - this.scrollbar.vScroll.ratio_); + this.scrollbar.vScroll.ratio); } // We have to shift the translation so that when the canvas is at 0, 0 the // workspace origin is not underneath the toolbox. @@ -2235,8 +2235,8 @@ Blockly.WorkspaceSvg.prototype.scroll = function(x, y) { * @param {Blockly.Toolbox|Blockly.Flyout} elem The element to get the * dimensions of, or null. It should be a toolbox or flyout, and should * implement getWidth() and getHeight(). - * @return {!Object} An object containing width and height attributes, which - * will both be zero if elem did not exist. + * @return {!Blockly.utils.Size} An object containing width and height + * attributes, which will both be zero if elem did not exist. * @private */ Blockly.WorkspaceSvg.getDimensionsPx_ = function(elem) { @@ -2246,10 +2246,7 @@ Blockly.WorkspaceSvg.getDimensionsPx_ = function(elem) { width = elem.getWidth(); height = elem.getHeight(); } - return { - width: width, - height: height - }; + return new Blockly.utils.Size(width, height); }; /** @@ -2370,8 +2367,8 @@ Blockly.WorkspaceSvg.getContentDimensionsBounded_ = function(ws, svgSize) { * .flyoutHeight: Height of the flyout if it is always open. Otherwise zero. * .toolboxPosition: Top, bottom, left or right. Use TOOLBOX_AT constants to * compare. - * @return {!Object} Contains size and position metrics of a top level - * workspace. + * @return {!Blockly.utils.Metrics} Contains size and position metrics of a top + * level workspace. * @private * @this {Blockly.WorkspaceSvg} */ @@ -2441,11 +2438,10 @@ Blockly.WorkspaceSvg.getTopLevelWorkspaceMetrics_ = function() { toolboxWidth: toolboxDimensions.width, toolboxHeight: toolboxDimensions.height, + toolboxPosition: this.toolboxPosition, flyoutWidth: flyoutDimensions.width, - flyoutHeight: flyoutDimensions.height, - - toolboxPosition: this.toolboxPosition + flyoutHeight: flyoutDimensions.height }; return metrics; }; diff --git a/demos/minimap/minimap.js b/demos/minimap/minimap.js index db467a503..fe19065bf 100644 --- a/demos/minimap/minimap.js +++ b/demos/minimap/minimap.js @@ -35,7 +35,7 @@ Minimap.init = function(workspace, minimap) { // New code starts from here. // Get the absolutePosition. - var absolutePosition = (this.handlePosition_ / this.ratio_); + var absolutePosition = (this.handlePosition_ / this.ratio); // Firing the scroll change listener. Minimap.onScrollChange(absolutePosition, this.horizontal_); @@ -50,7 +50,7 @@ Minimap.init = function(workspace, minimap) { // New code starts from here. // Get the absolutePosition. - var absolutePosition = (this.handlePosition_ / this.ratio_); + var absolutePosition = (this.handlePosition_ / this.ratio); // Firing the scroll change listener. Minimap.onScrollChange(absolutePosition, this.horizontal_); diff --git a/scripts/gulpfiles/typings.js b/scripts/gulpfiles/typings.js index 0822dea3b..cdb16ad66 100644 --- a/scripts/gulpfiles/typings.js +++ b/scripts/gulpfiles/typings.js @@ -28,7 +28,6 @@ function typings() { "core/", "core/components", "core/components/tree", - "core/components/menu", "core/keyboard_nav", "core/renderers/common", "core/renderers/measurables", @@ -66,9 +65,9 @@ function typings() { 'typings/templates/blockly-header.template', 'typings/templates/blockly-interfaces.template', `${tmpDir}/core/**`, + `${tmpDir}/core/interfaces/**`, `${tmpDir}/core/components/**`, `${tmpDir}/core/components/tree/**`, - `${tmpDir}/core/components/menu/**`, `${tmpDir}/core/keyboard_nav/**`, `${tmpDir}/core/renderers/common/**`, `${tmpDir}/core/renderers/measurables/**`, diff --git a/typings/templates/blockly-interfaces.template b/typings/templates/blockly-interfaces.template index 4afd7fe2c..a1639546f 100644 --- a/typings/templates/blockly-interfaces.template +++ b/typings/templates/blockly-interfaces.template @@ -51,19 +51,6 @@ declare module Blockly { startHats?: boolean; } - interface Metrics { - absoluteLeft: number; - absoluteTop: number; - contentHeight: number; - contentLeft: number; - contentTop: number; - contentWidth: number; - viewHeight: number; - viewLeft: number; - viewTop: number; - viewWidth: number; - } - /** * Set the Blockly locale. * Note: this method is only available in the npm release of Blockly. @@ -72,3 +59,25 @@ declare module Blockly { */ function setLocale(msg: {[key: string]: string;}): void; } + +declare module Blockly.utils { + interface Metrics { + viewHeight: number; + viewWidth: number; + contentHeight: number; + contentWidth: number; + viewTop: number; + viewLeft: number; + contentTop: number; + contentLeft: number; + absoluteTop: number; + absoluteLeft: number; + svgHeight?: number; + svgWidth?: number; + toolboxWidth?: number; + toolboxHeight?: number; + flyoutWidth?: number; + flyoutHeight?: number; + toolboxPosition?: number; + } +}