Type fixes for Dropdown / Widget divs (#3911)

* Stricter typechecks in dropdown and widget div
This commit is contained in:
Sam El-Husseini
2020-05-28 08:42:26 -07:00
committed by GitHub
parent 2b2ff909a9
commit acfc335870
6 changed files with 124 additions and 77 deletions

View File

@@ -54,10 +54,11 @@ goog.requireType('Blockly.ICopyable');
Blockly.BlockSvg = function(workspace, prototypeName, opt_id) { Blockly.BlockSvg = function(workspace, prototypeName, opt_id) {
// Create core elements for the block. // Create core elements for the block.
/** /**
* @type {!SVGElement} * @type {!SVGGElement}
* @private * @private
*/ */
this.svgGroup_ = Blockly.utils.dom.createSvgElement('g', {}, null); this.svgGroup_ = /** @type {!SVGGElement} */ (
Blockly.utils.dom.createSvgElement('g', {}, null));
this.svgGroup_.translate_ = ''; 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 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() { Blockly.BlockSvg.prototype.getSvgRoot = function() {
return this.svgGroup_; return this.svgGroup_;

View File

@@ -24,6 +24,7 @@ goog.require('Blockly.Msg');
goog.require('Blockly.utils'); goog.require('Blockly.utils');
goog.require('Blockly.utils.Coordinate'); goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.dom'); goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.Rect');
goog.require('Blockly.utils.userAgent'); goog.require('Blockly.utils.userAgent');
goog.require('Blockly.Xml'); goog.require('Blockly.Xml');
@@ -109,12 +110,12 @@ Blockly.ContextMenu.position_ = function(menu, e, rtl) {
var viewportBBox = Blockly.utils.getViewportBBox(); var viewportBBox = Blockly.utils.getViewportBBox();
// This one is just a point, but we'll pretend that it's a rect so we can use // This one is just a point, but we'll pretend that it's a rect so we can use
// some helper functions. // some helper functions.
var anchorBBox = { var anchorBBox = new Blockly.utils.Rect(
top: e.clientY + viewportBBox.top, e.clientY + viewportBBox.top,
bottom: e.clientY + viewportBBox.top, e.clientY + viewportBBox.top,
left: e.clientX + viewportBBox.left, e.clientX + viewportBBox.left,
right: e.clientX + viewportBBox.left e.clientX + viewportBBox.left
}; );
Blockly.ContextMenu.createWidget_(menu); Blockly.ContextMenu.createWidget_(menu);
var menuSize = menu.getSize(); var menuSize = menu.getSize();

View File

@@ -19,6 +19,9 @@ goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.math'); goog.require('Blockly.utils.math');
goog.require('Blockly.utils.style'); goog.require('Blockly.utils.style');
goog.requireType('Blockly.utils.Rect');
goog.requireType('Blockly.utils.Size');
/** /**
* Class for drop-down div. * Class for drop-down div.
@@ -117,6 +120,35 @@ Blockly.DropDownDiv.rendererClassName_ = '';
*/ */
Blockly.DropDownDiv.themeClassName_ = ''; 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. * Create and insert the DOM element for this div.
* @package * @package
@@ -214,7 +246,7 @@ Blockly.DropDownDiv.setColour = function(backgroundColour, borderColour) {
* and the secondary position above the block. Drop-down will be * and the secondary position above the block. Drop-down will be
* constrained to the block's workspace. * constrained to the block's workspace.
* @param {!Blockly.Field} field The field showing the drop-down. * @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 * @param {Function=} opt_onHide Optional callback for when the drop-down is
* hidden. * hidden.
* @param {number=} opt_secondaryYOffset Optional Y offset for above-block * @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. * 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. * @return {!Blockly.utils.Rect} The scaled bounding box of the block.
* @private * @private
*/ */
@@ -302,14 +334,15 @@ Blockly.DropDownDiv.showPositionedByRect_ = function(bBox, field,
if (opt_secondaryYOffset) { if (opt_secondaryYOffset) {
secondaryY += 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. // Set bounds to main workspace; show the drop-down.
var workspace = sourceBlock.workspace; var workspace = sourceBlock.workspace;
while (workspace.options.parentWorkspace) { while (workspace.options.parentWorkspace) {
workspace = workspace.options.parentWorkspace; workspace = /** @type {!Blockly.WorkspaceSvg} */ (
workspace.options.parentWorkspace);
} }
Blockly.DropDownDiv.setBoundsElement( Blockly.DropDownDiv.setBoundsElement(
workspace.getParentSvg().parentNode); /** @type {Element} */ (workspace.getParentSvg().parentNode));
return Blockly.DropDownDiv.show( return Blockly.DropDownDiv.show(
field, sourceBlock.RTL, field, sourceBlock.RTL,
primaryX, primaryY, secondaryX, secondaryY, opt_onHide); primaryX, primaryY, secondaryX, secondaryY, opt_onHide);
@@ -344,10 +377,11 @@ Blockly.DropDownDiv.show = function(owner, rtl, primaryX, primaryY,
var div = Blockly.DropDownDiv.DIV_; var div = Blockly.DropDownDiv.DIV_;
div.style.direction = rtl ? 'rtl' : 'ltr'; div.style.direction = rtl ? 'rtl' : 'ltr';
var mainWorkspace =
/** @type {!Blockly.WorkspaceSvg} */ (Blockly.getMainWorkspace());
Blockly.DropDownDiv.rendererClassName_ = Blockly.DropDownDiv.rendererClassName_ =
Blockly.getMainWorkspace().getRenderer().getClassName(); mainWorkspace.getRenderer().getClassName();
Blockly.DropDownDiv.themeClassName_ = Blockly.DropDownDiv.themeClassName_ = mainWorkspace.getTheme().getClassName();
Blockly.getMainWorkspace().getTheme().getClassName();
Blockly.utils.dom.addClass(div, Blockly.DropDownDiv.rendererClassName_); Blockly.utils.dom.addClass(div, Blockly.DropDownDiv.rendererClassName_);
Blockly.utils.dom.addClass(div, Blockly.DropDownDiv.themeClassName_); 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. * Get sizing info about the bounding element.
* @return {!Object} An object containing size information about the bounding * @return {!Blockly.DropDownDiv.BoundsInfo} An object containing size
* element (bounding box and width/height). * information about the bounding element (bounding box and width/height).
* @private * @private
*/ */
Blockly.DropDownDiv.getBoundsInfo_ = function() { Blockly.DropDownDiv.getBoundsInfo_ = function() {
@@ -392,11 +426,11 @@ Blockly.DropDownDiv.getBoundsInfo_ = function() {
* @param {number} primaryX Desired origin point x, in absolute px. * @param {number} primaryX Desired origin point x, in absolute px.
* @param {number} primaryY Desired origin point y, in absolute px. * @param {number} primaryY Desired origin point y, in absolute px.
* @param {number} secondaryX Secondary/alternative origin point x, * @param {number} secondaryX Secondary/alternative origin point x,
* in absolute px. * in absolute px.
* @param {number} secondaryY Secondary/alternative origin point y, * @param {number} secondaryY Secondary/alternative origin point y,
* in absolute px. * in absolute px.
* @return {Object} Various final metrics, including rendered positions * @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics,
* for drop-down and arrow. * including rendered positions for drop-down and arrow.
* @private * @private
*/ */
Blockly.DropDownDiv.getPositionMetrics_ = function(primaryX, primaryY, 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. * Get the metrics for positioning the div below the source.
* @param {number} primaryX Desired origin point x, in absolute px. * @param {number} primaryX Desired origin point x, in absolute px.
* @param {number} primaryY Desired origin point y, in absolute px. * @param {number} primaryY Desired origin point y, in absolute px.
* @param {!Object} boundsInfo An object containing size information about the * @param {!Blockly.DropDownDiv.BoundsInfo} boundsInfo An object containing size
* bounding element (bounding box and width/height). * information about the bounding element (bounding box and width/height).
* @param {!Object} divSize An object containing information about the size * @param {!Blockly.utils.Size} divSize An object containing information about
* of the DropDownDiv (width & height). * the size of the DropDownDiv (width & height).
* @return {Object} Various final metrics, including rendered positions * @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics,
* for drop-down and arrow. * including rendered positions for drop-down and arrow.
* @private * @private
*/ */
Blockly.DropDownDiv.getPositionBelowMetrics_ = function( Blockly.DropDownDiv.getPositionBelowMetrics_ = function(
@@ -468,15 +502,15 @@ Blockly.DropDownDiv.getPositionBelowMetrics_ = function(
/** /**
* Get the metrics for positioning the div above the source. * Get the metrics for positioning the div above the source.
* @param {number} secondaryX Secondary/alternative origin point x, * @param {number} secondaryX Secondary/alternative origin point x,
* in absolute px. * in absolute px.
* @param {number} secondaryY Secondary/alternative origin point y, * @param {number} secondaryY Secondary/alternative origin point y,
* in absolute px. * in absolute px.
* @param {!Object} boundsInfo An object containing size information about the * @param {!Blockly.DropDownDiv.BoundsInfo} boundsInfo An object containing size
* bounding element (bounding box and width/height). * information about the bounding element (bounding box and width/height).
* @param {!Object} divSize An object containing information about the size * @param {!Blockly.utils.Size} divSize An object containing information about
* of the DropDownDiv (width & height). * the size of the DropDownDiv (width & height).
* @return {Object} Various final metrics, including rendered positions * @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics,
* for drop-down and arrow. * including rendered positions for drop-down and arrow.
* @private * @private
*/ */
Blockly.DropDownDiv.getPositionAboveMetrics_ = function( 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. * Get the metrics for positioning the div at the top of the page.
* @param {number} sourceX Desired origin point x, in absolute px. * @param {number} sourceX Desired origin point x, in absolute px.
* @param {!Object} boundsInfo An object containing size information about the * @param {!Blockly.DropDownDiv.BoundsInfo} boundsInfo An object containing size
* bounding element (bounding box and width/height). * information about the bounding element (bounding box and width/height).
* @param {!Object} divSize An object containing information about the size * @param {!Blockly.utils.Size} divSize An object containing information about
* of the DropDownDiv (width & height). * the size of the DropDownDiv (width & height).
* @return {Object} Various final metrics, including rendered positions * @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics,
* for drop-down and arrow. * including rendered positions for drop-down and arrow.
* @private * @private
*/ */
Blockly.DropDownDiv.getPositionTopOfPageMetrics_ = function( Blockly.DropDownDiv.getPositionTopOfPageMetrics_ = function(
@@ -525,6 +559,9 @@ Blockly.DropDownDiv.getPositionTopOfPageMetrics_ = function(
initialY : 0, initialY : 0,
finalX: xCoords.divX, // X position remains constant during animation. finalX: xCoords.divX, // X position remains constant during animation.
finalY: 0, // Y position remains constant during animation. finalY: 0, // Y position remains constant during animation.
arrowAtTop: null,
arrowX: null,
arrowY: null,
arrowVisible: false arrowVisible: false
}; };
}; };
@@ -653,7 +690,8 @@ Blockly.DropDownDiv.hideWithoutAnimation = function() {
Blockly.utils.dom.removeClass(div, Blockly.DropDownDiv.themeClassName_); Blockly.utils.dom.removeClass(div, Blockly.DropDownDiv.themeClassName_);
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; var dy = finalY - initialY;
div.style.transform = 'translate(' + dx + 'px,' + dy + 'px)'; div.style.transform = 'translate(' + dx + 'px,' + dy + 'px)';
return metrics.arrowAtTop; return !!metrics.arrowAtTop;
}; };
/** /**
@@ -720,7 +758,7 @@ Blockly.DropDownDiv.repositionForWindowResize = function() {
// it. // it.
if (Blockly.DropDownDiv.owner_) { if (Blockly.DropDownDiv.owner_) {
var field = /** @type {!Blockly.Field} */ (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_ ? var bBox = Blockly.DropDownDiv.positionToField_ ?
Blockly.DropDownDiv.getScaledBboxOfField_(field) : Blockly.DropDownDiv.getScaledBboxOfField_(field) :
Blockly.DropDownDiv.getScaledBboxOfBlock_(block); Blockly.DropDownDiv.getScaledBboxOfBlock_(block);

View File

@@ -19,6 +19,7 @@ goog.require('Blockly.Events.BlockChange');
goog.require('Blockly.Gesture'); goog.require('Blockly.Gesture');
goog.require('Blockly.utils'); goog.require('Blockly.utils');
goog.require('Blockly.utils.dom'); goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.Rect');
goog.require('Blockly.utils.Size'); goog.require('Blockly.utils.Size');
goog.require('Blockly.utils.style'); goog.require('Blockly.utils.style');
goog.require('Blockly.utils.userAgent'); 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 * Returns the bounding box of the rendered field, accounting for workspace
* scaling. * scaling.
* @return {!Object} An object with top, bottom, left, and right in pixels * @return {!Blockly.utils.Rect} An object with top, bottom, left, and right in
* relative to the top left corner of the page (window coordinates). * pixels relative to the top left corner of the page (window coordinates).
* @package * @package
*/ */
Blockly.Field.prototype.getScaledBBox = function() { Blockly.Field.prototype.getScaledBBox = function() {
@@ -750,12 +751,12 @@ Blockly.Field.prototype.getScaledBBox = function() {
var scaledWidth = bBox.width; var scaledWidth = bBox.width;
var scaledHeight = bBox.height; var scaledHeight = bBox.height;
} }
return { return new Blockly.utils.Rect(
top: xy.y, xy.y,
bottom: xy.y + scaledHeight, xy.y + scaledHeight,
left: xy.x, xy.x,
right: xy.x + scaledWidth xy.x + scaledWidth
}; );
}; };
/** /**

View File

@@ -23,6 +23,7 @@ goog.require('Blockly.constants');
goog.require('Blockly.utils.colour'); goog.require('Blockly.utils.colour');
goog.require('Blockly.utils.Coordinate'); goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.global'); goog.require('Blockly.utils.global');
goog.require('Blockly.utils.Rect');
goog.require('Blockly.utils.string'); goog.require('Blockly.utils.string');
goog.require('Blockly.utils.style'); goog.require('Blockly.utils.style');
goog.require('Blockly.utils.userAgent'); 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 * Get the position of the current viewport in window coordinates. This takes
* scroll into account. * scroll into account.
* @return {!Object} An object containing window width, height, and scroll * @return {!Blockly.utils.Rect} An object containing window width, height, and
* position in window coordinates. * scroll position in window coordinates.
* @package * @package
*/ */
Blockly.utils.getViewportBBox = function() { Blockly.utils.getViewportBBox = function() {
// Pixels, in window coordinates. // Pixels, in window coordinates.
var scrollOffset = Blockly.utils.style.getViewportPageOffset(); var scrollOffset = Blockly.utils.style.getViewportPageOffset();
return { return new Blockly.utils.Rect(
right: document.documentElement.clientWidth + scrollOffset.x, scrollOffset.y,
bottom: document.documentElement.clientHeight + scrollOffset.y, document.documentElement.clientHeight + scrollOffset.y,
top: scrollOffset.y, scrollOffset.x,
left: scrollOffset.x document.documentElement.clientWidth + scrollOffset.x
}; );
}; };
/** /**

View File

@@ -20,6 +20,9 @@ goog.provide('Blockly.WidgetDiv');
goog.require('Blockly.utils.style'); goog.require('Blockly.utils.style');
goog.requireType('Blockly.utils.Rect');
goog.requireType('Blockly.utils.Size');
/** /**
* The object currently using this container. * The object currently using this container.
@@ -80,10 +83,11 @@ Blockly.WidgetDiv.show = function(newOwner, rtl, dispose) {
var div = Blockly.WidgetDiv.DIV; var div = Blockly.WidgetDiv.DIV;
div.style.direction = rtl ? 'rtl' : 'ltr'; div.style.direction = rtl ? 'rtl' : 'ltr';
div.style.display = 'block'; div.style.display = 'block';
var mainWorkspace =
/** @type {!Blockly.WorkspaceSvg} */ (Blockly.getMainWorkspace());
Blockly.WidgetDiv.rendererClassName_ = Blockly.WidgetDiv.rendererClassName_ =
Blockly.getMainWorkspace().getRenderer().getClassName(); mainWorkspace.getRenderer().getClassName();
Blockly.WidgetDiv.themeClassName_ = Blockly.WidgetDiv.themeClassName_ = mainWorkspace.getTheme().getClassName();
Blockly.getMainWorkspace().getTheme().getClassName();
Blockly.utils.dom.addClass(div, Blockly.WidgetDiv.rendererClassName_); Blockly.utils.dom.addClass(div, Blockly.WidgetDiv.rendererClassName_);
Blockly.utils.dom.addClass(div, Blockly.WidgetDiv.themeClassName_); 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.utils.dom.removeClass(div, Blockly.WidgetDiv.themeClassName_);
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 * The widget should be placed adjacent to but not overlapping the anchor
* rectangle. The preferred position is directly below and aligned to the left * rectangle. The preferred position is directly below and aligned to the left
* (LTR) or right (RTL) side of the anchor. * (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. * in window coordinates.
* @param {!Object} anchorBBox The bounding rectangle of the anchor, in window * @param {!Blockly.utils.Size} widgetSize The size of the widget that is inside
* coordinates. * 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 * @param {boolean} rtl Whether the workspace is in RTL mode. This determines
* horizontal alignment. * horizontal alignment.
* @package * @package
@@ -180,10 +185,10 @@ Blockly.WidgetDiv.positionWithAnchor = function(viewportBBox, anchorBBox,
/** /**
* Calculate an x position (in window coordinates) such that the widget will not * Calculate an x position (in window coordinates) such that the widget will not
* be offscreen on the right or left. * 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. * 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 * @param {Blockly.utils.Size} widgetSize The dimensions of the widget inside the
* widget div. * widget div.
* @param {boolean} rtl Whether the Blockly workspace is in RTL mode. * @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 * Calculate a y position (in window coordinates) such that the widget will not
* be offscreen on the top or bottom. * 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. * 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 * @param {Blockly.utils.Size} widgetSize The dimensions of the widget inside the
* widget div. * widget div.
* @return {number} A valid y-coordinate for the top left corner of the widget * @return {number} A valid y-coordinate for the top left corner of the widget