From 79f91266accc76b66a439613be04676d9eb11803 Mon Sep 17 00:00:00 2001 From: Katelyn Mann Date: Tue, 12 Apr 2016 14:59:58 -0700 Subject: [PATCH 01/12] Take into account height of the hat for hat blocks. Also, position hat blocks correctly in the flyout/toolbox. This fixes #330. --- core/block_render_svg.js | 15 +++++++++++++-- core/flyout.js | 8 ++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/core/block_render_svg.js b/core/block_render_svg.js index 92ba5270f..362821264 100644 --- a/core/block_render_svg.js +++ b/core/block_render_svg.js @@ -76,11 +76,18 @@ Blockly.BlockSvg.CORNER_RADIUS = 8; * @const */ Blockly.BlockSvg.START_HAT = false; +/** + * Height of the top hat. + * @const + */ +Blockly.BlockSvg.START_HAT_HEIGHT = 15; /** * Path of the top hat's curve. * @const */ -Blockly.BlockSvg.START_HAT_PATH = 'c 30,-15 70,-15 100,0'; +Blockly.BlockSvg.START_HAT_PATH = 'c 30,-' + + Blockly.BlockSvg.START_HAT_HEIGHT +' 70,-' + + Blockly.BlockSvg.START_HAT_HEIGHT +' 100,0'; /** * Path of the top hat's curve's highlight in LTR. * @const @@ -480,6 +487,9 @@ Blockly.BlockSvg.prototype.renderCompute_ = function(iconWidth) { */ Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) { this.startHat_ = false; + // Reset the height to zero and let the rendering process add in + // portions of the block height as it goes. (e.g. hats, inputs, etc.) + this.height = 0; // Should the top and bottom left corners be rounded or square? if (this.outputConnection) { this.squareTopLeftCorner_ = true; @@ -497,6 +507,7 @@ Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) { // No output or previous connection. this.squareTopLeftCorner_ = true; this.startHat_ = true; + this.height += Blockly.BlockSvg.START_HAT_HEIGHT; inputRows.rightEdge = Math.max(inputRows.rightEdge, 100); } var nextBlock = this.getNextBlock(); @@ -860,7 +871,7 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, highlightSteps, */ Blockly.BlockSvg.prototype.renderDrawBottom_ = function(steps, highlightSteps, connectionsXY, cursorY) { - this.height = cursorY + 1; // Add one for the shadow. + this.height += cursorY + 1; // Add one for the shadow. if (this.nextConnection) { steps.push('H', (Blockly.BlockSvg.NOTCH_WIDTH + (this.RTL ? 0.5 : - 0.5)) + ' ' + Blockly.BlockSvg.NOTCH_PATH_RIGHT); diff --git a/core/flyout.js b/core/flyout.js index 84bcbb810..5258feec8 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -510,6 +510,14 @@ Blockly.Flyout.prototype.reflow = function() { var blockXY = block.getRelativeToSurfaceXY(); block.flyoutRect_.setAttribute('x', this.RTL ? blockXY.x - blockHW.width + tab : blockXY.x - tab); + // For hat blocks we want to shift them down by the hat height + // since the y coordinate is the corner, not the top of the + // hat. + var hatOffset = + block.startHat_ ? Blockly.BlockSvg.START_HAT_HEIGHT : 0; + if (hatOffset != 0) { + block.moveBy(0, hatOffset); + } block.flyoutRect_.setAttribute('y', blockXY.y); } } From b69ab459bd78ae36625de7fd7552535cfb6aa169 Mon Sep 17 00:00:00 2001 From: Katelyn Mann Date: Mon, 18 Apr 2016 16:53:32 -0700 Subject: [PATCH 02/12] Fix bad merge. Commit 36e0764e577397d451c2270861c3ecbff36891a6 and e39a0b70692f89c83493371f141e82010f7c6e7c collided on their use/removal of the event param to one of the zoom functions. --- core/workspace_svg.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 0194925c9..3aeb66915 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -1061,6 +1061,7 @@ Blockly.WorkspaceSvg.prototype.zoomReset = function(e) { this.scrollCenter_(); // This event has been handled. Don't start a workspace drag. e.stopPropagation(); + e.preventDefault(); }; /** @@ -1105,9 +1106,6 @@ Blockly.WorkspaceSvg.prototype.setScale = function(newScale) { // No toolbox, resize flyout. this.flyout_.reflow(); } - // This event has been handled. Don't start a workspace drag. - e.stopPropagation(); - e.preventDefault(); }; /** From 6d58f7959f2aef0a3a6b43bf8cbc2832ad299872 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Tue, 19 Apr 2016 20:50:26 -0700 Subject: [PATCH 03/12] Don't fire click when editing a field. Issue #336 part 1. --- core/block_svg.js | 8 +++++++- core/blockly.js | 15 ++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/core/block_svg.js b/core/block_svg.js index efcacab5c..e5d191b1a 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -127,6 +127,11 @@ Blockly.BlockSvg.prototype.initSvg = function() { * Select this block. Highlight it visually. */ Blockly.BlockSvg.prototype.select = function() { + if (this.isShadow() && this.getParent()) { + // Shadow blocks should not be selected. + this.getParent().select(); + return; + } if (Blockly.selected == this) { return; } @@ -563,7 +568,8 @@ Blockly.BlockSvg.prototype.onMouseDown_ = function(e) { * @private */ Blockly.BlockSvg.prototype.onMouseUp_ = function(e) { - if (Blockly.dragMode_ != Blockly.DRAG_FREE) { + if (Blockly.dragMode_ != Blockly.DRAG_FREE && + !Blockly.WidgetDiv.isVisible()) { Blockly.Events.fire( new Blockly.Events.Ui(this, 'click', undefined, undefined)); } diff --git a/core/blockly.js b/core/blockly.js index 365255099..aee1a20ca 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -233,15 +233,12 @@ Blockly.onKeyDown_ = function(e) { Blockly.hideChaff(); } else if (e.keyCode == 8 || e.keyCode == 46) { // Delete or backspace. - try { - if (Blockly.selected && Blockly.selected.isDeletable()) { - deleteBlock = true; - } - } finally { - // Stop the browser from going back to the previous page. - // Use a finally so that any error in delete code above doesn't disappear - // from the console when the page rolls back. - e.preventDefault(); + // Stop the browser from going back to the previous page. + // Do this first to prevent an error in the delete code from resulting in + // data loss. + e.preventDefault(); + if (Blockly.selected && Blockly.selected.isDeletable()) { + deleteBlock = true; } } else if (e.altKey || e.ctrlKey || e.metaKey) { if (Blockly.selected && From 7e96a60da79e218802d4b3533e60aecdbe90e312 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Tue, 19 Apr 2016 21:14:23 -0700 Subject: [PATCH 04/12] Use goog.math.Coordinate instead of separate X and Y properties. --- core/block_svg.js | 4 ++-- core/bubble.js | 57 +++++++++++++++++++++-------------------------- core/comment.js | 5 ++--- core/icon.js | 35 +++++++++++++---------------- core/mutator.js | 6 ++--- core/warning.js | 5 ++--- 6 files changed, 49 insertions(+), 63 deletions(-) diff --git a/core/block_svg.js b/core/block_svg.js index e5d191b1a..b2fab52cb 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -853,8 +853,8 @@ Blockly.BlockSvg.prototype.onMouseMove_ = function(e) { // Drag all the nested bubbles. for (var i = 0; i < this.draggedBubbles_.length; i++) { var commentData = this.draggedBubbles_[i]; - commentData.bubble.setIconLocation(commentData.x + dx, - commentData.y + dy); + commentData.bubble.setIconLocation( + new goog.math.Coordinate(commentData.x + dx, commentData.y + dy)); } // Check to see if any of this block's connections are within range of diff --git a/core/bubble.js b/core/bubble.js index 144ff05a2..b5a99f40b 100644 --- a/core/bubble.js +++ b/core/bubble.js @@ -34,18 +34,17 @@ goog.require('goog.userAgent'); /** * Class for UI bubble. - * @param {!Blockly.Workspace} workspace The workspace on which to draw the + * @param {!Blockly.WorkspaceSvg} workspace The workspace on which to draw the * bubble. * @param {!Element} content SVG content for the bubble. * @param {Element} shape SVG element to avoid eclipsing. - * @param {number} anchorX Absolute horizontal position of bubbles anchor point. - * @param {number} anchorY Absolute vertical position of bubbles anchor point. + * @param {!goog.math.Coodinate} anchorXY Absolute position of bubble's anchor + * point. * @param {?number} bubbleWidth Width of bubble, or null if not resizable. * @param {?number} bubbleHeight Height of bubble, or null if not resizable. * @constructor */ -Blockly.Bubble = function(workspace, content, shape, - anchorX, anchorY, +Blockly.Bubble = function(workspace, content, shape, anchorXY, bubbleWidth, bubbleHeight) { this.workspace_ = workspace; this.content_ = content; @@ -60,7 +59,7 @@ Blockly.Bubble = function(workspace, content, shape, var canvas = workspace.getBubbleCanvas(); canvas.appendChild(this.createDom_(content, !!(bubbleWidth && bubbleHeight))); - this.setAnchorLocation(anchorX, anchorY); + this.setAnchorLocation(anchorXY); if (!bubbleWidth || !bubbleHeight) { var bBox = /** @type {SVGLocatable} */ (this.content_).getBBox(); bubbleWidth = bBox.width + 2 * Blockly.Bubble.BORDER_WIDTH; @@ -145,16 +144,11 @@ Blockly.Bubble.unbindDragEvents_ = function() { Blockly.Bubble.prototype.rendered_ = false; /** - * Absolute X coordinate of anchor point. + * Absolute coordinate of anchor point. + * @type {goog.math.Coordinate} * @private */ -Blockly.Bubble.prototype.anchorX_ = 0; - -/** - * Absolute Y coordinate of anchor point. - * @private - */ -Blockly.Bubble.prototype.anchorY_ = 0; +Blockly.Bubble.prototype.anchorXY_ = null; /** * Relative X coordinate of bubble with respect to the anchor's centre. @@ -360,12 +354,10 @@ Blockly.Bubble.prototype.promote_ = function() { /** * Notification that the anchor has moved. * Update the arrow and bubble accordingly. - * @param {number} x Absolute horizontal location. - * @param {number} y Absolute vertical location. + * @param {!goog.math.Coordinate} xy Absolute location. */ -Blockly.Bubble.prototype.setAnchorLocation = function(x, y) { - this.anchorX_ = x; - this.anchorY_ = y; +Blockly.Bubble.prototype.setAnchorLocation = function(xy) { + this.anchorXY_ = xy; if (this.rendered_) { this.positionBubble_(); } @@ -383,31 +375,32 @@ Blockly.Bubble.prototype.layoutBubble_ = function() { var metrics = this.workspace_.getMetrics(); metrics.viewWidth /= this.workspace_.scale; metrics.viewLeft /= this.workspace_.scale; + var anchorX = this.anchorXY_.x; if (this.workspace_.RTL) { - if (this.anchorX_ - metrics.viewLeft - relativeLeft - this.width_ < + if (anchorX - metrics.viewLeft - relativeLeft - this.width_ < Blockly.Scrollbar.scrollbarThickness) { // Slide the bubble right until it is onscreen. - relativeLeft = this.anchorX_ - metrics.viewLeft - this.width_ - + relativeLeft = anchorX - metrics.viewLeft - this.width_ - Blockly.Scrollbar.scrollbarThickness; - } else if (this.anchorX_ - metrics.viewLeft - relativeLeft > + } else if (anchorX - metrics.viewLeft - relativeLeft > metrics.viewWidth) { // Slide the bubble left until it is onscreen. - relativeLeft = this.anchorX_ - metrics.viewLeft - metrics.viewWidth; + relativeLeft = anchorX - metrics.viewLeft - metrics.viewWidth; } } else { - if (this.anchorX_ + relativeLeft < metrics.viewLeft) { + if (anchorX + relativeLeft < metrics.viewLeft) { // Slide the bubble right until it is onscreen. - relativeLeft = metrics.viewLeft - this.anchorX_; + relativeLeft = metrics.viewLeft - anchorX; } else if (metrics.viewLeft + metrics.viewWidth < - this.anchorX_ + relativeLeft + this.width_ + + anchorX + relativeLeft + this.width_ + Blockly.BlockSvg.SEP_SPACE_X + Blockly.Scrollbar.scrollbarThickness) { // Slide the bubble left until it is onscreen. - relativeLeft = metrics.viewLeft + metrics.viewWidth - this.anchorX_ - + relativeLeft = metrics.viewLeft + metrics.viewWidth - anchorX - this.width_ - Blockly.Scrollbar.scrollbarThickness; } } - if (this.anchorY_ + relativeTop < metrics.viewTop) { + if (this.anchorXY_.y + relativeTop < metrics.viewTop) { // Slide the bubble below the block. var bBox = /** @type {SVGLocatable} */ (this.shape_).getBBox(); relativeTop = bBox.height; @@ -421,13 +414,13 @@ Blockly.Bubble.prototype.layoutBubble_ = function() { * @private */ Blockly.Bubble.prototype.positionBubble_ = function() { - var left; + var left = this.anchorXY_.x; if (this.workspace_.RTL) { - left = this.anchorX_ - this.relativeLeft_ - this.width_; + left -= this.relativeLeft_ + this.width_; } else { - left = this.anchorX_ + this.relativeLeft_; + left += this.relativeLeft_; } - var top = this.relativeTop_ + this.anchorY_; + var top = this.relativeTop_ + this.anchorXY_.y; this.bubbleGroup_.setAttribute('transform', 'translate(' + left + ',' + top + ')'); }; diff --git a/core/comment.js b/core/comment.js index 06c9bae77..d1e4363bf 100644 --- a/core/comment.js +++ b/core/comment.js @@ -180,10 +180,9 @@ Blockly.Comment.prototype.setVisible = function(visible) { if (visible) { // Create the bubble. this.bubble_ = new Blockly.Bubble( - /** @type {!Blockly.Workspace} */ (this.block_.workspace), + /** @type {!Blockly.WorkspaceSvg} */ (this.block_.workspace), this.createEditor_(), this.block_.svgPath_, - this.iconX_, this.iconY_, - this.width_, this.height_); + this.iconXY_, this.width_, this.height_); this.bubble_.registerResizeEvent(this, this.resizeBubble_); this.updateColour(); } else { diff --git a/core/icon.js b/core/icon.js index 5793a5b7a..9d2cbeaad 100644 --- a/core/icon.js +++ b/core/icon.js @@ -27,6 +27,7 @@ goog.provide('Blockly.Icon'); goog.require('goog.dom'); +goog.require('goog.math.Coordinate'); /** @@ -56,16 +57,11 @@ Blockly.Icon.prototype.SIZE = 17; Blockly.Icon.prototype.bubble_ = null; /** - * Absolute X coordinate of icon's center. + * Absolute coordinate of icon's center. + * @type {goog.math.Coordinate} * @private */ -Blockly.Icon.prototype.iconX_ = 0; - -/** - * Absolute Y coordinate of icon's centre. - * @private - */ -Blockly.Icon.prototype.iconY_ = 0; +Blockly.Icon.prototype.iconXY_ = null; /** * Create the icon on the block. @@ -176,14 +172,12 @@ Blockly.Icon.prototype.renderIcon = function(cursorX) { /** * Notification that the icon has moved. Update the arrow accordingly. - * @param {number} x Absolute horizontal location. - * @param {number} y Absolute vertical location. + * @param {!goog.math.Coordinate} xy Absolute location. */ -Blockly.Icon.prototype.setIconLocation = function(x, y) { - this.iconX_ = x; - this.iconY_ = y; +Blockly.Icon.prototype.setIconLocation = function(xy) { + this.iconXY_ = xy; if (this.isVisible()) { - this.bubble_.setAnchorLocation(x, y); + this.bubble_.setAnchorLocation(xy); } }; @@ -195,17 +189,18 @@ Blockly.Icon.prototype.computeIconLocation = function() { // Find coordinates for the centre of the icon and update the arrow. var blockXY = this.block_.getRelativeToSurfaceXY(); var iconXY = Blockly.getRelativeXY_(this.iconGroup_); - var newX = blockXY.x + iconXY.x + this.SIZE / 2; - var newY = blockXY.y + iconXY.y + this.SIZE / 2; - if (newX !== this.iconX_ || newY !== this.iconY_) { - this.setIconLocation(newX, newY); + var newXY = new goog.math.Coordinate( + blockXY.x + iconXY.x + this.SIZE / 2, + blockXY.y + iconXY.y + this.SIZE / 2); + if (!goog.math.Coordinate.equals(this.getIconLocation(), newXY)) { + this.setIconLocation(newXY); } }; /** * Returns the center of the block's icon relative to the surface. - * @return {!Object} Object with x and y properties. + * @return {!goog.math.Coordinate} Object with x and y properties. */ Blockly.Icon.prototype.getIconLocation = function() { - return {x: this.iconX_, y: this.iconY_}; + return this.iconXY_; }; diff --git a/core/mutator.js b/core/mutator.js index ba7c5c27a..a53babfd9 100644 --- a/core/mutator.js +++ b/core/mutator.js @@ -202,9 +202,9 @@ Blockly.Mutator.prototype.setVisible = function(visible) { new Blockly.Events.Ui(this.block_, 'mutatorOpen', !visible, visible)); if (visible) { // Create the bubble. - this.bubble_ = new Blockly.Bubble(this.block_.workspace, - this.createEditor_(), this.block_.svgPath_, - this.iconX_, this.iconY_, null, null); + this.bubble_ = new Blockly.Bubble( + /** @type {!Blockly.WorkspaceSvg} */ (this.block_.workspace), + this.createEditor_(), this.block_.svgPath_, this.iconXY_, null, null); var tree = this.workspace_.options.languageTree; if (tree) { this.workspace_.flyout_.init(this.workspace_); diff --git a/core/warning.js b/core/warning.js index 4ec8bf771..bffbf06df 100644 --- a/core/warning.js +++ b/core/warning.js @@ -111,9 +111,8 @@ Blockly.Warning.prototype.setVisible = function(visible) { // Create the bubble to display all warnings. var paragraph = Blockly.Warning.textToDom_(this.getText()); this.bubble_ = new Blockly.Bubble( - /** @type {!Blockly.Workspace} */ (this.block_.workspace), - paragraph, this.block_.svgPath_, - this.iconX_, this.iconY_, null, null); + /** @type {!Blockly.WorkspaceSvg} */ (this.block_.workspace), + paragraph, this.block_.svgPath_, this.iconXY_, null, null); if (this.block_.RTL) { // Right-align the paragraph. // This cannot be done until the bubble is rendered on screen. From c785cabe4057559cdb2abc5159fcd3a1691469d1 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Tue, 19 Apr 2016 23:24:42 -0700 Subject: [PATCH 05/12] Use more coordinates. --- core/block_svg.js | 9 ++++----- core/bubble.js | 9 +++++---- core/connection.js | 8 +++----- core/connection_db.js | 12 +++++------- core/events.js | 2 ++ core/workspace_svg.js | 29 +++++++++-------------------- 6 files changed, 28 insertions(+), 41 deletions(-) diff --git a/core/block_svg.js b/core/block_svg.js index b2fab52cb..c58eade11 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -537,7 +537,7 @@ Blockly.BlockSvg.prototype.onMouseDown_ = function(e) { Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED); this.dragStartXY_ = this.getRelativeToSurfaceXY(); - this.workspace.startDrag(e, this.dragStartXY_.x, this.dragStartXY_.y); + this.workspace.startDrag(e, this.dragStartXY_); Blockly.dragMode_ = Blockly.DRAG_STICKY; Blockly.BlockSvg.onMouseUpWrapper_ = Blockly.bindEvent_(document, @@ -845,8 +845,7 @@ Blockly.BlockSvg.prototype.onMouseMove_ = function(e) { } if (Blockly.dragMode_ == Blockly.DRAG_FREE) { // Unrestricted dragging. - var dx = oldXY.x - this.dragStartXY_.x; - var dy = oldXY.y - this.dragStartXY_.y; + var dxy = goog.math.Coordinate.difference(oldXY, this.dragStartXY_); var group = this.getSvgRoot(); group.translate_ = 'translate(' + newXY.x + ',' + newXY.y + ')'; group.setAttribute('transform', group.translate_ + group.skew_); @@ -854,7 +853,7 @@ Blockly.BlockSvg.prototype.onMouseMove_ = function(e) { for (var i = 0; i < this.draggedBubbles_.length; i++) { var commentData = this.draggedBubbles_[i]; commentData.bubble.setIconLocation( - new goog.math.Coordinate(commentData.x + dx, commentData.y + dy)); + goog.math.Coordinate.sum(commentData, dxy)); } // Check to see if any of this block's connections are within range of @@ -865,7 +864,7 @@ Blockly.BlockSvg.prototype.onMouseMove_ = function(e) { var radiusConnection = Blockly.SNAP_RADIUS; for (var i = 0; i < myConnections.length; i++) { var myConnection = myConnections[i]; - var neighbour = myConnection.closest(radiusConnection, dx, dy); + var neighbour = myConnection.closest(radiusConnection, dxy); if (neighbour.connection) { closestConnection = neighbour.connection; localConnection = myConnection; diff --git a/core/bubble.js b/core/bubble.js index b5a99f40b..379b9b1b1 100644 --- a/core/bubble.js +++ b/core/bubble.js @@ -29,6 +29,7 @@ goog.provide('Blockly.Bubble'); goog.require('Blockly.Workspace'); goog.require('goog.dom'); goog.require('goog.math'); +goog.require('goog.math.Coordinate'); goog.require('goog.userAgent'); @@ -263,9 +264,9 @@ Blockly.Bubble.prototype.bubbleMouseDown_ = function(e) { // Left-click (or middle click) Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED); - this.workspace_.startDrag(e, + this.workspace_.startDrag(e, new goog.math.Coordinate( this.workspace_.RTL ? -this.relativeLeft_ : this.relativeLeft_, - this.relativeTop_); + this.relativeTop_)); Blockly.Bubble.onMouseUpWrapper_ = Blockly.bindEvent_(document, 'mouseup', this, Blockly.Bubble.unbindDragEvents_); @@ -306,8 +307,8 @@ Blockly.Bubble.prototype.resizeMouseDown_ = function(e) { // Left-click (or middle click) Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED); - this.workspace_.startDrag(e, - this.workspace_.RTL ? -this.width_ : this.width_, this.height_); + this.workspace_.startDrag(e, new goog.math.Coordinate( + this.workspace_.RTL ? -this.width_ : this.width_, this.height_)); Blockly.Bubble.onMouseUpWrapper_ = Blockly.bindEvent_(document, 'mouseup', this, Blockly.Bubble.unbindDragEvents_); diff --git a/core/connection.js b/core/connection.js index 8f32d746b..25adcf49d 100644 --- a/core/connection.js +++ b/core/connection.js @@ -664,16 +664,14 @@ Blockly.Connection.prototype.tighten_ = function() { /** * Find the closest compatible connection to this connection. * @param {number} maxLimit The maximum radius to another connection. - * @param {number} dx Horizontal offset between this connection's location - * in the database and the current location (as a result of dragging). - * @param {number} dy Vertical offset between this connection's location + * @param {!goog.math.Coordinate} dxy Offset between this connection's location * in the database and the current location (as a result of dragging). * @return {!{connection: ?Blockly.Connection, radius: number}} Contains two * properties:' connection' which is either another connection or null, * and 'radius' which is the distance. */ -Blockly.Connection.prototype.closest = function(maxLimit, dx, dy) { - return this.dbOpposite_.searchForClosest(this, maxLimit, dx, dy); +Blockly.Connection.prototype.closest = function(maxLimit, dxy) { + return this.dbOpposite_.searchForClosest(this, maxLimit, dxy); }; /** diff --git a/core/connection_db.js b/core/connection_db.js index 580ae353d..f0b69f7f5 100644 --- a/core/connection_db.js +++ b/core/connection_db.js @@ -226,16 +226,14 @@ Blockly.ConnectionDB.prototype.isInYRange_ = function(index, baseY, maxRadius) { * @param {!Blockly.Connection} conn The connection searching for a compatible * mate. * @param {number} maxRadius The maximum radius to another connection. - * @param {number} dx Horizontal offset between this connection's location - * in the database and the current location (as a result of dragging). - * @param {number} dy Vertical offset between this connection's location + * @param {!goog.math.Coordinate} dxy Offset between this connection's location * in the database and the current location (as a result of dragging). * @return {!{connection: ?Blockly.Connection, radius: number}} Contains two * properties:' connection' which is either another connection or null, * and 'radius' which is the distance. */ -Blockly.ConnectionDB.prototype.searchForClosest = function(conn, maxRadius, dx, - dy) { +Blockly.ConnectionDB.prototype.searchForClosest = function(conn, maxRadius, + dxy) { // Don't bother. if (!this.length) { return {connection: null, radius: maxRadius}; @@ -245,8 +243,8 @@ Blockly.ConnectionDB.prototype.searchForClosest = function(conn, maxRadius, dx, var baseY = conn.y_; var baseX = conn.x_; - conn.x_ = baseX + dx; - conn.y_ = baseY + dy; + conn.x_ = baseX + dxy.x; + conn.y_ = baseY + dxy.y; // findPositionForConnection finds an index for insertion, which is always // after any block with the same y index. We want to search both forward diff --git a/core/events.js b/core/events.js index ee401c99e..3de8bc5c3 100644 --- a/core/events.js +++ b/core/events.js @@ -26,6 +26,8 @@ goog.provide('Blockly.Events'); +goog.require('goog.math.Coordinate'); + /** * Group ID for new events. Grouped events are indivisible. diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 3aeb66915..4b9570ce3 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -107,18 +107,11 @@ Blockly.WorkspaceSvg.prototype.startScrollX = 0; Blockly.WorkspaceSvg.prototype.startScrollY = 0; /** - * Horizontal distance from mouse to object being dragged. - * @type {number} + * Distance from mouse to object being dragged. + * @type {goog.math.Coordinate} * @private */ -Blockly.WorkspaceSvg.prototype.dragDeltaX_ = 0; - -/** - * Vertical distance from mouse to object being dragged. - * @type {number} - * @private - */ -Blockly.WorkspaceSvg.prototype.dragDeltaY_ = 0; +Blockly.WorkspaceSvg.prototype.dragDeltaXY_ = null; /** * Current scale. @@ -494,8 +487,8 @@ Blockly.WorkspaceSvg.prototype.paste = function(xmlBlock) { // Check for blocks in snap range to any of its connections. var connections = block.getConnections_(false); for (var i = 0, connection; connection = connections[i]; i++) { - var neighbour = - connection.closest(Blockly.SNAP_RADIUS, blockX, blockY); + var neighbour = connection.closest(Blockly.SNAP_RADIUS, + new goog.math.Coordinate(blockX, blockY)); if (neighbour.connection) { collide = true; break; @@ -620,17 +613,15 @@ Blockly.WorkspaceSvg.prototype.onMouseDown_ = function(e) { /** * Start tracking a drag of an object on this workspace. * @param {!Event} e Mouse down event. - * @param {number} x Starting horizontal location of object. - * @param {number} y Starting vertical location of object. + * @param {!goog.math.Coordinate} xy Starting location of object. */ -Blockly.WorkspaceSvg.prototype.startDrag = function(e, x, y) { +Blockly.WorkspaceSvg.prototype.startDrag = function(e, xy) { // Record the starting offset between the bubble's location and the mouse. var point = Blockly.mouseToSvg(e, this.getParentSvg()); // Fix scale of mouse event. point.x /= this.scale; point.y /= this.scale; - this.dragDeltaX_ = x - point.x; - this.dragDeltaY_ = y - point.y; + this.dragDeltaXY_ = goog.math.Coordinate.difference(xy, point); }; /** @@ -643,9 +634,7 @@ Blockly.WorkspaceSvg.prototype.moveDrag = function(e) { // Fix scale of mouse event. point.x /= this.scale; point.y /= this.scale; - var x = this.dragDeltaX_ + point.x; - var y = this.dragDeltaY_ + point.y; - return new goog.math.Coordinate(x, y); + return goog.math.Coordinate.sum(this.dragDeltaXY_, point); }; /** From 52eb6adcc23d1ad376cd87cc9c1c27886087fcf4 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Wed, 20 Apr 2016 05:03:05 -0700 Subject: [PATCH 06/12] Add click events to non-closing flyout blocks. Issue #337. --- core/flyout.js | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/core/flyout.js b/core/flyout.js index f960bafba..d4930be8b 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -28,6 +28,7 @@ goog.provide('Blockly.Flyout'); goog.require('Blockly.Block'); goog.require('Blockly.Comment'); +goog.require('Blockly.Events.Ui'); goog.require('Blockly.WorkspaceSvg'); goog.require('goog.dom'); goog.require('goog.events'); @@ -436,16 +437,18 @@ Blockly.Flyout.prototype.show = function(xmlList) { if (this.autoClose) { this.listeners_.push(Blockly.bindEvent_(root, 'mousedown', null, this.createBlockFunc_(block))); + this.listeners_.push(Blockly.bindEvent_(rect, 'mousedown', null, + this.createBlockFunc_(block))); } else { this.listeners_.push(Blockly.bindEvent_(root, 'mousedown', null, this.blockMouseDown_(block))); + this.listeners_.push(Blockly.bindEvent_(rect, 'mousedown', null, + this.blockMouseDown_(block))); } this.listeners_.push(Blockly.bindEvent_(root, 'mouseover', block, block.addSelect)); this.listeners_.push(Blockly.bindEvent_(root, 'mouseout', block, block.removeSelect)); - this.listeners_.push(Blockly.bindEvent_(rect, 'mousedown', null, - this.createBlockFunc_(block))); this.listeners_.push(Blockly.bindEvent_(rect, 'mouseover', block, block.addSelect)); this.listeners_.push(Blockly.bindEvent_(rect, 'mouseout', block, @@ -544,7 +547,7 @@ Blockly.Flyout.prototype.blockMouseDown_ = function(block) { Blockly.Flyout.startBlock_ = block; Blockly.Flyout.startFlyout_ = flyout; Blockly.Flyout.onMouseUpWrapper_ = Blockly.bindEvent_(document, - 'mouseup', this, Blockly.terminateDrag_); + 'mouseup', this, flyout.onMouseUp_); Blockly.Flyout.onMouseMoveBlockWrapper_ = Blockly.bindEvent_(document, 'mousemove', this, flyout.onMouseMoveBlock_); } @@ -574,6 +577,23 @@ Blockly.Flyout.prototype.onMouseDown_ = function(e) { e.stopPropagation(); }; +/** + * Handle a mouse-up anywhere in the SVG pane. Is only registered when a + * block is clicked. We can't use mouseUp on the block since a fast-moving + * cursor can briefly escape the block before it catches up. + * @param {!Event} e Mouse up event. + * @private + */ +Blockly.Flyout.prototype.onMouseUp_ = function(e) { + if (Blockly.dragMode_ != Blockly.DRAG_FREE && + !Blockly.WidgetDiv.isVisible()) { + Blockly.Events.fire( + new Blockly.Events.Ui(Blockly.Flyout.startBlock_, 'click', + undefined, undefined)); + } + Blockly.terminateDrag_(); +}; + /** * Handle a mouse-move to vertically drag the flyout. * @param {!Event} e Mouse move event. From 4bc24fe3e14bff661458b5b6d3781a36856fdccb Mon Sep 17 00:00:00 2001 From: rachel-fenichel Date: Wed, 20 Apr 2016 11:26:01 -0700 Subject: [PATCH 07/12] Constrain scrollbar value in set() and decompose resize() into horizontal and vertical modes. --- core/scrollbar.js | 139 +++++++++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 58 deletions(-) diff --git a/core/scrollbar.js b/core/scrollbar.js index 42d053cc0..6c2a4ff31 100644 --- a/core/scrollbar.js +++ b/core/scrollbar.js @@ -270,69 +270,91 @@ Blockly.Scrollbar.prototype.resize = function(opt_metrics) { * .absoluteLeft: Left-edge of view. */ if (this.horizontal_) { - var outerLength = hostMetrics.viewWidth - 1; - if (this.pair_) { - // Shorten the scrollbar to make room for the corner square. - outerLength -= Blockly.Scrollbar.scrollbarThickness; - } else { - // Only show the scrollbar if needed. - // Ideally this would also apply to scrollbar pairs, but that's a bigger - // headache (due to interactions with the corner square). - this.setVisible(outerLength < hostMetrics.contentWidth); - } - this.ratio_ = outerLength / hostMetrics.contentWidth; - if (this.ratio_ === -Infinity || this.ratio_ === Infinity || - isNaN(this.ratio_)) { - this.ratio_ = 0; - } - var innerLength = hostMetrics.viewWidth * this.ratio_; - var innerOffset = (hostMetrics.viewLeft - hostMetrics.contentLeft) * - this.ratio_; - this.svgKnob_.setAttribute('width', Math.max(0, innerLength)); - this.xCoordinate = hostMetrics.absoluteLeft + 0.5; - if (this.pair_ && this.workspace_.RTL) { - this.xCoordinate += hostMetrics.absoluteLeft + - Blockly.Scrollbar.scrollbarThickness; - } - this.yCoordinate = hostMetrics.absoluteTop + hostMetrics.viewHeight - - Blockly.Scrollbar.scrollbarThickness - 0.5; - this.svgGroup_.setAttribute('transform', - 'translate(' + this.xCoordinate + ',' + this.yCoordinate + ')'); - this.svgBackground_.setAttribute('width', Math.max(0, outerLength)); - this.svgKnob_.setAttribute('x', this.constrainKnob_(innerOffset)); + this.resizeHorizontal_(hostMetrics); } else { - var outerLength = hostMetrics.viewHeight - 1; - if (this.pair_) { - // Shorten the scrollbar to make room for the corner square. - outerLength -= Blockly.Scrollbar.scrollbarThickness; - } else { - // Only show the scrollbar if needed. - this.setVisible(outerLength < hostMetrics.contentHeight); - } - this.ratio_ = outerLength / hostMetrics.contentHeight; - if (this.ratio_ === -Infinity || this.ratio_ === Infinity || - isNaN(this.ratio_)) { - this.ratio_ = 0; - } - var innerLength = hostMetrics.viewHeight * this.ratio_; - var innerOffset = (hostMetrics.viewTop - hostMetrics.contentTop) * - this.ratio_; - this.svgKnob_.setAttribute('height', Math.max(0, innerLength)); - this.xCoordinate = hostMetrics.absoluteLeft + 0.5; - if (!this.workspace_.RTL) { - this.xCoordinate += hostMetrics.viewWidth - - Blockly.Scrollbar.scrollbarThickness - 1; - } - this.yCoordinate = hostMetrics.absoluteTop + 0.5; - this.svgGroup_.setAttribute('transform', - 'translate(' + this.xCoordinate + ',' + this.yCoordinate + ')'); - this.svgBackground_.setAttribute('height', Math.max(0, outerLength)); - this.svgKnob_.setAttribute('y', this.constrainKnob_(innerOffset)); + this.resizeVertical_(hostMetrics); } // Resizing may have caused some scrolling. this.onScroll_(); }; +/** + * 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. + * @private + */ +Blockly.Scrollbar.prototype.resizeHorizontal_ = function(hostMetrics) { + var outerLength = hostMetrics.viewWidth - 1; + if (this.pair_) { + // Shorten the scrollbar to make room for the corner square. + outerLength -= Blockly.Scrollbar.scrollbarThickness; + } else { + // Only show the scrollbar if needed. + // Ideally this would also apply to scrollbar pairs, but that's a bigger + // headache (due to interactions with the corner square). + this.setVisible(outerLength < hostMetrics.contentWidth); + } + this.ratio_ = outerLength / hostMetrics.contentWidth; + if (this.ratio_ === -Infinity || this.ratio_ === Infinity || + isNaN(this.ratio_)) { + this.ratio_ = 0; + } + var innerLength = hostMetrics.viewWidth * this.ratio_; + var innerOffset = (hostMetrics.viewLeft - hostMetrics.contentLeft) * + this.ratio_; + this.svgKnob_.setAttribute('width', Math.max(0, innerLength)); + this.xCoordinate = hostMetrics.absoluteLeft + 0.5; + if (this.pair_ && this.workspace_.RTL) { + this.xCoordinate += hostMetrics.absoluteLeft + + Blockly.Scrollbar.scrollbarThickness; + } + this.yCoordinate = hostMetrics.absoluteTop + hostMetrics.viewHeight - + Blockly.Scrollbar.scrollbarThickness - 0.5; + this.svgGroup_.setAttribute('transform', + 'translate(' + this.xCoordinate + ',' + this.yCoordinate + ')'); + this.svgBackground_.setAttribute('width', Math.max(0, outerLength)); + this.svgKnob_.setAttribute('x', this.constrainKnob_(innerOffset)); +}; + +/** + * 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. + * @private + */ +Blockly.Scrollbar.prototype.resizeVertical_ = function(hostMetrics) { + var outerLength = hostMetrics.viewHeight - 1; + if (this.pair_) { + // Shorten the scrollbar to make room for the corner square. + outerLength -= Blockly.Scrollbar.scrollbarThickness; + } else { + // Only show the scrollbar if needed. + this.setVisible(outerLength < hostMetrics.contentHeight); + } + this.ratio_ = outerLength / hostMetrics.contentHeight; + if (this.ratio_ === -Infinity || this.ratio_ === Infinity || + isNaN(this.ratio_)) { + this.ratio_ = 0; + } + var innerLength = hostMetrics.viewHeight * this.ratio_; + var innerOffset = (hostMetrics.viewTop - hostMetrics.contentTop) * + this.ratio_; + this.svgKnob_.setAttribute('height', Math.max(0, innerLength)); + this.xCoordinate = hostMetrics.absoluteLeft + 0.5; + if (!this.workspace_.RTL) { + this.xCoordinate += hostMetrics.viewWidth - + Blockly.Scrollbar.scrollbarThickness - 1; + } + this.yCoordinate = hostMetrics.absoluteTop + 0.5; + this.svgGroup_.setAttribute('transform', + 'translate(' + this.xCoordinate + ',' + this.yCoordinate + ')'); + this.svgBackground_.setAttribute('height', Math.max(0, outerLength)); + this.svgKnob_.setAttribute('y', this.constrainKnob_(innerOffset)); +}; + /** * Create all the DOM elements required for a scrollbar. * The resulting widget is not sized. @@ -533,8 +555,9 @@ Blockly.Scrollbar.prototype.onScroll_ = function() { * @param {number} value The distance from the top/left end of the bar. */ Blockly.Scrollbar.prototype.set = function(value) { + var constrainedValue = this.constrainKnob_(value * this.ratio_); // Move the scrollbar slider. - this.svgKnob_.setAttribute(this.horizontal_ ? 'x' : 'y', value * this.ratio_); + this.svgKnob_.setAttribute(this.horizontal_ ? 'x' : 'y', constrainedValue); this.onScroll_(); }; From c8f98a7dedec6bfb5ec7d95c06e7f0b106619a13 Mon Sep 17 00:00:00 2001 From: rachel-fenichel Date: Wed, 20 Apr 2016 14:14:53 -0700 Subject: [PATCH 08/12] Decompose functions in flyout. Does not change behaviour. --- core/flyout.js | 250 ++++++++++++++++++++++++++++++------------------- 1 file changed, 156 insertions(+), 94 deletions(-) diff --git a/core/flyout.js b/core/flyout.js index d4930be8b..47d7a8bdb 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -264,20 +264,8 @@ Blockly.Flyout.prototype.position = function() { if (this.RTL) { edgeWidth *= -1; } - var path = ['M ' + (this.RTL ? this.width_ : 0) + ',0']; - path.push('h', edgeWidth); - path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, - this.RTL ? 0 : 1, - this.RTL ? -this.CORNER_RADIUS : this.CORNER_RADIUS, - this.CORNER_RADIUS); - path.push('v', Math.max(0, metrics.viewHeight - this.CORNER_RADIUS * 2)); - path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, - this.RTL ? 0 : 1, - this.RTL ? this.CORNER_RADIUS : -this.CORNER_RADIUS, - this.CORNER_RADIUS); - path.push('h', -edgeWidth); - path.push('z'); - this.svgBackground_.setAttribute('d', path.join(' ')); + + this.setBackgroundPath_(edgeWidth, metrics.viewHeight); var x = metrics.absoluteLeft; if (this.RTL) { @@ -296,6 +284,37 @@ Blockly.Flyout.prototype.position = function() { } }; +/** + * Create and set the path for the visible boundaries of the flyout. + * @param {number} width The width of the flyout, not including the + * rounded corners. + * @param {number} height The height of the flyout, not including + * rounded corners. + * @private + */ +Blockly.Flyout.prototype.setBackgroundPath_ = function(width, height) { + var path = ['M ' + (this.RTL ? this.width_ : 0) + ',0']; + // Top. + path.push('h', width); + // Rounded corner. + path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, + this.RTL ? 0 : 1, + this.RTL ? -this.CORNER_RADIUS : this.CORNER_RADIUS, + this.CORNER_RADIUS); + // Side closest to the workspace. + path.push('v', Math.max(0, height - this.CORNER_RADIUS * 2)); + // Rounded corner. + path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, + this.RTL ? 0 : 1, + this.RTL ? this.CORNER_RADIUS : -this.CORNER_RADIUS, + this.CORNER_RADIUS); + // Bottom. + path.push('h', -width); + // Side away from the workspace. + path.push('z'); + this.svgBackground_.setAttribute('d', path.join(' ')); +}; + /** * Scroll the flyout to the top. */ @@ -363,18 +382,7 @@ Blockly.Flyout.prototype.hide = function() { */ Blockly.Flyout.prototype.show = function(xmlList) { this.hide(); - // Delete any blocks from a previous showing. - var blocks = this.workspace_.getTopBlocks(false); - for (var i = 0, block; block = blocks[i]; i++) { - if (block.workspace == this.workspace_) { - block.dispose(false, false); - } - } - // Delete any background buttons from a previous showing. - for (var i = 0, rect; rect = this.buttons_[i]; i++) { - goog.dom.removeNode(rect); - } - this.buttons_.length = 0; + this.clearOldBlocks_(); if (xmlList == Blockly.Variables.NAME_TYPE) { // Special category for variables. @@ -406,6 +414,38 @@ Blockly.Flyout.prototype.show = function(xmlList) { } } + this.layoutBlocks_(blocks, gaps, margin); + + // IE 11 is an incompetant browser that fails to fire mouseout events. + // When the mouse is over the background, deselect all blocks. + var deselectAll = function(e) { + var blocks = this.workspace_.getTopBlocks(false); + for (var i = 0, block; block = blocks[i]; i++) { + block.removeSelect(); + } + }; + this.listeners_.push(Blockly.bindEvent_(this.svgBackground_, 'mouseover', + this, deselectAll)); + + this.width_ = 0; + this.reflow(); + + this.filterForCapacity_(); + + // Fire a resize event to update the flyout's scrollbar. + Blockly.fireUiEventNow(window, 'resize'); + this.reflowWrapper_ = this.reflow.bind(this); + this.workspace_.addChangeListener(this.reflowWrapper_); +}; + +/** + * Lay out the blocks in the flyout. + * @param {!Array} blocks The blocks to lay out. + * @param {!Array} gaps The visible gaps between blocks. + * @param {number} margin The margin around the edges of the flyout. + * @private + */ +Blockly.Flyout.prototype.layoutBlocks_ = function(blocks, gaps, margin) { // Lay out the blocks vertically. var cursorY = margin; for (var i = 0, block; block = blocks[i]; i++) { @@ -434,47 +474,57 @@ Blockly.Flyout.prototype.show = function(xmlList) { block.flyoutRect_ = rect; this.buttons_[i] = rect; - if (this.autoClose) { - this.listeners_.push(Blockly.bindEvent_(root, 'mousedown', null, - this.createBlockFunc_(block))); - this.listeners_.push(Blockly.bindEvent_(rect, 'mousedown', null, - this.createBlockFunc_(block))); - } else { - this.listeners_.push(Blockly.bindEvent_(root, 'mousedown', null, - this.blockMouseDown_(block))); - this.listeners_.push(Blockly.bindEvent_(rect, 'mousedown', null, - this.blockMouseDown_(block))); - } - this.listeners_.push(Blockly.bindEvent_(root, 'mouseover', block, - block.addSelect)); - this.listeners_.push(Blockly.bindEvent_(root, 'mouseout', block, - block.removeSelect)); - this.listeners_.push(Blockly.bindEvent_(rect, 'mouseover', block, - block.addSelect)); - this.listeners_.push(Blockly.bindEvent_(rect, 'mouseout', block, - block.removeSelect)); + this.addBlockListeners_(root, block, rect); } +}; - // IE 11 is an incompetant browser that fails to fire mouseout events. - // When the mouse is over the background, deselect all blocks. - var deselectAll = function(e) { - var blocks = this.workspace_.getTopBlocks(false); - for (var i = 0, block; block = blocks[i]; i++) { - block.removeSelect(); +/** + * Delete blocks and background buttons from a previous showing of the flyout. + * @private + */ +Blockly.Flyout.prototype.clearOldBlocks_ = function() { + // Delete any blocks from a previous showing. + var blocks = this.workspace_.getTopBlocks(false); + for (var i = 0, block; block = blocks[i]; i++) { + if (block.workspace == this.workspace_) { + block.dispose(false, false); } - }; - this.listeners_.push(Blockly.bindEvent_(this.svgBackground_, 'mouseover', - this, deselectAll)); + } + // Delete any background buttons from a previous showing. + for (var j = 0, rect; rect = this.buttons_[j]; j++) { + goog.dom.removeNode(rect); + } + this.buttons_.length = 0; +}; - this.width_ = 0; - this.reflow(); - - this.filterForCapacity_(); - - // Fire a resize event to update the flyout's scrollbar. - Blockly.fireUiEventNow(window, 'resize'); - this.reflowWrapper_ = this.reflow.bind(this); - this.workspace_.addChangeListener(this.reflowWrapper_); +/** + * Add listeners to a block that has been added to the flyout. + * @param {!Element} root The root node of the SVG group the block is in. + * @param {!Blockly.Block} block The block to add listeners for. + * @param {!Element} rect The invisible rectangle under the block that acts as + * a button for that block. + * @private + */ +Blockly.Flyout.prototype.addBlockListeners_ = function(root, block, rect) { + if (this.autoClose) { + this.listeners_.push(Blockly.bindEvent_(root, 'mousedown', null, + this.createBlockFunc_(block))); + this.listeners_.push(Blockly.bindEvent_(rect, 'mousedown', null, + this.createBlockFunc_(block))); + } else { + this.listeners_.push(Blockly.bindEvent_(root, 'mousedown', null, + this.blockMouseDown_(block))); + this.listeners_.push(Blockly.bindEvent_(rect, 'mousedown', null, + this.blockMouseDown_(block))); + } + this.listeners_.push(Blockly.bindEvent_(root, 'mouseover', block, + block.addSelect)); + this.listeners_.push(Blockly.bindEvent_(root, 'mouseout', block, + block.removeSelect)); + this.listeners_.push(Blockly.bindEvent_(rect, 'mouseover', block, + block.addSelect)); + this.listeners_.push(Blockly.bindEvent_(rect, 'mouseout', block, + block.removeSelect)); }; /** @@ -656,37 +706,7 @@ Blockly.Flyout.prototype.createBlockFunc_ = function(originBlock) { return; } Blockly.Events.disable(); - // Create the new block by cloning the block in the flyout (via XML). - var xml = Blockly.Xml.blockToDom(originBlock); - var block = Blockly.Xml.domToBlock(xml, workspace); - // Place it in the same spot as the flyout copy. - var svgRootOld = originBlock.getSvgRoot(); - if (!svgRootOld) { - throw 'originBlock is not rendered.'; - } - var xyOld = Blockly.getSvgXY_(svgRootOld, workspace); - // Scale the scroll (getSvgXY_ did not do this). - if (flyout.RTL) { - var width = workspace.getMetrics().viewWidth - flyout.width_; - xyOld.x += width / workspace.scale - width; - } else { - xyOld.x += flyout.workspace_.scrollX / flyout.workspace_.scale - - flyout.workspace_.scrollX; - } - xyOld.y += flyout.workspace_.scrollY / flyout.workspace_.scale - - flyout.workspace_.scrollY; - var svgRootNew = block.getSvgRoot(); - if (!svgRootNew) { - throw 'block is not rendered.'; - } - var xyNew = Blockly.getSvgXY_(svgRootNew, workspace); - // Scale the scroll (getSvgXY_ did not do this). - xyNew.x += workspace.scrollX / workspace.scale - workspace.scrollX; - xyNew.y += workspace.scrollY / workspace.scale - workspace.scrollY; - if (workspace.toolbox_ && !workspace.scrollbar) { - xyNew.x += workspace.toolbox_.width / workspace.scale; - } - block.moveBy(xyOld.x - xyNew.x, xyOld.y - xyNew.y); + var block = flyout.placeNewBlock_(originBlock, workspace); Blockly.Events.enable(); if (Blockly.Events.isEnabled()) { Blockly.Events.setGroup(true); @@ -704,6 +724,48 @@ Blockly.Flyout.prototype.createBlockFunc_ = function(originBlock) { }; }; +/** + * Copy a block from the flyout to the workspace and position it correctly. + * @param {!Blockly.Block} originBlock The flyout block to copy. + * @param {!Blockly.Workspace} workspace The main workspace. + * @return {!Blockly.Block} The new block in the main workspace. + * @private + */ +Blockly.Flyout.prototype.placeNewBlock_ = function(originBlock, workspace) { + // Create the new block by cloning the block in the flyout (via XML). + var xml = Blockly.Xml.blockToDom(originBlock); + var block = Blockly.Xml.domToBlock(xml, workspace); + // Place it in the same spot as the flyout copy. + var svgRootOld = originBlock.getSvgRoot(); + if (!svgRootOld) { + throw 'originBlock is not rendered.'; + } + var xyOld = Blockly.getSvgXY_(svgRootOld, workspace); + // Scale the scroll (getSvgXY_ did not do this). + if (this.RTL) { + var width = workspace.getMetrics().viewWidth - this.width_; + xyOld.x += width / workspace.scale - width; + } else { + xyOld.x += this.workspace_.scrollX / this.workspace_.scale - + this.workspace_.scrollX; + } + xyOld.y += this.workspace_.scrollY / this.workspace_.scale - + this.workspace_.scrollY; + var svgRootNew = block.getSvgRoot(); + if (!svgRootNew) { + throw 'block is not rendered.'; + } + var xyNew = Blockly.getSvgXY_(svgRootNew, workspace); + // Scale the scroll (getSvgXY_ did not do this). + xyNew.x += workspace.scrollX / workspace.scale - workspace.scrollX; + xyNew.y += workspace.scrollY / workspace.scale - workspace.scrollY; + if (workspace.toolbox_ && !workspace.scrollbar) { + xyNew.x += workspace.toolbox_.width / workspace.scale; + } + block.moveBy(xyOld.x - xyNew.x, xyOld.y - xyNew.y); + return block; +}; + /** * Filter the blocks on the flyout to disable the ones that are above the * capacity limit. From 703ac981dab8020aa4d0410566616d28d349ac56 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Wed, 20 Apr 2016 16:44:13 -0700 Subject: [PATCH 09/12] Routine recompile. --- blockly_compressed.js | 689 ++++++++++++++++++++-------------------- blockly_uncompressed.js | 8 +- core/flyout.js | 2 +- 3 files changed, 349 insertions(+), 350 deletions(-) diff --git a/blockly_compressed.js b/blockly_compressed.js index 56202c9fe..9f68ad4c7 100644 --- a/blockly_compressed.js +++ b/blockly_compressed.js @@ -8,8 +8,8 @@ goog.VALID_MODULE_RE_=/^[a-zA-Z_$][a-zA-Z0-9._$]*$/;goog.module=function(a){if(! goog.module.get=function(a){return goog.module.getInternal_(a)};goog.module.getInternal_=function(a){if(!COMPILED)return goog.isProvided_(a)?a in goog.loadedModules_?goog.loadedModules_[a]:goog.getObjectByName(a):null};goog.moduleLoaderState_=null;goog.isInModuleLoader_=function(){return null!=goog.moduleLoaderState_}; goog.module.declareLegacyNamespace=function(){if(!COMPILED&&!goog.isInModuleLoader_())throw Error("goog.module.declareLegacyNamespace must be called from within a goog.module");if(!COMPILED&&!goog.moduleLoaderState_.moduleName)throw Error("goog.module must be called prior to goog.module.declareLegacyNamespace.");goog.moduleLoaderState_.declareLegacyNamespace=!0}; goog.setTestOnly=function(a){if(goog.DISALLOW_TEST_ONLY_CODE)throw a=a||"",Error("Importing test-only code into non-debug environment"+(a?": "+a:"."));};goog.forwardDeclare=function(a){};COMPILED||(goog.isProvided_=function(a){return a in goog.loadedModules_||!goog.implicitNamespaces_[a]&&goog.isDefAndNotNull(goog.getObjectByName(a))},goog.implicitNamespaces_={"goog.module":!0}); -goog.getObjectByName=function(a,b){for(var c=a.split("."),d=b||goog.global,f;f=c.shift();)if(goog.isDefAndNotNull(d[f]))d=d[f];else return null;return d};goog.globalize=function(a,b){var c=b||goog.global,d;for(d in a)c[d]=a[d]}; -goog.addDependency=function(a,b,c,d){if(goog.DEPENDENCIES_ENABLED){var f;a=a.replace(/\\/g,"/");var g=goog.dependencies_;d&&"boolean"!==typeof d||(d=d?{module:"goog"}:{});for(var h=0;f=b[h];h++)g.nameToPath[f]=a,g.pathIsModule[a]="goog"==d.module;for(d=0;b=c[d];d++)a in g.requires||(g.requires[a]={}),g.requires[a][b]=!0}};goog.ENABLE_DEBUG_LOADER=!0;goog.logToConsole_=function(a){goog.global.console&&goog.global.console.error(a)}; +goog.getObjectByName=function(a,b){for(var c=a.split("."),d=b||goog.global,e;e=c.shift();)if(goog.isDefAndNotNull(d[e]))d=d[e];else return null;return d};goog.globalize=function(a,b){var c=b||goog.global,d;for(d in a)c[d]=a[d]}; +goog.addDependency=function(a,b,c,d){if(goog.DEPENDENCIES_ENABLED){var e;a=a.replace(/\\/g,"/");var f=goog.dependencies_;d&&"boolean"!==typeof d||(d=d?{module:"goog"}:{});for(var g=0;e=b[g];g++)f.nameToPath[e]=a,f.pathIsModule[a]="goog"==d.module;for(d=0;b=c[d];d++)a in f.requires||(f.requires[a]={}),f.requires[a][b]=!0}};goog.ENABLE_DEBUG_LOADER=!0;goog.logToConsole_=function(a){goog.global.console&&goog.global.console.error(a)}; goog.require=function(a){if(!COMPILED){goog.ENABLE_DEBUG_LOADER&&goog.IS_OLD_IE_&&goog.maybeProcessDeferredDep_(a);if(goog.isProvided_(a))return goog.isInModuleLoader_()?goog.module.getInternal_(a):null;if(goog.ENABLE_DEBUG_LOADER){var b=goog.getPathFromDeps_(a);if(b)return goog.writeScripts_(b),null}a="goog.require could not find: "+a;goog.logToConsole_(a);throw Error(a);}};goog.basePath="";goog.nullFunction=function(){}; goog.abstractMethod=function(){throw Error("unimplemented abstract method");};goog.addSingletonGetter=function(a){a.getInstance=function(){if(a.instance_)return a.instance_;goog.DEBUG&&(goog.instantiatedSingletons_[goog.instantiatedSingletons_.length]=a);return a.instance_=new a}};goog.instantiatedSingletons_=[];goog.LOAD_MODULE_USING_EVAL=!0;goog.SEAL_MODULE_EXPORTS=goog.DEBUG;goog.loadedModules_={};goog.DEPENDENCIES_ENABLED=!COMPILED&&goog.ENABLE_DEBUG_LOADER; goog.DEPENDENCIES_ENABLED&&(goog.dependencies_={pathIsModule:{},nameToPath:{},requires:{},visited:{},written:{},deferred:{}},goog.inHtmlDocument_=function(){var a=goog.global.document;return null!=a&&"write"in a},goog.findBasePath_=function(){if(goog.isDef(goog.global.CLOSURE_BASE_PATH))goog.basePath=goog.global.CLOSURE_BASE_PATH;else if(goog.inHtmlDocument_())for(var a=goog.global.document.getElementsByTagName("SCRIPT"),b=a.length-1;0<=b;--b){var c=a[b].src,d=c.lastIndexOf("?"),d=-1==d?c.length: @@ -19,8 +19,8 @@ goog.maybeProcessDeferredPath_(goog.basePath+a))},goog.isDeferredModule_=functio goog.dependencies_.deferred[a];delete goog.dependencies_.deferred[a];goog.globalEval(b)}},goog.loadModuleFromUrl=function(a){goog.retrieveAndExecModule_(a)},goog.loadModule=function(a){var b=goog.moduleLoaderState_;try{goog.moduleLoaderState_={moduleName:void 0,declareLegacyNamespace:!1};var c;if(goog.isFunction(a))c=a.call(goog.global,{});else if(goog.isString(a))c=goog.loadModuleFromSource_.call(goog.global,a);else throw Error("Invalid module definition");var d=goog.moduleLoaderState_.moduleName; if(!goog.isString(d)||!d)throw Error('Invalid module name "'+d+'"');goog.moduleLoaderState_.declareLegacyNamespace?goog.constructNamespace_(d,c):goog.SEAL_MODULE_EXPORTS&&Object.seal&&Object.seal(c);goog.loadedModules_[d]=c}finally{goog.moduleLoaderState_=b}},goog.loadModuleFromSource_=function(a){eval(a);return{}},goog.writeScriptSrcNode_=function(a){goog.global.document.write('