diff --git a/core/flyout_base.js b/core/flyout_base.js index 18e99d88e..fd8272214 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -752,3 +752,62 @@ Blockly.Flyout.prototype.reflow = function() { Blockly.Flyout.prototype.isScrollable = function() { return this.scrollbar_ ? this.scrollbar_.isVisible() : false; }; + +/** + * Copy a block from the flyout to the workspace and position it correctly. + * @param {!Blockly.Block} oldBlock The flyout block to copy. + * @return {!Blockly.Block} The new block in the main workspace. + * @private + */ +Blockly.Flyout.prototype.placeNewBlock_ = function(oldBlock) { + var targetWorkspace = this.targetWorkspace_; + var svgRootOld = oldBlock.getSvgRoot(); + if (!svgRootOld) { + throw 'oldBlock is not rendered.'; + } + + // Create the new block by cloning the block in the flyout (via XML). + var xml = Blockly.Xml.blockToDom(oldBlock); + // The target workspace would normally resize during domToBlock, which will + // lead to weird jumps. Save it for terminateDrag. + targetWorkspace.setResizesEnabled(false); + + // Using domToBlock instead of domToWorkspace means that the new block will be + // placed at position (0, 0) in main workspace units. + var block = Blockly.Xml.domToBlock(xml, targetWorkspace); + var svgRootNew = block.getSvgRoot(); + if (!svgRootNew) { + throw 'block is not rendered.'; + } + + // The offset in pixels between the main workspace's origin and the upper left + // corner of the injection div. + var mainOffsetPixels = targetWorkspace.getOriginOffsetInPixels(); + + // The offset in pixels between the flyout workspace's origin and the upper + // left corner of the injection div. + var flyoutOffsetPixels = this.workspace_.getOriginOffsetInPixels(); + + // The position of the old block in flyout workspace coordinates. + var oldBlockPosWs = oldBlock.getRelativeToSurfaceXY(); + + // The position of the old block in pixels relative to the flyout + // workspace's origin. + var oldBlockPosPixels = oldBlockPosWs.scale(this.workspace_.scale); + + // The position of the old block in pixels relative to the upper left corner + // of the injection div. + var oldBlockOffsetPixels = goog.math.Coordinate.sum(flyoutOffsetPixels, + oldBlockPosPixels); + + // The position of the old block in pixels relative to the origin of the + // main workspace. + var finalOffsetPixels = goog.math.Coordinate.difference(oldBlockOffsetPixels, + mainOffsetPixels); + + // The position of the old block in main workspace coordinates. + var finalOffsetMainWs = finalOffsetPixels.scale(1 / targetWorkspace.scale); + + block.moveBy(finalOffsetMainWs.x, finalOffsetMainWs.y); + return block; +}; diff --git a/core/flyout_horizontal.js b/core/flyout_horizontal.js index 0c1c8e52c..e09d95512 100644 --- a/core/flyout_horizontal.js +++ b/core/flyout_horizontal.js @@ -322,82 +322,6 @@ Blockly.HorizontalFlyout.prototype.isDragTowardWorkspace = function( return false; }; -/** - * Copy a block from the flyout to the workspace and position it correctly. - * @param {!Blockly.Block} originBlock The flyout block to copy.. - * @return {!Blockly.Block} The new block in the main workspace. - * @private - */ -Blockly.HorizontalFlyout.prototype.placeNewBlock_ = function(originBlock) { - var targetWorkspace = this.targetWorkspace_; - var svgRootOld = originBlock.getSvgRoot(); - if (!svgRootOld) { - throw 'originBlock is not rendered.'; - } - // Figure out where the original block is on the screen, relative to the upper - // left corner of the main workspace. - if (targetWorkspace.isMutator) { - var xyOld = this.workspace_.getSvgXY(/** @type {!Element} */ (svgRootOld)); - } else { - var xyOld = Blockly.utils.getInjectionDivXY_(svgRootOld); - } - - // Take into account that the flyout might have been scrolled horizontally - // (separately from the main workspace). - // Generally a no-op in vertical mode but likely to happen in horizontal - // mode. - var scrollX = this.workspace_.scrollX; - var scale = this.workspace_.scale; - xyOld.x += scrollX / scale - scrollX; - - // Take into account that the flyout might have been scrolled vertically - // (separately from the main workspace). - // Generally a no-op in horizontal mode but likely to happen in vertical - // mode. - var scrollY = this.workspace_.scrollY; - scale = this.workspace_.scale; - xyOld.y += scrollY / scale - scrollY; - // If the flyout is on the bottom, (0, 0) in the flyout is offset to be below - // (0, 0) in the main workspace. Add an offset to take that into account. - if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) { - scrollY = targetWorkspace.getMetrics().viewHeight - this.height_; - scale = targetWorkspace.scale; - xyOld.y += scrollY / scale - scrollY; - } - - // 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, targetWorkspace); - var svgRootNew = block.getSvgRoot(); - if (!svgRootNew) { - throw 'block is not rendered.'; - } - // Figure out where the new block got placed on the screen, relative to the - // upper left corner of the workspace. This may not be the same as the - // original block because the flyout's origin may not be the same as the - // main workspace's origin. - if (targetWorkspace.isMutator) { - var xyNew = targetWorkspace.getSvgXY(/* @type {!Element} */(svgRootNew)); - } else { - var xyNew = Blockly.utils.getInjectionDivXY_(svgRootNew); - } - - // Scale the scroll (getSvgXY_ did not do this). - xyNew.x += - targetWorkspace.scrollX / targetWorkspace.scale - targetWorkspace.scrollX; - xyNew.y += - targetWorkspace.scrollY / targetWorkspace.scale - targetWorkspace.scrollY; - // If the flyout is collapsible and the workspace can't be scrolled. - if (targetWorkspace.toolbox_ && !targetWorkspace.scrollbar) { - xyNew.x += targetWorkspace.toolbox_.getWidth() / targetWorkspace.scale; - xyNew.y += targetWorkspace.toolbox_.getHeight() / targetWorkspace.scale; - } - - // Move the new block to where the old block is. - block.moveBy(xyOld.x - xyNew.x, xyOld.y - xyNew.y); - return block; -}; - /** * Return the deletion rectangle for this flyout in viewport coordinates. * @return {goog.math.Rect} Rectangle in which to delete. diff --git a/core/flyout_vertical.js b/core/flyout_vertical.js index f1595a0a3..19d7a4330 100644 --- a/core/flyout_vertical.js +++ b/core/flyout_vertical.js @@ -300,88 +300,6 @@ Blockly.VerticalFlyout.prototype.isDragTowardWorkspace = function( return false; }; -/** - * Copy a block from the flyout to the workspace and position it correctly. - * @param {!Blockly.Block} originBlock The flyout block to copy. - * @return {!Blockly.Block} The new block in the main workspace. - * @private - */ -Blockly.VerticalFlyout.prototype.placeNewBlock_ = function(originBlock) { - var targetWorkspace = this.targetWorkspace_; - var svgRootOld = originBlock.getSvgRoot(); - if (!svgRootOld) { - throw 'originBlock is not rendered.'; - } - // Figure out where the original block is on the screen, relative to the upper - // left corner of the main workspace. - if (targetWorkspace.isMutator) { - var xyOld = this.workspace_.getSvgXY(/** @type {!Element} */ (svgRootOld)); - } else { - var xyOld = Blockly.utils.getInjectionDivXY_(svgRootOld); - } - - // Take into account that the flyout might have been scrolled horizontally - // (separately from the main workspace). - // Generally a no-op in vertical mode but likely to happen in horizontal - // mode. - var scrollX = this.workspace_.scrollX; - var scale = this.workspace_.scale; - xyOld.x += scrollX / scale - scrollX; - - var targetMetrics = targetWorkspace.getMetrics(); - - // If the flyout is on the right side, (0, 0) in the flyout is offset to - // the right of (0, 0) in the main workspace. Add an offset to take that - // into account. - var scrollX = 0; - if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) { - scrollX = targetMetrics.viewWidth - this.width_; - // Scale the scroll (getSvgXY_ did not do this). - xyOld.x += scrollX / scale - scrollX; - } - - // Take into account that the flyout might have been scrolled vertically - // (separately from the main workspace). - // Generally a no-op in horizontal mode but likely to happen in vertical - // mode. - var scrollY = this.workspace_.scrollY; - scale = this.workspace_.scale; - xyOld.y += scrollY / scale - scrollY; - - // 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, targetWorkspace); - var svgRootNew = block.getSvgRoot(); - if (!svgRootNew) { - throw 'block is not rendered.'; - } - // Figure out where the new block got placed on the screen, relative to the - // upper left corner of the workspace. This may not be the same as the - // original block because the flyout's origin may not be the same as the - // main workspace's origin. - if (targetWorkspace.isMutator) { - var xyNew = targetWorkspace.getSvgXY(/* @type {!Element} */(svgRootNew)); - } else { - var xyNew = Blockly.utils.getInjectionDivXY_(svgRootNew); - } - - // Scale the scroll (getSvgXY_ did not do this). - xyNew.x += - targetWorkspace.scrollX / targetWorkspace.scale - targetWorkspace.scrollX; - xyNew.y += - targetWorkspace.scrollY / targetWorkspace.scale - targetWorkspace.scrollY; - - // If the flyout is collapsible and the workspace can't be scrolled. - if (targetWorkspace.toolbox_ && !targetWorkspace.scrollbar) { - xyNew.x += targetWorkspace.toolbox_.getWidth() / targetWorkspace.scale; - xyNew.y += targetWorkspace.toolbox_.getHeight() / targetWorkspace.scale; - } - - // Move the new block to where the old block is. - block.moveBy(xyOld.x - xyNew.x, xyOld.y - xyNew.y); - return block; -}; - /** * Return the deletion rectangle for this flyout in viewport coordinates. * @return {goog.math.Rect} Rectangle in which to delete. diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 27477591d..62df5d146 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -315,6 +315,18 @@ Blockly.WorkspaceSvg.prototype.getSvgXY = function(element) { return new goog.math.Coordinate(x, y); }; +/** + * Return the position of the workspace origin relative to the injection div + * origin in pixels. + * The workspace origin is where a block would render at position (0, 0). + * It is not the upper left corner of the workspace SVG. + * @return {!goog.math.Coordinate} Offset in pixels. + * @package + */ +Blockly.WorkspaceSvg.prototype.getOriginOffsetInPixels = function() { + return Blockly.utils.getInjectionDivXY_(this.svgBlockCanvas_); +}; + /** * Save resize handler data so we can delete it later in dispose. * @param {!Array.} handler Data that can be passed to unbindEvent_.