diff --git a/core/block_svg.js b/core/block_svg.js index 46d187e0a..19a58bc9b 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -54,10 +54,11 @@ goog.requireType('Blockly.ICopyable'); Blockly.BlockSvg = function(workspace, prototypeName, opt_id) { // Create core elements for the block. /** - * @type {!SVGElement} + * @type {!SVGGElement} * @private */ - this.svgGroup_ = Blockly.utils.dom.createSvgElement('g', {}, null); + this.svgGroup_ = /** @type {!SVGGElement} */ ( + Blockly.utils.dom.createSvgElement('g', {}, null)); this.svgGroup_.translate_ = ''; /** @@ -931,7 +932,7 @@ Blockly.BlockSvg.prototype.setInsertionMarker = function(insertionMarker) { /** * Return the root node of the SVG or null if none exists. - * @return {!SVGElement} The root SVG node (probably a group). + * @return {!SVGGElement} The root SVG node (probably a group). */ Blockly.BlockSvg.prototype.getSvgRoot = function() { return this.svgGroup_; diff --git a/core/contextmenu.js b/core/contextmenu.js index 07f636bc3..f4b10dbfc 100644 --- a/core/contextmenu.js +++ b/core/contextmenu.js @@ -24,6 +24,7 @@ goog.require('Blockly.Msg'); goog.require('Blockly.utils'); goog.require('Blockly.utils.Coordinate'); goog.require('Blockly.utils.dom'); +goog.require('Blockly.utils.Rect'); goog.require('Blockly.utils.userAgent'); goog.require('Blockly.Xml'); @@ -109,12 +110,12 @@ Blockly.ContextMenu.position_ = function(menu, e, rtl) { var viewportBBox = Blockly.utils.getViewportBBox(); // This one is just a point, but we'll pretend that it's a rect so we can use // some helper functions. - var anchorBBox = { - top: e.clientY + viewportBBox.top, - bottom: e.clientY + viewportBBox.top, - left: e.clientX + viewportBBox.left, - right: e.clientX + viewportBBox.left - }; + var anchorBBox = new Blockly.utils.Rect( + e.clientY + viewportBBox.top, + e.clientY + viewportBBox.top, + e.clientX + viewportBBox.left, + e.clientX + viewportBBox.left + ); Blockly.ContextMenu.createWidget_(menu); var menuSize = menu.getSize(); diff --git a/core/dropdowndiv.js b/core/dropdowndiv.js index a84920b1c..770af679f 100644 --- a/core/dropdowndiv.js +++ b/core/dropdowndiv.js @@ -19,6 +19,9 @@ goog.require('Blockly.utils.dom'); goog.require('Blockly.utils.math'); goog.require('Blockly.utils.style'); +goog.requireType('Blockly.utils.Rect'); +goog.requireType('Blockly.utils.Size'); + /** * Class for drop-down div. @@ -117,6 +120,35 @@ Blockly.DropDownDiv.rendererClassName_ = ''; */ Blockly.DropDownDiv.themeClassName_ = ''; +/** + * Dropdown bounds info object used to encapsulate sizing information about a + * bounding element (bounding box and width/height). + * @typedef {{ + * top:number, + * left:number, + * bottom:number, + * right:number, + * width:number, + * height:number + * }} + */ +Blockly.DropDownDiv.BoundsInfo; + +/** + * Dropdown position metrics. + * @typedef {{ + * initialX:number, + * initialY:number, + * finalX:number, + * finalY:number, + * arrowX:?number, + * arrowY:?number, + * arrowAtTop:?boolean, + * arrowVisible:boolean + * }} + */ +Blockly.DropDownDiv.PositionMetrics; + /** * Create and insert the DOM element for this div. * @package @@ -214,7 +246,7 @@ Blockly.DropDownDiv.setColour = function(backgroundColour, borderColour) { * and the secondary position above the block. Drop-down will be * constrained to the block's workspace. * @param {!Blockly.Field} field The field showing the drop-down. - * @param {!Blockly.Block} block Block to position the drop-down around. + * @param {!Blockly.BlockSvg} block Block to position the drop-down around. * @param {Function=} opt_onHide Optional callback for when the drop-down is * hidden. * @param {number=} opt_secondaryYOffset Optional Y offset for above-block @@ -250,7 +282,7 @@ Blockly.DropDownDiv.showPositionedByField = function(field, /** * Get the scaled bounding box of a block. - * @param {!Blockly.Block} block The block. + * @param {!Blockly.BlockSvg} block The block. * @return {!Blockly.utils.Rect} The scaled bounding box of the block. * @private */ @@ -302,14 +334,15 @@ Blockly.DropDownDiv.showPositionedByRect_ = function(bBox, field, if (opt_secondaryYOffset) { secondaryY += opt_secondaryYOffset; } - var sourceBlock = field.getSourceBlock(); + var sourceBlock = /** @type {!Blockly.BlockSvg} */ (field.getSourceBlock()); // Set bounds to main workspace; show the drop-down. var workspace = sourceBlock.workspace; while (workspace.options.parentWorkspace) { - workspace = workspace.options.parentWorkspace; + workspace = /** @type {!Blockly.WorkspaceSvg} */ ( + workspace.options.parentWorkspace); } Blockly.DropDownDiv.setBoundsElement( - workspace.getParentSvg().parentNode); + /** @type {Element} */ (workspace.getParentSvg().parentNode)); return Blockly.DropDownDiv.show( field, sourceBlock.RTL, primaryX, primaryY, secondaryX, secondaryY, opt_onHide); @@ -344,10 +377,11 @@ Blockly.DropDownDiv.show = function(owner, rtl, primaryX, primaryY, var div = Blockly.DropDownDiv.DIV_; div.style.direction = rtl ? 'rtl' : 'ltr'; + var mainWorkspace = + /** @type {!Blockly.WorkspaceSvg} */ (Blockly.getMainWorkspace()); Blockly.DropDownDiv.rendererClassName_ = - Blockly.getMainWorkspace().getRenderer().getClassName(); - Blockly.DropDownDiv.themeClassName_ = - Blockly.getMainWorkspace().getTheme().getClassName(); + mainWorkspace.getRenderer().getClassName(); + Blockly.DropDownDiv.themeClassName_ = mainWorkspace.getTheme().getClassName(); Blockly.utils.dom.addClass(div, Blockly.DropDownDiv.rendererClassName_); Blockly.utils.dom.addClass(div, Blockly.DropDownDiv.themeClassName_); @@ -366,8 +400,8 @@ Blockly.DropDownDiv.show = function(owner, rtl, primaryX, primaryY, /** * Get sizing info about the bounding element. - * @return {!Object} An object containing size information about the bounding - * element (bounding box and width/height). + * @return {!Blockly.DropDownDiv.BoundsInfo} An object containing size + * information about the bounding element (bounding box and width/height). * @private */ Blockly.DropDownDiv.getBoundsInfo_ = function() { @@ -392,11 +426,11 @@ Blockly.DropDownDiv.getBoundsInfo_ = function() { * @param {number} primaryX Desired origin point x, in absolute px. * @param {number} primaryY Desired origin point y, in absolute px. * @param {number} secondaryX Secondary/alternative origin point x, - * in absolute px. + * in absolute px. * @param {number} secondaryY Secondary/alternative origin point y, - * in absolute px. - * @return {Object} Various final metrics, including rendered positions - * for drop-down and arrow. + * in absolute px. + * @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics, + * including rendered positions for drop-down and arrow. * @private */ Blockly.DropDownDiv.getPositionMetrics_ = function(primaryX, primaryY, @@ -435,12 +469,12 @@ Blockly.DropDownDiv.getPositionMetrics_ = function(primaryX, primaryY, * Get the metrics for positioning the div below the source. * @param {number} primaryX Desired origin point x, in absolute px. * @param {number} primaryY Desired origin point y, in absolute px. - * @param {!Object} boundsInfo An object containing size information about the - * bounding element (bounding box and width/height). - * @param {!Object} divSize An object containing information about the size - * of the DropDownDiv (width & height). - * @return {Object} Various final metrics, including rendered positions - * for drop-down and arrow. + * @param {!Blockly.DropDownDiv.BoundsInfo} boundsInfo An object containing size + * information about the bounding element (bounding box and width/height). + * @param {!Blockly.utils.Size} divSize An object containing information about + * the size of the DropDownDiv (width & height). + * @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics, + * including rendered positions for drop-down and arrow. * @private */ Blockly.DropDownDiv.getPositionBelowMetrics_ = function( @@ -468,15 +502,15 @@ Blockly.DropDownDiv.getPositionBelowMetrics_ = function( /** * Get the metrics for positioning the div above the source. * @param {number} secondaryX Secondary/alternative origin point x, - * in absolute px. + * in absolute px. * @param {number} secondaryY Secondary/alternative origin point y, - * in absolute px. - * @param {!Object} boundsInfo An object containing size information about the - * bounding element (bounding box and width/height). - * @param {!Object} divSize An object containing information about the size - * of the DropDownDiv (width & height). - * @return {Object} Various final metrics, including rendered positions - * for drop-down and arrow. + * in absolute px. + * @param {!Blockly.DropDownDiv.BoundsInfo} boundsInfo An object containing size + * information about the bounding element (bounding box and width/height). + * @param {!Blockly.utils.Size} divSize An object containing information about + * the size of the DropDownDiv (width & height). + * @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics, + * including rendered positions for drop-down and arrow. * @private */ Blockly.DropDownDiv.getPositionAboveMetrics_ = function( @@ -505,12 +539,12 @@ Blockly.DropDownDiv.getPositionAboveMetrics_ = function( /** * Get the metrics for positioning the div at the top of the page. * @param {number} sourceX Desired origin point x, in absolute px. - * @param {!Object} boundsInfo An object containing size information about the - * bounding element (bounding box and width/height). - * @param {!Object} divSize An object containing information about the size - * of the DropDownDiv (width & height). - * @return {Object} Various final metrics, including rendered positions - * for drop-down and arrow. + * @param {!Blockly.DropDownDiv.BoundsInfo} boundsInfo An object containing size + * information about the bounding element (bounding box and width/height). + * @param {!Blockly.utils.Size} divSize An object containing information about + * the size of the DropDownDiv (width & height). + * @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics, + * including rendered positions for drop-down and arrow. * @private */ Blockly.DropDownDiv.getPositionTopOfPageMetrics_ = function( @@ -525,6 +559,9 @@ Blockly.DropDownDiv.getPositionTopOfPageMetrics_ = function( initialY : 0, finalX: xCoords.divX, // X position remains constant during animation. finalY: 0, // Y position remains constant during animation. + arrowAtTop: null, + arrowX: null, + arrowY: null, arrowVisible: false }; }; @@ -653,7 +690,8 @@ Blockly.DropDownDiv.hideWithoutAnimation = function() { Blockly.utils.dom.removeClass(div, Blockly.DropDownDiv.themeClassName_); Blockly.DropDownDiv.themeClassName_ = ''; } - Blockly.getMainWorkspace().markFocused(); + (/** @type {!Blockly.WorkspaceSvg} */ ( + Blockly.getMainWorkspace())).markFocused(); }; /** @@ -704,7 +742,7 @@ Blockly.DropDownDiv.positionInternal_ = function( var dy = finalY - initialY; div.style.transform = 'translate(' + dx + 'px,' + dy + 'px)'; - return metrics.arrowAtTop; + return !!metrics.arrowAtTop; }; /** @@ -720,7 +758,7 @@ Blockly.DropDownDiv.repositionForWindowResize = function() { // it. if (Blockly.DropDownDiv.owner_) { var field = /** @type {!Blockly.Field} */ (Blockly.DropDownDiv.owner_); - var block = Blockly.DropDownDiv.owner_.getSourceBlock(); + var block = /** @type {!Blockly.BlockSvg} */ (field.getSourceBlock()); var bBox = Blockly.DropDownDiv.positionToField_ ? Blockly.DropDownDiv.getScaledBboxOfField_(field) : Blockly.DropDownDiv.getScaledBboxOfBlock_(block); diff --git a/core/field.js b/core/field.js index 6e7e76883..03cbf6d2e 100644 --- a/core/field.js +++ b/core/field.js @@ -19,6 +19,7 @@ goog.require('Blockly.Events.BlockChange'); goog.require('Blockly.Gesture'); goog.require('Blockly.utils'); goog.require('Blockly.utils.dom'); +goog.require('Blockly.utils.Rect'); goog.require('Blockly.utils.Size'); goog.require('Blockly.utils.style'); goog.require('Blockly.utils.userAgent'); @@ -716,8 +717,8 @@ Blockly.Field.prototype.getSize = function() { /** * Returns the bounding box of the rendered field, accounting for workspace * scaling. - * @return {!Object} An object with top, bottom, left, and right in pixels - * relative to the top left corner of the page (window coordinates). + * @return {!Blockly.utils.Rect} An object with top, bottom, left, and right in + * pixels relative to the top left corner of the page (window coordinates). * @package */ Blockly.Field.prototype.getScaledBBox = function() { @@ -750,12 +751,12 @@ Blockly.Field.prototype.getScaledBBox = function() { var scaledWidth = bBox.width; var scaledHeight = bBox.height; } - return { - top: xy.y, - bottom: xy.y + scaledHeight, - left: xy.x, - right: xy.x + scaledWidth - }; + return new Blockly.utils.Rect( + xy.y, + xy.y + scaledHeight, + xy.x, + xy.x + scaledWidth + ); }; /** diff --git a/core/utils.js b/core/utils.js index cfd047ca1..4bbffdca5 100644 --- a/core/utils.js +++ b/core/utils.js @@ -23,6 +23,7 @@ goog.require('Blockly.constants'); goog.require('Blockly.utils.colour'); goog.require('Blockly.utils.Coordinate'); goog.require('Blockly.utils.global'); +goog.require('Blockly.utils.Rect'); goog.require('Blockly.utils.string'); goog.require('Blockly.utils.style'); goog.require('Blockly.utils.userAgent'); @@ -496,19 +497,19 @@ Blockly.utils.runAfterPageLoad = function(fn) { /** * Get the position of the current viewport in window coordinates. This takes * scroll into account. - * @return {!Object} An object containing window width, height, and scroll - * position in window coordinates. + * @return {!Blockly.utils.Rect} An object containing window width, height, and + * scroll position in window coordinates. * @package */ Blockly.utils.getViewportBBox = function() { // Pixels, in window coordinates. var scrollOffset = Blockly.utils.style.getViewportPageOffset(); - return { - right: document.documentElement.clientWidth + scrollOffset.x, - bottom: document.documentElement.clientHeight + scrollOffset.y, - top: scrollOffset.y, - left: scrollOffset.x - }; + return new Blockly.utils.Rect( + scrollOffset.y, + document.documentElement.clientHeight + scrollOffset.y, + scrollOffset.x, + document.documentElement.clientWidth + scrollOffset.x + ); }; /** diff --git a/core/widgetdiv.js b/core/widgetdiv.js index 3529a4400..37631bd2b 100644 --- a/core/widgetdiv.js +++ b/core/widgetdiv.js @@ -20,6 +20,9 @@ goog.provide('Blockly.WidgetDiv'); goog.require('Blockly.utils.style'); +goog.requireType('Blockly.utils.Rect'); +goog.requireType('Blockly.utils.Size'); + /** * The object currently using this container. @@ -80,10 +83,11 @@ Blockly.WidgetDiv.show = function(newOwner, rtl, dispose) { var div = Blockly.WidgetDiv.DIV; div.style.direction = rtl ? 'rtl' : 'ltr'; div.style.display = 'block'; + var mainWorkspace = + /** @type {!Blockly.WorkspaceSvg} */ (Blockly.getMainWorkspace()); Blockly.WidgetDiv.rendererClassName_ = - Blockly.getMainWorkspace().getRenderer().getClassName(); - Blockly.WidgetDiv.themeClassName_ = - Blockly.getMainWorkspace().getTheme().getClassName(); + mainWorkspace.getRenderer().getClassName(); + Blockly.WidgetDiv.themeClassName_ = mainWorkspace.getTheme().getClassName(); Blockly.utils.dom.addClass(div, Blockly.WidgetDiv.rendererClassName_); Blockly.utils.dom.addClass(div, Blockly.WidgetDiv.themeClassName_); }; @@ -113,7 +117,8 @@ Blockly.WidgetDiv.hide = function() { Blockly.utils.dom.removeClass(div, Blockly.WidgetDiv.themeClassName_); Blockly.WidgetDiv.themeClassName_ = ''; } - Blockly.getMainWorkspace().markFocused(); + (/** @type {!Blockly.WorkspaceSvg} */ ( + Blockly.getMainWorkspace())).markFocused(); }; /** @@ -154,12 +159,12 @@ Blockly.WidgetDiv.positionInternal_ = function(x, y, height) { * The widget should be placed adjacent to but not overlapping the anchor * rectangle. The preferred position is directly below and aligned to the left * (LTR) or right (RTL) side of the anchor. - * @param {!Object} viewportBBox The bounding rectangle of the current viewport, + * @param {!Blockly.utils.Rect} viewportBBox The bounding rectangle of the + * current viewport, in window coordinates. + * @param {!Blockly.utils.Rect} anchorBBox The bounding rectangle of the anchor, * in window coordinates. - * @param {!Object} anchorBBox The bounding rectangle of the anchor, in window - * coordinates. - * @param {!Blockly.utils.Size} widgetSize The size of the widget that is inside the - * widget div, in window coordinates. + * @param {!Blockly.utils.Size} widgetSize The size of the widget that is inside + * the widget div, in window coordinates. * @param {boolean} rtl Whether the workspace is in RTL mode. This determines * horizontal alignment. * @package @@ -180,10 +185,10 @@ Blockly.WidgetDiv.positionWithAnchor = function(viewportBBox, anchorBBox, /** * Calculate an x position (in window coordinates) such that the widget will not * be offscreen on the right or left. - * @param {!Object} viewportBBox The bounding rectangle of the current viewport, + * @param {!Blockly.utils.Rect} viewportBBox The bounding rectangle of the + * current viewport, in window coordinates. + * @param {!Blockly.utils.Rect} anchorBBox The bounding rectangle of the anchor, * in window coordinates. - * @param {!Object} anchorBBox The bounding rectangle of the anchor, in window - * coordinates. * @param {Blockly.utils.Size} widgetSize The dimensions of the widget inside the * widget div. * @param {boolean} rtl Whether the Blockly workspace is in RTL mode. @@ -212,10 +217,10 @@ Blockly.WidgetDiv.calculateX_ = function(viewportBBox, anchorBBox, widgetSize, /** * Calculate a y position (in window coordinates) such that the widget will not * be offscreen on the top or bottom. - * @param {!Object} viewportBBox The bounding rectangle of the current viewport, + * @param {!Blockly.utils.Rect} viewportBBox The bounding rectangle of the + * current viewport, in window coordinates. + * @param {!Blockly.utils.Rect} anchorBBox The bounding rectangle of the anchor, * in window coordinates. - * @param {!Object} anchorBBox The bounding rectangle of the anchor, in window - * coordinates. * @param {Blockly.utils.Size} widgetSize The dimensions of the widget inside the * widget div. * @return {number} A valid y-coordinate for the top left corner of the widget