Skip to content

Correctly place blocks when the flyout and main workspace are at diferent zoom levels. #1000

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 36 additions & 69 deletions core/flyout_vertical.js
Original file line number Diff line number Diff line change
Expand Up @@ -616,93 +616,60 @@ Blockly.VerticalFlyout.prototype.isDragTowardWorkspace = function(currentDragDel

/**
* Copy a block from the flyout to the workspace and position it correctly.
* @param {!Blockly.Block} originBlock The flyout block to copy.
* @param {!Blockly.Block} oldBlock The flyout block to copy.
* @return {!Blockly.Block} The new block in the main workspace.
* @private
*/
Blockly.VerticalFlyout.prototype.placeNewBlock_ = function(originBlock) {
Blockly.VerticalFlyout.prototype.placeNewBlock_ = function(oldBlock) {
var targetWorkspace = this.targetWorkspace_;
var svgRootOld = originBlock.getSvgRoot();
var svgRootOld = oldBlock.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.
// In what coordinates? Pixels?
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;
}

// The main workspace has 0,0 at the top inside corner of the toolbox.
// Need to take that into account now that the flyout is offset from there in
// both directions.
if (this.parentToolbox_) {
// TODO (fenichel): fix these offsets to correctly deal with scaling
// changes.
xyOld.y += (this.parentToolbox_.getHeight()) /
targetWorkspace.scale -
(this.parentToolbox_.getHeight());
var xOffset = this.parentToolbox_.getWidth() / targetWorkspace.scale -
this.parentToolbox_.getWidth();
if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) {
xyOld.x += xOffset;
} else {
xyOld.x -= xOffset;
}
}

// Take into account that the flyout might have been scrolled vertically
// (separately from the main workspace).
var scrollY = this.workspace_.scrollY;
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 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.';
}
// 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.
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;

// Move the new block to where the old block is.
var dx = ((scale * xyOld.x) - (targetWorkspace.scale * xyNew.x)) /
targetWorkspace.scale;
var dy = ((scale * xyOld.y) - (targetWorkspace.scale * xyNew.y)) /
targetWorkspace.scale;
block.moveBy(dx, dy);

// 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;
};

Expand Down
12 changes: 12 additions & 0 deletions core/workspace_svg.js
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,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.<!Array>} handler Data that can be passed to unbindEvent_.
Expand Down