mirror of
https://github.com/google/blockly.git
synced 2026-01-07 00:50:27 +01:00
refactor: convert some files to es6 classes (#5861)
* refactor: convert utils/coordinate.js to ES6 class * refactor: convert utils/rect.js to ES6 class * refactor: convert utils/size.js to ES6 class * refactor: convert block_drag_surface.js to ES6 class * refactor: convert block_dragger.js to ES6 class * refactor: convert bubble_dragger.js to ES6 class * chore: declare bubble property in the constructor * refactor: convert bubble.js to ES6 class * chore: clang-format * chore(lint): lint and format
This commit is contained in:
@@ -35,233 +35,235 @@ const {Svg} = goog.require('Blockly.utils.Svg');
|
||||
/**
|
||||
* Class for a drag surface for the currently dragged block. This is a separate
|
||||
* SVG that contains only the currently moving block, or nothing.
|
||||
* @param {!Element} container Containing element.
|
||||
* @constructor
|
||||
* @alias Blockly.BlockDragSurfaceSvg
|
||||
*/
|
||||
const BlockDragSurfaceSvg = function(container) {
|
||||
const BlockDragSurfaceSvg = class {
|
||||
/**
|
||||
* The SVG drag surface. Set once by BlockDragSurfaceSvg.createDom.
|
||||
* @type {?SVGElement}
|
||||
* @private
|
||||
* @param {!Element} container Containing element.
|
||||
* @alias Blockly.BlockDragSurfaceSvg
|
||||
*/
|
||||
this.SVG_ = null;
|
||||
constructor(container) {
|
||||
/**
|
||||
* The SVG drag surface. Set once by BlockDragSurfaceSvg.createDom.
|
||||
* @type {?SVGElement}
|
||||
* @private
|
||||
*/
|
||||
this.SVG_ = null;
|
||||
|
||||
/**
|
||||
* This is where blocks live while they are being dragged if the drag surface
|
||||
* is enabled.
|
||||
* @type {?SVGElement}
|
||||
* @private
|
||||
*/
|
||||
this.dragGroup_ = null;
|
||||
/**
|
||||
* This is where blocks live while they are being dragged if the drag
|
||||
* surface is enabled.
|
||||
* @type {?SVGElement}
|
||||
* @private
|
||||
*/
|
||||
this.dragGroup_ = null;
|
||||
|
||||
/**
|
||||
* Containing HTML element; parent of the workspace and the drag surface.
|
||||
* @type {!Element}
|
||||
* @private
|
||||
*/
|
||||
this.container_ = container;
|
||||
/**
|
||||
* Containing HTML element; parent of the workspace and the drag surface.
|
||||
* @type {!Element}
|
||||
* @private
|
||||
*/
|
||||
this.container_ = container;
|
||||
|
||||
/**
|
||||
* Cached value for the scale of the drag surface.
|
||||
* Used to set/get the correct translation during and after a drag.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.scale_ = 1;
|
||||
/**
|
||||
* Cached value for the scale of the drag surface.
|
||||
* Used to set/get the correct translation during and after a drag.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.scale_ = 1;
|
||||
|
||||
/**
|
||||
* Cached value for the translation of the drag surface.
|
||||
* This translation is in pixel units, because the scale is applied to the
|
||||
* drag group rather than the top-level SVG.
|
||||
* @type {?Coordinate}
|
||||
* @private
|
||||
*/
|
||||
this.surfaceXY_ = null;
|
||||
/**
|
||||
* Cached value for the translation of the drag surface.
|
||||
* This translation is in pixel units, because the scale is applied to the
|
||||
* drag group rather than the top-level SVG.
|
||||
* @type {?Coordinate}
|
||||
* @private
|
||||
*/
|
||||
this.surfaceXY_ = null;
|
||||
|
||||
/**
|
||||
* Cached value for the translation of the child drag surface in pixel units.
|
||||
* Since the child drag surface tracks the translation of the workspace this
|
||||
* is ultimately the translation of the workspace.
|
||||
* @type {!Coordinate}
|
||||
* @private
|
||||
*/
|
||||
this.childSurfaceXY_ = new Coordinate(0, 0);
|
||||
/**
|
||||
* Cached value for the translation of the child drag surface in pixel
|
||||
* units. Since the child drag surface tracks the translation of the
|
||||
* workspace this is ultimately the translation of the workspace.
|
||||
* @type {!Coordinate}
|
||||
* @private
|
||||
*/
|
||||
this.childSurfaceXY_ = new Coordinate(0, 0);
|
||||
|
||||
this.createDom();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create the drag surface and inject it into the container.
|
||||
*/
|
||||
BlockDragSurfaceSvg.prototype.createDom = function() {
|
||||
if (this.SVG_) {
|
||||
return; // Already created.
|
||||
this.createDom();
|
||||
}
|
||||
this.SVG_ = dom.createSvgElement(
|
||||
Svg.SVG, {
|
||||
'xmlns': dom.SVG_NS,
|
||||
'xmlns:html': dom.HTML_NS,
|
||||
'xmlns:xlink': dom.XLINK_NS,
|
||||
'version': '1.1',
|
||||
'class': 'blocklyBlockDragSurface',
|
||||
},
|
||||
this.container_);
|
||||
this.dragGroup_ = dom.createSvgElement(Svg.G, {}, this.SVG_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the SVG blocks on the drag surface's group and show the surface.
|
||||
* Only one block group should be on the drag surface at a time.
|
||||
* @param {!SVGElement} blocks Block or group of blocks to place on the drag
|
||||
* surface.
|
||||
*/
|
||||
BlockDragSurfaceSvg.prototype.setBlocksAndShow = function(blocks) {
|
||||
if (this.dragGroup_.childNodes.length) {
|
||||
throw Error('Already dragging a block.');
|
||||
}
|
||||
// appendChild removes the blocks from the previous parent
|
||||
this.dragGroup_.appendChild(blocks);
|
||||
this.SVG_.style.display = 'block';
|
||||
this.surfaceXY_ = new Coordinate(0, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Translate and scale the entire drag surface group to the given position, to
|
||||
* keep in sync with the workspace.
|
||||
* @param {number} x X translation in pixel coordinates.
|
||||
* @param {number} y Y translation in pixel coordinates.
|
||||
* @param {number} scale Scale of the group.
|
||||
*/
|
||||
BlockDragSurfaceSvg.prototype.translateAndScaleGroup = function(x, y, scale) {
|
||||
this.scale_ = scale;
|
||||
// This is a work-around to prevent a the blocks from rendering
|
||||
// fuzzy while they are being dragged on the drag surface.
|
||||
const fixedX = x.toFixed(0);
|
||||
const fixedY = y.toFixed(0);
|
||||
|
||||
this.childSurfaceXY_.x = parseInt(fixedX, 10);
|
||||
this.childSurfaceXY_.y = parseInt(fixedY, 10);
|
||||
|
||||
this.dragGroup_.setAttribute(
|
||||
'transform',
|
||||
'translate(' + fixedX + ',' + fixedY + ') scale(' + scale + ')');
|
||||
};
|
||||
|
||||
/**
|
||||
* Translate the drag surface's SVG based on its internal state.
|
||||
* @private
|
||||
*/
|
||||
BlockDragSurfaceSvg.prototype.translateSurfaceInternal_ = function() {
|
||||
let x = this.surfaceXY_.x;
|
||||
let y = this.surfaceXY_.y;
|
||||
// This is a work-around to prevent a the blocks from rendering
|
||||
// fuzzy while they are being dragged on the drag surface.
|
||||
x = x.toFixed(0);
|
||||
y = y.toFixed(0);
|
||||
this.SVG_.style.display = 'block';
|
||||
|
||||
dom.setCssTransform(this.SVG_, 'translate3d(' + x + 'px, ' + y + 'px, 0)');
|
||||
};
|
||||
|
||||
/**
|
||||
* Translates the entire surface by a relative offset.
|
||||
* @param {number} deltaX Horizontal offset in pixel units.
|
||||
* @param {number} deltaY Vertical offset in pixel units.
|
||||
*/
|
||||
BlockDragSurfaceSvg.prototype.translateBy = function(deltaX, deltaY) {
|
||||
const x = this.surfaceXY_.x + deltaX;
|
||||
const y = this.surfaceXY_.y + deltaY;
|
||||
this.surfaceXY_ = new Coordinate(x, y);
|
||||
this.translateSurfaceInternal_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Translate the entire drag surface during a drag.
|
||||
* We translate the drag surface instead of the blocks inside the surface
|
||||
* so that the browser avoids repainting the SVG.
|
||||
* Because of this, the drag coordinates must be adjusted by scale.
|
||||
* @param {number} x X translation for the entire surface.
|
||||
* @param {number} y Y translation for the entire surface.
|
||||
*/
|
||||
BlockDragSurfaceSvg.prototype.translateSurface = function(x, y) {
|
||||
this.surfaceXY_ = new Coordinate(x * this.scale_, y * this.scale_);
|
||||
this.translateSurfaceInternal_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Reports the surface translation in scaled workspace coordinates.
|
||||
* Use this when finishing a drag to return blocks to the correct position.
|
||||
* @return {!Coordinate} Current translation of the surface.
|
||||
*/
|
||||
BlockDragSurfaceSvg.prototype.getSurfaceTranslation = function() {
|
||||
const xy = svgMath.getRelativeXY(/** @type {!SVGElement} */ (this.SVG_));
|
||||
return new Coordinate(xy.x / this.scale_, xy.y / this.scale_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Provide a reference to the drag group (primarily for
|
||||
* BlockSvg.getRelativeToSurfaceXY).
|
||||
* @return {?SVGElement} Drag surface group element.
|
||||
*/
|
||||
BlockDragSurfaceSvg.prototype.getGroup = function() {
|
||||
return this.dragGroup_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the SVG drag surface.
|
||||
* @returns {?SVGElement} The SVG drag surface.
|
||||
*/
|
||||
BlockDragSurfaceSvg.prototype.getSvgRoot = function() {
|
||||
return this.SVG_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current blocks on the drag surface, if any (primarily
|
||||
* for BlockSvg.getRelativeToSurfaceXY).
|
||||
* @return {?Element} Drag surface block DOM element, or null if no blocks
|
||||
* exist.
|
||||
*/
|
||||
BlockDragSurfaceSvg.prototype.getCurrentBlock = function() {
|
||||
return /** @type {Element} */ (this.dragGroup_.firstChild);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the translation of the child block surface
|
||||
* This surface is in charge of keeping track of how much the workspace has
|
||||
* moved.
|
||||
* @return {!Coordinate} The amount the workspace has been moved.
|
||||
*/
|
||||
BlockDragSurfaceSvg.prototype.getWsTranslation = function() {
|
||||
// Returning a copy so the coordinate can not be changed outside this class.
|
||||
return this.childSurfaceXY_.clone();
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the group and hide the surface; move the blocks off onto the provided
|
||||
* element.
|
||||
* If the block is being deleted it doesn't need to go back to the original
|
||||
* surface, since it would be removed immediately during dispose.
|
||||
* @param {Element=} opt_newSurface Surface the dragging blocks should be moved
|
||||
* to, or null if the blocks should be removed from this surface without
|
||||
* being moved to a different surface.
|
||||
*/
|
||||
BlockDragSurfaceSvg.prototype.clearAndHide = function(opt_newSurface) {
|
||||
const currentBlockElement = this.getCurrentBlock();
|
||||
if (currentBlockElement) {
|
||||
if (opt_newSurface) {
|
||||
// appendChild removes the node from this.dragGroup_
|
||||
opt_newSurface.appendChild(currentBlockElement);
|
||||
} else {
|
||||
this.dragGroup_.removeChild(currentBlockElement);
|
||||
/**
|
||||
* Create the drag surface and inject it into the container.
|
||||
*/
|
||||
createDom() {
|
||||
if (this.SVG_) {
|
||||
return; // Already created.
|
||||
}
|
||||
this.SVG_ = dom.createSvgElement(
|
||||
Svg.SVG, {
|
||||
'xmlns': dom.SVG_NS,
|
||||
'xmlns:html': dom.HTML_NS,
|
||||
'xmlns:xlink': dom.XLINK_NS,
|
||||
'version': '1.1',
|
||||
'class': 'blocklyBlockDragSurface',
|
||||
},
|
||||
this.container_);
|
||||
this.dragGroup_ = dom.createSvgElement(Svg.G, {}, this.SVG_);
|
||||
}
|
||||
this.SVG_.style.display = 'none';
|
||||
if (this.dragGroup_.childNodes.length) {
|
||||
throw Error('Drag group was not cleared.');
|
||||
|
||||
/**
|
||||
* Set the SVG blocks on the drag surface's group and show the surface.
|
||||
* Only one block group should be on the drag surface at a time.
|
||||
* @param {!SVGElement} blocks Block or group of blocks to place on the drag
|
||||
* surface.
|
||||
*/
|
||||
setBlocksAndShow(blocks) {
|
||||
if (this.dragGroup_.childNodes.length) {
|
||||
throw Error('Already dragging a block.');
|
||||
}
|
||||
// appendChild removes the blocks from the previous parent
|
||||
this.dragGroup_.appendChild(blocks);
|
||||
this.SVG_.style.display = 'block';
|
||||
this.surfaceXY_ = new Coordinate(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate and scale the entire drag surface group to the given position, to
|
||||
* keep in sync with the workspace.
|
||||
* @param {number} x X translation in pixel coordinates.
|
||||
* @param {number} y Y translation in pixel coordinates.
|
||||
* @param {number} scale Scale of the group.
|
||||
*/
|
||||
translateAndScaleGroup(x, y, scale) {
|
||||
this.scale_ = scale;
|
||||
// This is a work-around to prevent a the blocks from rendering
|
||||
// fuzzy while they are being dragged on the drag surface.
|
||||
const fixedX = x.toFixed(0);
|
||||
const fixedY = y.toFixed(0);
|
||||
|
||||
this.childSurfaceXY_.x = parseInt(fixedX, 10);
|
||||
this.childSurfaceXY_.y = parseInt(fixedY, 10);
|
||||
|
||||
this.dragGroup_.setAttribute(
|
||||
'transform',
|
||||
'translate(' + fixedX + ',' + fixedY + ') scale(' + scale + ')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate the drag surface's SVG based on its internal state.
|
||||
* @private
|
||||
*/
|
||||
translateSurfaceInternal_() {
|
||||
let x = this.surfaceXY_.x;
|
||||
let y = this.surfaceXY_.y;
|
||||
// This is a work-around to prevent a the blocks from rendering
|
||||
// fuzzy while they are being dragged on the drag surface.
|
||||
x = x.toFixed(0);
|
||||
y = y.toFixed(0);
|
||||
this.SVG_.style.display = 'block';
|
||||
|
||||
dom.setCssTransform(this.SVG_, 'translate3d(' + x + 'px, ' + y + 'px, 0)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the entire surface by a relative offset.
|
||||
* @param {number} deltaX Horizontal offset in pixel units.
|
||||
* @param {number} deltaY Vertical offset in pixel units.
|
||||
*/
|
||||
translateBy(deltaX, deltaY) {
|
||||
const x = this.surfaceXY_.x + deltaX;
|
||||
const y = this.surfaceXY_.y + deltaY;
|
||||
this.surfaceXY_ = new Coordinate(x, y);
|
||||
this.translateSurfaceInternal_();
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate the entire drag surface during a drag.
|
||||
* We translate the drag surface instead of the blocks inside the surface
|
||||
* so that the browser avoids repainting the SVG.
|
||||
* Because of this, the drag coordinates must be adjusted by scale.
|
||||
* @param {number} x X translation for the entire surface.
|
||||
* @param {number} y Y translation for the entire surface.
|
||||
*/
|
||||
translateSurface(x, y) {
|
||||
this.surfaceXY_ = new Coordinate(x * this.scale_, y * this.scale_);
|
||||
this.translateSurfaceInternal_();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports the surface translation in scaled workspace coordinates.
|
||||
* Use this when finishing a drag to return blocks to the correct position.
|
||||
* @return {!Coordinate} Current translation of the surface.
|
||||
*/
|
||||
getSurfaceTranslation() {
|
||||
const xy = svgMath.getRelativeXY(/** @type {!SVGElement} */ (this.SVG_));
|
||||
return new Coordinate(xy.x / this.scale_, xy.y / this.scale_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a reference to the drag group (primarily for
|
||||
* BlockSvg.getRelativeToSurfaceXY).
|
||||
* @return {?SVGElement} Drag surface group element.
|
||||
*/
|
||||
getGroup() {
|
||||
return this.dragGroup_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SVG drag surface.
|
||||
* @returns {?SVGElement} The SVG drag surface.
|
||||
*/
|
||||
getSvgRoot() {
|
||||
return this.SVG_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current blocks on the drag surface, if any (primarily
|
||||
* for BlockSvg.getRelativeToSurfaceXY).
|
||||
* @return {?Element} Drag surface block DOM element, or null if no blocks
|
||||
* exist.
|
||||
*/
|
||||
getCurrentBlock() {
|
||||
return /** @type {Element} */ (this.dragGroup_.firstChild);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the translation of the child block surface
|
||||
* This surface is in charge of keeping track of how much the workspace has
|
||||
* moved.
|
||||
* @return {!Coordinate} The amount the workspace has been moved.
|
||||
*/
|
||||
getWsTranslation() {
|
||||
// Returning a copy so the coordinate can not be changed outside this class.
|
||||
return this.childSurfaceXY_.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the group and hide the surface; move the blocks off onto the provided
|
||||
* element.
|
||||
* If the block is being deleted it doesn't need to go back to the original
|
||||
* surface, since it would be removed immediately during dispose.
|
||||
* @param {Element=} opt_newSurface Surface the dragging blocks should be
|
||||
* moved to, or null if the blocks should be removed from this surface
|
||||
* without being moved to a different surface.
|
||||
*/
|
||||
clearAndHide(opt_newSurface) {
|
||||
const currentBlockElement = this.getCurrentBlock();
|
||||
if (currentBlockElement) {
|
||||
if (opt_newSurface) {
|
||||
// appendChild removes the node from this.dragGroup_
|
||||
opt_newSurface.appendChild(currentBlockElement);
|
||||
} else {
|
||||
this.dragGroup_.removeChild(currentBlockElement);
|
||||
}
|
||||
}
|
||||
this.SVG_.style.display = 'none';
|
||||
if (this.dragGroup_.childNodes.length) {
|
||||
throw Error('Drag group was not cleared.');
|
||||
}
|
||||
this.surfaceXY_ = null;
|
||||
}
|
||||
this.surfaceXY_ = null;
|
||||
};
|
||||
|
||||
exports.BlockDragSurfaceSvg = BlockDragSurfaceSvg;
|
||||
|
||||
@@ -40,76 +40,411 @@ goog.require('Blockly.Events.BlockMove');
|
||||
/**
|
||||
* Class for a block dragger. It moves blocks around the workspace when they
|
||||
* are being dragged by a mouse or touch.
|
||||
* @param {!BlockSvg} block The block to drag.
|
||||
* @param {!WorkspaceSvg} workspace The workspace to drag on.
|
||||
* @constructor
|
||||
* @implements {IBlockDragger}
|
||||
* @alias Blockly.BlockDragger
|
||||
*/
|
||||
const BlockDragger = function(block, workspace) {
|
||||
const BlockDragger = class {
|
||||
/**
|
||||
* The top block in the stack that is being dragged.
|
||||
* @type {!BlockSvg}
|
||||
* @param {!BlockSvg} block The block to drag.
|
||||
* @param {!WorkspaceSvg} workspace The workspace to drag on.
|
||||
* @alias Blockly.BlockDragger
|
||||
*/
|
||||
constructor(block, workspace) {
|
||||
/**
|
||||
* The top block in the stack that is being dragged.
|
||||
* @type {!BlockSvg}
|
||||
* @protected
|
||||
*/
|
||||
this.draggingBlock_ = block;
|
||||
|
||||
/**
|
||||
* The workspace on which the block is being dragged.
|
||||
* @type {!WorkspaceSvg}
|
||||
* @protected
|
||||
*/
|
||||
this.workspace_ = workspace;
|
||||
|
||||
/**
|
||||
* Object that keeps track of connections on dragged blocks.
|
||||
* @type {!InsertionMarkerManager}
|
||||
* @protected
|
||||
*/
|
||||
this.draggedConnectionManager_ =
|
||||
new InsertionMarkerManager(this.draggingBlock_);
|
||||
|
||||
/**
|
||||
* Which drag area the mouse pointer is over, if any.
|
||||
* @type {?IDragTarget}
|
||||
* @private
|
||||
*/
|
||||
this.dragTarget_ = null;
|
||||
|
||||
/**
|
||||
* Whether the block would be deleted if dropped immediately.
|
||||
* @type {boolean}
|
||||
* @protected
|
||||
*/
|
||||
this.wouldDeleteBlock_ = false;
|
||||
|
||||
/**
|
||||
* The location of the top left corner of the dragging block at the
|
||||
* beginning of the drag in workspace coordinates.
|
||||
* @type {!Coordinate}
|
||||
* @protected
|
||||
*/
|
||||
this.startXY_ = this.draggingBlock_.getRelativeToSurfaceXY();
|
||||
|
||||
/**
|
||||
* A list of all of the icons (comment, warning, and mutator) that are
|
||||
* on this block and its descendants. Moving an icon moves the bubble that
|
||||
* extends from it if that bubble is open.
|
||||
* @type {Array<!Object>}
|
||||
* @protected
|
||||
*/
|
||||
this.dragIconData_ = initIconData(block);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sever all links from this object.
|
||||
* @package
|
||||
*/
|
||||
dispose() {
|
||||
this.dragIconData_.length = 0;
|
||||
|
||||
if (this.draggedConnectionManager_) {
|
||||
this.draggedConnectionManager_.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start dragging a block. This includes moving it to the drag surface.
|
||||
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at mouse down, in pixel units.
|
||||
* @param {boolean} healStack Whether or not to heal the stack after
|
||||
* disconnecting.
|
||||
* @public
|
||||
*/
|
||||
startDrag(currentDragDeltaXY, healStack) {
|
||||
if (!eventUtils.getGroup()) {
|
||||
eventUtils.setGroup(true);
|
||||
}
|
||||
this.fireDragStartEvent_();
|
||||
|
||||
// Mutators don't have the same type of z-ordering as the normal workspace
|
||||
// during a drag. They have to rely on the order of the blocks in the SVG.
|
||||
// For performance reasons that usually happens at the end of a drag,
|
||||
// but do it at the beginning for mutators.
|
||||
if (this.workspace_.isMutator) {
|
||||
this.draggingBlock_.bringToFront();
|
||||
}
|
||||
|
||||
// During a drag there may be a lot of rerenders, but not field changes.
|
||||
// Turn the cache on so we don't do spurious remeasures during the drag.
|
||||
dom.startTextWidthCache();
|
||||
this.workspace_.setResizesEnabled(false);
|
||||
blockAnimation.disconnectUiStop();
|
||||
|
||||
if (this.shouldDisconnect_(healStack)) {
|
||||
this.disconnectBlock_(healStack, currentDragDeltaXY);
|
||||
}
|
||||
this.draggingBlock_.setDragging(true);
|
||||
// For future consideration: we may be able to put moveToDragSurface inside
|
||||
// the block dragger, which would also let the block not track the block
|
||||
// drag surface.
|
||||
this.draggingBlock_.moveToDragSurface();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not we should disconnect the block when a drag is started.
|
||||
* @param {boolean} healStack Whether or not to heal the stack after
|
||||
* disconnecting.
|
||||
* @return {boolean} True to disconnect the block, false otherwise.
|
||||
* @protected
|
||||
*/
|
||||
this.draggingBlock_ = block;
|
||||
shouldDisconnect_(healStack) {
|
||||
return !!(
|
||||
this.draggingBlock_.getParent() ||
|
||||
(healStack && this.draggingBlock_.nextConnection &&
|
||||
this.draggingBlock_.nextConnection.targetBlock()));
|
||||
}
|
||||
|
||||
/**
|
||||
* The workspace on which the block is being dragged.
|
||||
* @type {!WorkspaceSvg}
|
||||
* Disconnects the block and moves it to a new location.
|
||||
* @param {boolean} healStack Whether or not to heal the stack after
|
||||
* disconnecting.
|
||||
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at mouse down, in pixel units.
|
||||
* @protected
|
||||
*/
|
||||
this.workspace_ = workspace;
|
||||
disconnectBlock_(healStack, currentDragDeltaXY) {
|
||||
this.draggingBlock_.unplug(healStack);
|
||||
const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
|
||||
const newLoc = Coordinate.sum(this.startXY_, delta);
|
||||
|
||||
this.draggingBlock_.translate(newLoc.x, newLoc.y);
|
||||
blockAnimation.disconnectUiEffect(this.draggingBlock_);
|
||||
this.draggedConnectionManager_.updateAvailableConnections();
|
||||
}
|
||||
|
||||
/**
|
||||
* Object that keeps track of connections on dragged blocks.
|
||||
* @type {!InsertionMarkerManager}
|
||||
* Fire a UI event at the start of a block drag.
|
||||
* @protected
|
||||
*/
|
||||
this.draggedConnectionManager_ =
|
||||
new InsertionMarkerManager(this.draggingBlock_);
|
||||
fireDragStartEvent_() {
|
||||
const event = new (eventUtils.get(eventUtils.BLOCK_DRAG))(
|
||||
this.draggingBlock_, true, this.draggingBlock_.getDescendants(false));
|
||||
eventUtils.fire(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Which drag area the mouse pointer is over, if any.
|
||||
* @type {?IDragTarget}
|
||||
* @private
|
||||
* Execute a step of block dragging, based on the given event. Update the
|
||||
* display accordingly.
|
||||
* @param {!Event} e The most recent move event.
|
||||
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at the start of the drag, in pixel units.
|
||||
* @public
|
||||
*/
|
||||
this.dragTarget_ = null;
|
||||
drag(e, currentDragDeltaXY) {
|
||||
const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
|
||||
const newLoc = Coordinate.sum(this.startXY_, delta);
|
||||
this.draggingBlock_.moveDuringDrag(newLoc);
|
||||
this.dragIcons_(delta);
|
||||
|
||||
const oldDragTarget = this.dragTarget_;
|
||||
this.dragTarget_ = this.workspace_.getDragTarget(e);
|
||||
|
||||
this.draggedConnectionManager_.update(delta, this.dragTarget_);
|
||||
const oldWouldDeleteBlock = this.wouldDeleteBlock_;
|
||||
this.wouldDeleteBlock_ = this.draggedConnectionManager_.wouldDeleteBlock();
|
||||
if (oldWouldDeleteBlock !== this.wouldDeleteBlock_) {
|
||||
// Prevent unnecessary add/remove class calls.
|
||||
this.updateCursorDuringBlockDrag_();
|
||||
}
|
||||
|
||||
// Call drag enter/exit/over after wouldDeleteBlock is called in
|
||||
// InsertionMarkerManager.update.
|
||||
if (this.dragTarget_ !== oldDragTarget) {
|
||||
oldDragTarget && oldDragTarget.onDragExit(this.draggingBlock_);
|
||||
this.dragTarget_ && this.dragTarget_.onDragEnter(this.draggingBlock_);
|
||||
}
|
||||
this.dragTarget_ && this.dragTarget_.onDragOver(this.draggingBlock_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the block would be deleted if dropped immediately.
|
||||
* @type {boolean}
|
||||
* Finish a block drag and put the block back on the workspace.
|
||||
* @param {!Event} e The mouseup/touchend event.
|
||||
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at the start of the drag, in pixel units.
|
||||
* @public
|
||||
*/
|
||||
endDrag(e, currentDragDeltaXY) {
|
||||
// Make sure internal state is fresh.
|
||||
this.drag(e, currentDragDeltaXY);
|
||||
this.dragIconData_ = [];
|
||||
this.fireDragEndEvent_();
|
||||
|
||||
dom.stopTextWidthCache();
|
||||
|
||||
blockAnimation.disconnectUiStop();
|
||||
|
||||
const preventMove = !!this.dragTarget_ &&
|
||||
this.dragTarget_.shouldPreventMove(this.draggingBlock_);
|
||||
/** @type {Coordinate} */
|
||||
let newLoc;
|
||||
/** @type {Coordinate} */
|
||||
let delta;
|
||||
if (preventMove) {
|
||||
newLoc = this.startXY_;
|
||||
} else {
|
||||
const newValues = this.getNewLocationAfterDrag_(currentDragDeltaXY);
|
||||
delta = newValues.delta;
|
||||
newLoc = newValues.newLocation;
|
||||
}
|
||||
this.draggingBlock_.moveOffDragSurface(newLoc);
|
||||
|
||||
if (this.dragTarget_) {
|
||||
this.dragTarget_.onDrop(this.draggingBlock_);
|
||||
}
|
||||
|
||||
const deleted = this.maybeDeleteBlock_();
|
||||
if (!deleted) {
|
||||
// These are expensive and don't need to be done if we're deleting.
|
||||
this.draggingBlock_.setDragging(false);
|
||||
if (delta) { // !preventMove
|
||||
this.updateBlockAfterMove_(delta);
|
||||
} else {
|
||||
// Blocks dragged directly from a flyout may need to be bumped into
|
||||
// bounds.
|
||||
bumpObjects.bumpIntoBounds(
|
||||
this.draggingBlock_.workspace,
|
||||
this.workspace_.getMetricsManager().getScrollMetrics(true),
|
||||
this.draggingBlock_);
|
||||
}
|
||||
}
|
||||
this.workspace_.setResizesEnabled(true);
|
||||
|
||||
eventUtils.setGroup(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the drag delta and new location values after a block is dragged.
|
||||
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the start of the drag, in pixel units.
|
||||
* @return {{delta: !Coordinate, newLocation:
|
||||
* !Coordinate}} New location after drag. delta is in
|
||||
* workspace units. newLocation is the new coordinate where the block
|
||||
* should end up.
|
||||
* @protected
|
||||
*/
|
||||
this.wouldDeleteBlock_ = false;
|
||||
getNewLocationAfterDrag_(currentDragDeltaXY) {
|
||||
const newValues = {};
|
||||
newValues.delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
|
||||
newValues.newLocation = Coordinate.sum(this.startXY_, newValues.delta);
|
||||
return newValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* The location of the top left corner of the dragging block at the beginning
|
||||
* of the drag in workspace coordinates.
|
||||
* @type {!Coordinate}
|
||||
* May delete the dragging block, if allowed. If `this.wouldDeleteBlock_` is
|
||||
* not true, the block will not be deleted. This should be called at the end
|
||||
* of a block drag.
|
||||
* @return {boolean} True if the block was deleted.
|
||||
* @protected
|
||||
*/
|
||||
this.startXY_ = this.draggingBlock_.getRelativeToSurfaceXY();
|
||||
maybeDeleteBlock_() {
|
||||
if (this.wouldDeleteBlock_) {
|
||||
// Fire a move event, so we know where to go back to for an undo.
|
||||
this.fireMoveEvent_();
|
||||
this.draggingBlock_.dispose(false, true);
|
||||
common.draggingConnections.length = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of all of the icons (comment, warning, and mutator) that are
|
||||
* on this block and its descendants. Moving an icon moves the bubble that
|
||||
* extends from it if that bubble is open.
|
||||
* @type {Array<!Object>}
|
||||
* Updates the necessary information to place a block at a certain location.
|
||||
* @param {!Coordinate} delta The change in location from where
|
||||
* the block started the drag to where it ended the drag.
|
||||
* @protected
|
||||
*/
|
||||
this.dragIconData_ = initIconData(block);
|
||||
};
|
||||
updateBlockAfterMove_(delta) {
|
||||
this.draggingBlock_.moveConnections(delta.x, delta.y);
|
||||
this.fireMoveEvent_();
|
||||
if (this.draggedConnectionManager_.wouldConnectBlock()) {
|
||||
// Applying connections also rerenders the relevant blocks.
|
||||
this.draggedConnectionManager_.applyConnections();
|
||||
} else {
|
||||
this.draggingBlock_.render();
|
||||
}
|
||||
this.draggingBlock_.scheduleSnapAndBump();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sever all links from this object.
|
||||
* @package
|
||||
*/
|
||||
BlockDragger.prototype.dispose = function() {
|
||||
this.dragIconData_.length = 0;
|
||||
/**
|
||||
* Fire a UI event at the end of a block drag.
|
||||
* @protected
|
||||
*/
|
||||
fireDragEndEvent_() {
|
||||
const event = new (eventUtils.get(eventUtils.BLOCK_DRAG))(
|
||||
this.draggingBlock_, false, this.draggingBlock_.getDescendants(false));
|
||||
eventUtils.fire(event);
|
||||
}
|
||||
|
||||
if (this.draggedConnectionManager_) {
|
||||
this.draggedConnectionManager_.dispose();
|
||||
/**
|
||||
* Adds or removes the style of the cursor for the toolbox.
|
||||
* This is what changes the cursor to display an x when a deletable block is
|
||||
* held over the toolbox.
|
||||
* @param {boolean} isEnd True if we are at the end of a drag, false
|
||||
* otherwise.
|
||||
* @protected
|
||||
*/
|
||||
updateToolboxStyle_(isEnd) {
|
||||
const toolbox = this.workspace_.getToolbox();
|
||||
|
||||
if (toolbox) {
|
||||
const style = this.draggingBlock_.isDeletable() ? 'blocklyToolboxDelete' :
|
||||
'blocklyToolboxGrab';
|
||||
|
||||
if (isEnd && typeof toolbox.removeStyle === 'function') {
|
||||
toolbox.removeStyle(style);
|
||||
} else if (!isEnd && typeof toolbox.addStyle === 'function') {
|
||||
toolbox.addStyle(style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire a move event at the end of a block drag.
|
||||
* @protected
|
||||
*/
|
||||
fireMoveEvent_() {
|
||||
const event =
|
||||
new (eventUtils.get(eventUtils.BLOCK_MOVE))(this.draggingBlock_);
|
||||
event.oldCoordinate = this.startXY_;
|
||||
event.recordNew();
|
||||
eventUtils.fire(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the cursor (and possibly the trash can lid) to reflect whether the
|
||||
* dragging block would be deleted if released immediately.
|
||||
* @protected
|
||||
*/
|
||||
updateCursorDuringBlockDrag_() {
|
||||
this.draggingBlock_.setDeleteStyle(this.wouldDeleteBlock_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a coordinate object from pixels to workspace units, including a
|
||||
* correction for mutator workspaces.
|
||||
* This function does not consider differing origins. It simply scales the
|
||||
* input's x and y values.
|
||||
* @param {!Coordinate} pixelCoord A coordinate with x and y
|
||||
* values in CSS pixel units.
|
||||
* @return {!Coordinate} The input coordinate divided by the
|
||||
* workspace scale.
|
||||
* @protected
|
||||
*/
|
||||
pixelsToWorkspaceUnits_(pixelCoord) {
|
||||
const result = new Coordinate(
|
||||
pixelCoord.x / this.workspace_.scale,
|
||||
pixelCoord.y / this.workspace_.scale);
|
||||
if (this.workspace_.isMutator) {
|
||||
// If we're in a mutator, its scale is always 1, purely because of some
|
||||
// oddities in our rendering optimizations. The actual scale is the same
|
||||
// as the scale on the parent workspace. Fix that for dragging.
|
||||
const mainScale = this.workspace_.options.parentWorkspace.scale;
|
||||
result.scale(1 / mainScale);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move all of the icons connected to this drag.
|
||||
* @param {!Coordinate} dxy How far to move the icons from their
|
||||
* original positions, in workspace units.
|
||||
* @protected
|
||||
*/
|
||||
dragIcons_(dxy) {
|
||||
// Moving icons moves their associated bubbles.
|
||||
for (let i = 0; i < this.dragIconData_.length; i++) {
|
||||
const data = this.dragIconData_[i];
|
||||
data.icon.setIconLocation(Coordinate.sum(data.location, dxy));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of the insertion markers that currently exist. Drags have 0, 1,
|
||||
* or 2 insertion markers.
|
||||
* @return {!Array<!BlockSvg>} A possibly empty list of insertion
|
||||
* marker blocks.
|
||||
* @public
|
||||
*/
|
||||
getInsertionMarkers() {
|
||||
// No insertion markers with the old style of dragged connection managers.
|
||||
if (this.draggedConnectionManager_ &&
|
||||
this.draggedConnectionManager_.getInsertionMarkers) {
|
||||
return this.draggedConnectionManager_.getInsertionMarkers();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -141,340 +476,6 @@ const initIconData = function(block) {
|
||||
return dragIconData;
|
||||
};
|
||||
|
||||
/**
|
||||
* Start dragging a block. This includes moving it to the drag surface.
|
||||
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at mouse down, in pixel units.
|
||||
* @param {boolean} healStack Whether or not to heal the stack after
|
||||
* disconnecting.
|
||||
* @public
|
||||
*/
|
||||
BlockDragger.prototype.startDrag = function(currentDragDeltaXY, healStack) {
|
||||
if (!eventUtils.getGroup()) {
|
||||
eventUtils.setGroup(true);
|
||||
}
|
||||
this.fireDragStartEvent_();
|
||||
|
||||
// Mutators don't have the same type of z-ordering as the normal workspace
|
||||
// during a drag. They have to rely on the order of the blocks in the SVG.
|
||||
// For performance reasons that usually happens at the end of a drag,
|
||||
// but do it at the beginning for mutators.
|
||||
if (this.workspace_.isMutator) {
|
||||
this.draggingBlock_.bringToFront();
|
||||
}
|
||||
|
||||
// During a drag there may be a lot of rerenders, but not field changes.
|
||||
// Turn the cache on so we don't do spurious remeasures during the drag.
|
||||
dom.startTextWidthCache();
|
||||
this.workspace_.setResizesEnabled(false);
|
||||
blockAnimation.disconnectUiStop();
|
||||
|
||||
if (this.shouldDisconnect_(healStack)) {
|
||||
this.disconnectBlock_(healStack, currentDragDeltaXY);
|
||||
}
|
||||
this.draggingBlock_.setDragging(true);
|
||||
// For future consideration: we may be able to put moveToDragSurface inside
|
||||
// the block dragger, which would also let the block not track the block drag
|
||||
// surface.
|
||||
this.draggingBlock_.moveToDragSurface();
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether or not we should disconnect the block when a drag is started.
|
||||
* @param {boolean} healStack Whether or not to heal the stack after
|
||||
* disconnecting.
|
||||
* @return {boolean} True to disconnect the block, false otherwise.
|
||||
* @protected
|
||||
*/
|
||||
BlockDragger.prototype.shouldDisconnect_ = function(healStack) {
|
||||
return !!(
|
||||
this.draggingBlock_.getParent() ||
|
||||
(healStack && this.draggingBlock_.nextConnection &&
|
||||
this.draggingBlock_.nextConnection.targetBlock()));
|
||||
};
|
||||
|
||||
/**
|
||||
* Disconnects the block and moves it to a new location.
|
||||
* @param {boolean} healStack Whether or not to heal the stack after
|
||||
* disconnecting.
|
||||
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at mouse down, in pixel units.
|
||||
* @protected
|
||||
*/
|
||||
BlockDragger.prototype.disconnectBlock_ = function(
|
||||
healStack, currentDragDeltaXY) {
|
||||
this.draggingBlock_.unplug(healStack);
|
||||
const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
|
||||
const newLoc = Coordinate.sum(this.startXY_, delta);
|
||||
|
||||
this.draggingBlock_.translate(newLoc.x, newLoc.y);
|
||||
blockAnimation.disconnectUiEffect(this.draggingBlock_);
|
||||
this.draggedConnectionManager_.updateAvailableConnections();
|
||||
};
|
||||
|
||||
/**
|
||||
* Fire a UI event at the start of a block drag.
|
||||
* @protected
|
||||
*/
|
||||
BlockDragger.prototype.fireDragStartEvent_ = function() {
|
||||
const event = new (eventUtils.get(eventUtils.BLOCK_DRAG))(
|
||||
this.draggingBlock_, true, this.draggingBlock_.getDescendants(false));
|
||||
eventUtils.fire(event);
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute a step of block dragging, based on the given event. Update the
|
||||
* display accordingly.
|
||||
* @param {!Event} e The most recent move event.
|
||||
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at the start of the drag, in pixel units.
|
||||
* @public
|
||||
*/
|
||||
BlockDragger.prototype.drag = function(e, currentDragDeltaXY) {
|
||||
const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
|
||||
const newLoc = Coordinate.sum(this.startXY_, delta);
|
||||
this.draggingBlock_.moveDuringDrag(newLoc);
|
||||
this.dragIcons_(delta);
|
||||
|
||||
const oldDragTarget = this.dragTarget_;
|
||||
this.dragTarget_ = this.workspace_.getDragTarget(e);
|
||||
|
||||
this.draggedConnectionManager_.update(delta, this.dragTarget_);
|
||||
const oldWouldDeleteBlock = this.wouldDeleteBlock_;
|
||||
this.wouldDeleteBlock_ = this.draggedConnectionManager_.wouldDeleteBlock();
|
||||
if (oldWouldDeleteBlock !== this.wouldDeleteBlock_) {
|
||||
// Prevent unnecessary add/remove class calls.
|
||||
this.updateCursorDuringBlockDrag_();
|
||||
}
|
||||
|
||||
// Call drag enter/exit/over after wouldDeleteBlock is called in
|
||||
// InsertionMarkerManager.update.
|
||||
if (this.dragTarget_ !== oldDragTarget) {
|
||||
oldDragTarget && oldDragTarget.onDragExit(this.draggingBlock_);
|
||||
this.dragTarget_ && this.dragTarget_.onDragEnter(this.draggingBlock_);
|
||||
}
|
||||
this.dragTarget_ && this.dragTarget_.onDragOver(this.draggingBlock_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Finish a block drag and put the block back on the workspace.
|
||||
* @param {!Event} e The mouseup/touchend event.
|
||||
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at the start of the drag, in pixel units.
|
||||
* @public
|
||||
*/
|
||||
BlockDragger.prototype.endDrag = function(e, currentDragDeltaXY) {
|
||||
// Make sure internal state is fresh.
|
||||
this.drag(e, currentDragDeltaXY);
|
||||
this.dragIconData_ = [];
|
||||
this.fireDragEndEvent_();
|
||||
|
||||
dom.stopTextWidthCache();
|
||||
|
||||
blockAnimation.disconnectUiStop();
|
||||
|
||||
const preventMove = !!this.dragTarget_ &&
|
||||
this.dragTarget_.shouldPreventMove(this.draggingBlock_);
|
||||
/** @type {Coordinate} */
|
||||
let newLoc;
|
||||
/** @type {Coordinate} */
|
||||
let delta;
|
||||
if (preventMove) {
|
||||
newLoc = this.startXY_;
|
||||
} else {
|
||||
const newValues = this.getNewLocationAfterDrag_(currentDragDeltaXY);
|
||||
delta = newValues.delta;
|
||||
newLoc = newValues.newLocation;
|
||||
}
|
||||
this.draggingBlock_.moveOffDragSurface(newLoc);
|
||||
|
||||
if (this.dragTarget_) {
|
||||
this.dragTarget_.onDrop(this.draggingBlock_);
|
||||
}
|
||||
|
||||
const deleted = this.maybeDeleteBlock_();
|
||||
if (!deleted) {
|
||||
// These are expensive and don't need to be done if we're deleting.
|
||||
this.draggingBlock_.setDragging(false);
|
||||
if (delta) { // !preventMove
|
||||
this.updateBlockAfterMove_(delta);
|
||||
} else {
|
||||
// Blocks dragged directly from a flyout may need to be bumped into
|
||||
// bounds.
|
||||
bumpObjects.bumpIntoBounds(
|
||||
this.draggingBlock_.workspace,
|
||||
this.workspace_.getMetricsManager().getScrollMetrics(true),
|
||||
this.draggingBlock_);
|
||||
}
|
||||
}
|
||||
this.workspace_.setResizesEnabled(true);
|
||||
|
||||
eventUtils.setGroup(false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates the drag delta and new location values after a block is dragged.
|
||||
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the start of the drag, in pixel units.
|
||||
* @return {{delta: !Coordinate, newLocation:
|
||||
* !Coordinate}} New location after drag. delta is in
|
||||
* workspace units. newLocation is the new coordinate where the block should
|
||||
* end up.
|
||||
* @protected
|
||||
*/
|
||||
BlockDragger.prototype.getNewLocationAfterDrag_ = function(currentDragDeltaXY) {
|
||||
const newValues = {};
|
||||
newValues.delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
|
||||
newValues.newLocation = Coordinate.sum(this.startXY_, newValues.delta);
|
||||
return newValues;
|
||||
};
|
||||
|
||||
/**
|
||||
* May delete the dragging block, if allowed. If `this.wouldDeleteBlock_` is not
|
||||
* true, the block will not be deleted. This should be called at the end of a
|
||||
* block drag.
|
||||
* @return {boolean} True if the block was deleted.
|
||||
* @protected
|
||||
*/
|
||||
BlockDragger.prototype.maybeDeleteBlock_ = function() {
|
||||
if (this.wouldDeleteBlock_) {
|
||||
// Fire a move event, so we know where to go back to for an undo.
|
||||
this.fireMoveEvent_();
|
||||
this.draggingBlock_.dispose(false, true);
|
||||
common.draggingConnections.length = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the necessary information to place a block at a certain location.
|
||||
* @param {!Coordinate} delta The change in location from where
|
||||
* the block started the drag to where it ended the drag.
|
||||
* @protected
|
||||
*/
|
||||
BlockDragger.prototype.updateBlockAfterMove_ = function(delta) {
|
||||
this.draggingBlock_.moveConnections(delta.x, delta.y);
|
||||
this.fireMoveEvent_();
|
||||
if (this.draggedConnectionManager_.wouldConnectBlock()) {
|
||||
// Applying connections also rerenders the relevant blocks.
|
||||
this.draggedConnectionManager_.applyConnections();
|
||||
} else {
|
||||
this.draggingBlock_.render();
|
||||
}
|
||||
this.draggingBlock_.scheduleSnapAndBump();
|
||||
};
|
||||
|
||||
/**
|
||||
* Fire a UI event at the end of a block drag.
|
||||
* @protected
|
||||
*/
|
||||
BlockDragger.prototype.fireDragEndEvent_ = function() {
|
||||
const event = new (eventUtils.get(eventUtils.BLOCK_DRAG))(
|
||||
this.draggingBlock_, false, this.draggingBlock_.getDescendants(false));
|
||||
eventUtils.fire(event);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds or removes the style of the cursor for the toolbox.
|
||||
* This is what changes the cursor to display an x when a deletable block is
|
||||
* held over the toolbox.
|
||||
* @param {boolean} isEnd True if we are at the end of a drag, false otherwise.
|
||||
* @protected
|
||||
*/
|
||||
BlockDragger.prototype.updateToolboxStyle_ = function(isEnd) {
|
||||
const toolbox = this.workspace_.getToolbox();
|
||||
|
||||
if (toolbox) {
|
||||
const style = this.draggingBlock_.isDeletable() ? 'blocklyToolboxDelete' :
|
||||
'blocklyToolboxGrab';
|
||||
|
||||
if (isEnd && typeof toolbox.removeStyle === 'function') {
|
||||
toolbox.removeStyle(style);
|
||||
} else if (!isEnd && typeof toolbox.addStyle === 'function') {
|
||||
toolbox.addStyle(style);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Fire a move event at the end of a block drag.
|
||||
* @protected
|
||||
*/
|
||||
BlockDragger.prototype.fireMoveEvent_ = function() {
|
||||
const event =
|
||||
new (eventUtils.get(eventUtils.BLOCK_MOVE))(this.draggingBlock_);
|
||||
event.oldCoordinate = this.startXY_;
|
||||
event.recordNew();
|
||||
eventUtils.fire(event);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the cursor (and possibly the trash can lid) to reflect whether the
|
||||
* dragging block would be deleted if released immediately.
|
||||
* @protected
|
||||
*/
|
||||
BlockDragger.prototype.updateCursorDuringBlockDrag_ = function() {
|
||||
this.draggingBlock_.setDeleteStyle(this.wouldDeleteBlock_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a coordinate object from pixels to workspace units, including a
|
||||
* correction for mutator workspaces.
|
||||
* This function does not consider differing origins. It simply scales the
|
||||
* input's x and y values.
|
||||
* @param {!Coordinate} pixelCoord A coordinate with x and y
|
||||
* values in CSS pixel units.
|
||||
* @return {!Coordinate} The input coordinate divided by the
|
||||
* workspace scale.
|
||||
* @protected
|
||||
*/
|
||||
BlockDragger.prototype.pixelsToWorkspaceUnits_ = function(pixelCoord) {
|
||||
const result = new Coordinate(
|
||||
pixelCoord.x / this.workspace_.scale,
|
||||
pixelCoord.y / this.workspace_.scale);
|
||||
if (this.workspace_.isMutator) {
|
||||
// If we're in a mutator, its scale is always 1, purely because of some
|
||||
// oddities in our rendering optimizations. The actual scale is the same as
|
||||
// the scale on the parent workspace.
|
||||
// Fix that for dragging.
|
||||
const mainScale = this.workspace_.options.parentWorkspace.scale;
|
||||
result.scale(1 / mainScale);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Move all of the icons connected to this drag.
|
||||
* @param {!Coordinate} dxy How far to move the icons from their
|
||||
* original positions, in workspace units.
|
||||
* @protected
|
||||
*/
|
||||
BlockDragger.prototype.dragIcons_ = function(dxy) {
|
||||
// Moving icons moves their associated bubbles.
|
||||
for (let i = 0; i < this.dragIconData_.length; i++) {
|
||||
const data = this.dragIconData_[i];
|
||||
data.icon.setIconLocation(Coordinate.sum(data.location, dxy));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a list of the insertion markers that currently exist. Drags have 0, 1,
|
||||
* or 2 insertion markers.
|
||||
* @return {!Array<!BlockSvg>} A possibly empty list of insertion
|
||||
* marker blocks.
|
||||
* @public
|
||||
*/
|
||||
BlockDragger.prototype.getInsertionMarkers = function() {
|
||||
// No insertion markers with the old style of dragged connection managers.
|
||||
if (this.draggedConnectionManager_ &&
|
||||
this.draggedConnectionManager_.getInsertionMarkers) {
|
||||
return this.draggedConnectionManager_.getInsertionMarkers();
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
registry.register(registry.Type.BLOCK_DRAGGER, registry.DEFAULT, BlockDragger);
|
||||
|
||||
exports.BlockDragger = BlockDragger;
|
||||
|
||||
1726
core/bubble.js
1726
core/bubble.js
File diff suppressed because it is too large
Load Diff
@@ -43,249 +43,253 @@ goog.require('Blockly.constants');
|
||||
* Class for a bubble dragger. It moves things on the bubble canvas around the
|
||||
* workspace when they are being dragged by a mouse or touch. These can be
|
||||
* block comments, mutators, warnings, or workspace comments.
|
||||
* @param {!IBubble} bubble The item on the bubble canvas to drag.
|
||||
* @param {!WorkspaceSvg} workspace The workspace to drag on.
|
||||
* @constructor
|
||||
* @alias Blockly.BubbleDragger
|
||||
*/
|
||||
const BubbleDragger = function(bubble, workspace) {
|
||||
const BubbleDragger = class {
|
||||
/**
|
||||
* The item on the bubble canvas that is being dragged.
|
||||
* @type {!IBubble}
|
||||
* @private
|
||||
* @param {!IBubble} bubble The item on the bubble canvas to drag.
|
||||
* @param {!WorkspaceSvg} workspace The workspace to drag on.
|
||||
* @alias Blockly.BubbleDragger
|
||||
*/
|
||||
this.draggingBubble_ = bubble;
|
||||
constructor(bubble, workspace) {
|
||||
/**
|
||||
* The item on the bubble canvas that is being dragged.
|
||||
* @type {!IBubble}
|
||||
* @private
|
||||
*/
|
||||
this.draggingBubble_ = bubble;
|
||||
|
||||
/**
|
||||
* The workspace on which the bubble is being dragged.
|
||||
* @type {!WorkspaceSvg}
|
||||
* @private
|
||||
*/
|
||||
this.workspace_ = workspace;
|
||||
/**
|
||||
* The workspace on which the bubble is being dragged.
|
||||
* @type {!WorkspaceSvg}
|
||||
* @private
|
||||
*/
|
||||
this.workspace_ = workspace;
|
||||
|
||||
/**
|
||||
* Which drag target the mouse pointer is over, if any.
|
||||
* @type {?IDragTarget}
|
||||
* @private
|
||||
*/
|
||||
this.dragTarget_ = null;
|
||||
/**
|
||||
* Which drag target the mouse pointer is over, if any.
|
||||
* @type {?IDragTarget}
|
||||
* @private
|
||||
*/
|
||||
this.dragTarget_ = null;
|
||||
|
||||
/**
|
||||
* Whether the bubble would be deleted if dropped immediately.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.wouldDeleteBubble_ = false;
|
||||
/**
|
||||
* Whether the bubble would be deleted if dropped immediately.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.wouldDeleteBubble_ = false;
|
||||
|
||||
/**
|
||||
* The location of the top left corner of the dragging bubble's body at the
|
||||
* beginning of the drag, in workspace coordinates.
|
||||
* @type {!Coordinate}
|
||||
* @private
|
||||
*/
|
||||
this.startXY_ = this.draggingBubble_.getRelativeToSurfaceXY();
|
||||
/**
|
||||
* The location of the top left corner of the dragging bubble's body at the
|
||||
* beginning of the drag, in workspace coordinates.
|
||||
* @type {!Coordinate}
|
||||
* @private
|
||||
*/
|
||||
this.startXY_ = this.draggingBubble_.getRelativeToSurfaceXY();
|
||||
|
||||
/**
|
||||
* The drag surface to move bubbles to during a drag, or null if none should
|
||||
* be used. Block dragging and bubble dragging use the same surface.
|
||||
* @type {BlockDragSurfaceSvg}
|
||||
* @private
|
||||
*/
|
||||
this.dragSurface_ =
|
||||
svgMath.is3dSupported() && !!workspace.getBlockDragSurface() ?
|
||||
workspace.getBlockDragSurface() :
|
||||
null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sever all links from this object.
|
||||
* @package
|
||||
* @suppress {checkTypes}
|
||||
*/
|
||||
BubbleDragger.prototype.dispose = function() {
|
||||
this.draggingBubble_ = null;
|
||||
this.workspace_ = null;
|
||||
this.dragSurface_ = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Start dragging a bubble. This includes moving it to the drag surface.
|
||||
* @package
|
||||
*/
|
||||
BubbleDragger.prototype.startBubbleDrag = function() {
|
||||
if (!eventUtils.getGroup()) {
|
||||
eventUtils.setGroup(true);
|
||||
/**
|
||||
* The drag surface to move bubbles to during a drag, or null if none should
|
||||
* be used. Block dragging and bubble dragging use the same surface.
|
||||
* @type {BlockDragSurfaceSvg}
|
||||
* @private
|
||||
*/
|
||||
this.dragSurface_ =
|
||||
svgMath.is3dSupported() && !!workspace.getBlockDragSurface() ?
|
||||
workspace.getBlockDragSurface() :
|
||||
null;
|
||||
}
|
||||
|
||||
this.workspace_.setResizesEnabled(false);
|
||||
this.draggingBubble_.setAutoLayout(false);
|
||||
if (this.dragSurface_) {
|
||||
this.moveToDragSurface_();
|
||||
/**
|
||||
* Sever all links from this object.
|
||||
* @package
|
||||
* @suppress {checkTypes}
|
||||
*/
|
||||
dispose() {
|
||||
this.draggingBubble_ = null;
|
||||
this.workspace_ = null;
|
||||
this.dragSurface_ = null;
|
||||
}
|
||||
|
||||
this.draggingBubble_.setDragging && this.draggingBubble_.setDragging(true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute a step of bubble dragging, based on the given event. Update the
|
||||
* display accordingly.
|
||||
* @param {!Event} e The most recent move event.
|
||||
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at the start of the drag, in pixel units.
|
||||
* @package
|
||||
*/
|
||||
BubbleDragger.prototype.dragBubble = function(e, currentDragDeltaXY) {
|
||||
const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
|
||||
const newLoc = Coordinate.sum(this.startXY_, delta);
|
||||
this.draggingBubble_.moveDuringDrag(this.dragSurface_, newLoc);
|
||||
|
||||
const oldDragTarget = this.dragTarget_;
|
||||
this.dragTarget_ = this.workspace_.getDragTarget(e);
|
||||
|
||||
const oldWouldDeleteBubble = this.wouldDeleteBubble_;
|
||||
this.wouldDeleteBubble_ = this.shouldDelete_(this.dragTarget_);
|
||||
if (oldWouldDeleteBubble !== this.wouldDeleteBubble_) {
|
||||
// Prevent unnecessary add/remove class calls.
|
||||
this.updateCursorDuringBubbleDrag_();
|
||||
}
|
||||
|
||||
// Call drag enter/exit/over after wouldDeleteBlock is called in shouldDelete_
|
||||
if (this.dragTarget_ !== oldDragTarget) {
|
||||
oldDragTarget && oldDragTarget.onDragExit(this.draggingBubble_);
|
||||
this.dragTarget_ && this.dragTarget_.onDragEnter(this.draggingBubble_);
|
||||
}
|
||||
this.dragTarget_ && this.dragTarget_.onDragOver(this.draggingBubble_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether ending the drag would delete the bubble.
|
||||
* @param {?IDragTarget} dragTarget The drag target that the bubblee is
|
||||
* currently over.
|
||||
* @return {boolean} Whether dropping the bubble immediately would delete the
|
||||
* block.
|
||||
* @private
|
||||
*/
|
||||
BubbleDragger.prototype.shouldDelete_ = function(dragTarget) {
|
||||
if (dragTarget) {
|
||||
const componentManager = this.workspace_.getComponentManager();
|
||||
const isDeleteArea = componentManager.hasCapability(
|
||||
dragTarget.id, ComponentManager.Capability.DELETE_AREA);
|
||||
if (isDeleteArea) {
|
||||
return (/** @type {!IDeleteArea} */ (dragTarget))
|
||||
.wouldDelete(this.draggingBubble_, false);
|
||||
/**
|
||||
* Start dragging a bubble. This includes moving it to the drag surface.
|
||||
* @package
|
||||
*/
|
||||
startBubbleDrag() {
|
||||
if (!eventUtils.getGroup()) {
|
||||
eventUtils.setGroup(true);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the cursor (and possibly the trash can lid) to reflect whether the
|
||||
* dragging bubble would be deleted if released immediately.
|
||||
* @private
|
||||
*/
|
||||
BubbleDragger.prototype.updateCursorDuringBubbleDrag_ = function() {
|
||||
this.draggingBubble_.setDeleteStyle(this.wouldDeleteBubble_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Finish a bubble drag and put the bubble back on the workspace.
|
||||
* @param {!Event} e The mouseup/touchend event.
|
||||
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at the start of the drag, in pixel units.
|
||||
* @package
|
||||
*/
|
||||
BubbleDragger.prototype.endBubbleDrag = function(e, currentDragDeltaXY) {
|
||||
// Make sure internal state is fresh.
|
||||
this.dragBubble(e, currentDragDeltaXY);
|
||||
|
||||
const preventMove = this.dragTarget_ &&
|
||||
this.dragTarget_.shouldPreventMove(this.draggingBubble_);
|
||||
let newLoc;
|
||||
if (preventMove) {
|
||||
newLoc = this.startXY_;
|
||||
} else {
|
||||
const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
|
||||
newLoc = Coordinate.sum(this.startXY_, delta);
|
||||
}
|
||||
// Move the bubble to its final location.
|
||||
this.draggingBubble_.moveTo(newLoc.x, newLoc.y);
|
||||
|
||||
if (this.dragTarget_) {
|
||||
this.dragTarget_.onDrop(this.draggingBubble_);
|
||||
}
|
||||
|
||||
if (this.wouldDeleteBubble_) {
|
||||
// Fire a move event, so we know where to go back to for an undo.
|
||||
this.fireMoveEvent_();
|
||||
this.draggingBubble_.dispose(false, true);
|
||||
} else {
|
||||
// Put everything back onto the bubble canvas.
|
||||
this.workspace_.setResizesEnabled(false);
|
||||
this.draggingBubble_.setAutoLayout(false);
|
||||
if (this.dragSurface_) {
|
||||
this.dragSurface_.clearAndHide(this.workspace_.getBubbleCanvas());
|
||||
this.moveToDragSurface_();
|
||||
}
|
||||
if (this.draggingBubble_.setDragging) {
|
||||
this.draggingBubble_.setDragging(false);
|
||||
|
||||
this.draggingBubble_.setDragging && this.draggingBubble_.setDragging(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a step of bubble dragging, based on the given event. Update the
|
||||
* display accordingly.
|
||||
* @param {!Event} e The most recent move event.
|
||||
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at the start of the drag, in pixel units.
|
||||
* @package
|
||||
*/
|
||||
dragBubble(e, currentDragDeltaXY) {
|
||||
const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
|
||||
const newLoc = Coordinate.sum(this.startXY_, delta);
|
||||
this.draggingBubble_.moveDuringDrag(this.dragSurface_, newLoc);
|
||||
|
||||
const oldDragTarget = this.dragTarget_;
|
||||
this.dragTarget_ = this.workspace_.getDragTarget(e);
|
||||
|
||||
const oldWouldDeleteBubble = this.wouldDeleteBubble_;
|
||||
this.wouldDeleteBubble_ = this.shouldDelete_(this.dragTarget_);
|
||||
if (oldWouldDeleteBubble !== this.wouldDeleteBubble_) {
|
||||
// Prevent unnecessary add/remove class calls.
|
||||
this.updateCursorDuringBubbleDrag_();
|
||||
}
|
||||
this.fireMoveEvent_();
|
||||
|
||||
// Call drag enter/exit/over after wouldDeleteBlock is called in
|
||||
// shouldDelete_
|
||||
if (this.dragTarget_ !== oldDragTarget) {
|
||||
oldDragTarget && oldDragTarget.onDragExit(this.draggingBubble_);
|
||||
this.dragTarget_ && this.dragTarget_.onDragEnter(this.draggingBubble_);
|
||||
}
|
||||
this.dragTarget_ && this.dragTarget_.onDragOver(this.draggingBubble_);
|
||||
}
|
||||
this.workspace_.setResizesEnabled(true);
|
||||
|
||||
eventUtils.setGroup(false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Fire a move event at the end of a bubble drag.
|
||||
* @private
|
||||
*/
|
||||
BubbleDragger.prototype.fireMoveEvent_ = function() {
|
||||
if (this.draggingBubble_.isComment) {
|
||||
// TODO (adodson): Resolve build errors when requiring WorkspaceCommentSvg.
|
||||
const event = new (eventUtils.get(eventUtils.COMMENT_MOVE))(
|
||||
/** @type {!WorkspaceCommentSvg} */ (this.draggingBubble_));
|
||||
event.setOldCoordinate(this.startXY_);
|
||||
event.recordNew();
|
||||
eventUtils.fire(event);
|
||||
/**
|
||||
* Whether ending the drag would delete the bubble.
|
||||
* @param {?IDragTarget} dragTarget The drag target that the bubblee is
|
||||
* currently over.
|
||||
* @return {boolean} Whether dropping the bubble immediately would delete the
|
||||
* block.
|
||||
* @private
|
||||
*/
|
||||
shouldDelete_(dragTarget) {
|
||||
if (dragTarget) {
|
||||
const componentManager = this.workspace_.getComponentManager();
|
||||
const isDeleteArea = componentManager.hasCapability(
|
||||
dragTarget.id, ComponentManager.Capability.DELETE_AREA);
|
||||
if (isDeleteArea) {
|
||||
return (/** @type {!IDeleteArea} */ (dragTarget))
|
||||
.wouldDelete(this.draggingBubble_, false);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// TODO (fenichel): move events for comments.
|
||||
return;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a coordinate object from pixels to workspace units, including a
|
||||
* correction for mutator workspaces.
|
||||
* This function does not consider differing origins. It simply scales the
|
||||
* input's x and y values.
|
||||
* @param {!Coordinate} pixelCoord A coordinate with x and y
|
||||
* values in CSS pixel units.
|
||||
* @return {!Coordinate} The input coordinate divided by the
|
||||
* workspace scale.
|
||||
* @private
|
||||
*/
|
||||
BubbleDragger.prototype.pixelsToWorkspaceUnits_ = function(pixelCoord) {
|
||||
const result = new Coordinate(
|
||||
pixelCoord.x / this.workspace_.scale,
|
||||
pixelCoord.y / this.workspace_.scale);
|
||||
if (this.workspace_.isMutator) {
|
||||
// If we're in a mutator, its scale is always 1, purely because of some
|
||||
// oddities in our rendering optimizations. The actual scale is the same as
|
||||
// the scale on the parent workspace.
|
||||
// Fix that for dragging.
|
||||
const mainScale = this.workspace_.options.parentWorkspace.scale;
|
||||
result.scale(1 / mainScale);
|
||||
/**
|
||||
* Update the cursor (and possibly the trash can lid) to reflect whether the
|
||||
* dragging bubble would be deleted if released immediately.
|
||||
* @private
|
||||
*/
|
||||
updateCursorDuringBubbleDrag_() {
|
||||
this.draggingBubble_.setDeleteStyle(this.wouldDeleteBubble_);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Move the bubble onto the drag surface at the beginning of a drag. Move the
|
||||
* drag surface to preserve the apparent location of the bubble.
|
||||
* @private
|
||||
*/
|
||||
BubbleDragger.prototype.moveToDragSurface_ = function() {
|
||||
this.draggingBubble_.moveTo(0, 0);
|
||||
this.dragSurface_.translateSurface(this.startXY_.x, this.startXY_.y);
|
||||
// Execute the move on the top-level SVG component.
|
||||
this.dragSurface_.setBlocksAndShow(this.draggingBubble_.getSvgRoot());
|
||||
/**
|
||||
* Finish a bubble drag and put the bubble back on the workspace.
|
||||
* @param {!Event} e The mouseup/touchend event.
|
||||
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at the start of the drag, in pixel units.
|
||||
* @package
|
||||
*/
|
||||
endBubbleDrag(e, currentDragDeltaXY) {
|
||||
// Make sure internal state is fresh.
|
||||
this.dragBubble(e, currentDragDeltaXY);
|
||||
|
||||
const preventMove = this.dragTarget_ &&
|
||||
this.dragTarget_.shouldPreventMove(this.draggingBubble_);
|
||||
let newLoc;
|
||||
if (preventMove) {
|
||||
newLoc = this.startXY_;
|
||||
} else {
|
||||
const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
|
||||
newLoc = Coordinate.sum(this.startXY_, delta);
|
||||
}
|
||||
// Move the bubble to its final location.
|
||||
this.draggingBubble_.moveTo(newLoc.x, newLoc.y);
|
||||
|
||||
if (this.dragTarget_) {
|
||||
this.dragTarget_.onDrop(this.draggingBubble_);
|
||||
}
|
||||
|
||||
if (this.wouldDeleteBubble_) {
|
||||
// Fire a move event, so we know where to go back to for an undo.
|
||||
this.fireMoveEvent_();
|
||||
this.draggingBubble_.dispose(false, true);
|
||||
} else {
|
||||
// Put everything back onto the bubble canvas.
|
||||
if (this.dragSurface_) {
|
||||
this.dragSurface_.clearAndHide(this.workspace_.getBubbleCanvas());
|
||||
}
|
||||
if (this.draggingBubble_.setDragging) {
|
||||
this.draggingBubble_.setDragging(false);
|
||||
}
|
||||
this.fireMoveEvent_();
|
||||
}
|
||||
this.workspace_.setResizesEnabled(true);
|
||||
|
||||
eventUtils.setGroup(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire a move event at the end of a bubble drag.
|
||||
* @private
|
||||
*/
|
||||
fireMoveEvent_() {
|
||||
if (this.draggingBubble_.isComment) {
|
||||
// TODO (adodson): Resolve build errors when requiring
|
||||
// WorkspaceCommentSvg.
|
||||
const event = new (eventUtils.get(eventUtils.COMMENT_MOVE))(
|
||||
/** @type {!WorkspaceCommentSvg} */ (this.draggingBubble_));
|
||||
event.setOldCoordinate(this.startXY_);
|
||||
event.recordNew();
|
||||
eventUtils.fire(event);
|
||||
}
|
||||
// TODO (fenichel): move events for comments.
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a coordinate object from pixels to workspace units, including a
|
||||
* correction for mutator workspaces.
|
||||
* This function does not consider differing origins. It simply scales the
|
||||
* input's x and y values.
|
||||
* @param {!Coordinate} pixelCoord A coordinate with x and y
|
||||
* values in CSS pixel units.
|
||||
* @return {!Coordinate} The input coordinate divided by the
|
||||
* workspace scale.
|
||||
* @private
|
||||
*/
|
||||
pixelsToWorkspaceUnits_(pixelCoord) {
|
||||
const result = new Coordinate(
|
||||
pixelCoord.x / this.workspace_.scale,
|
||||
pixelCoord.y / this.workspace_.scale);
|
||||
if (this.workspace_.isMutator) {
|
||||
// If we're in a mutator, its scale is always 1, purely because of some
|
||||
// oddities in our rendering optimizations. The actual scale is the same
|
||||
// as the scale on the parent workspace. Fix that for dragging.
|
||||
const mainScale = this.workspace_.options.parentWorkspace.scale;
|
||||
result.scale(1 / mainScale);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the bubble onto the drag surface at the beginning of a drag. Move the
|
||||
* drag surface to preserve the apparent location of the bubble.
|
||||
* @private
|
||||
*/
|
||||
moveToDragSurface_() {
|
||||
this.draggingBubble_.moveTo(0, 0);
|
||||
this.dragSurface_.translateSurface(this.startXY_.x, this.startXY_.y);
|
||||
// Execute the move on the top-level SVG component.
|
||||
this.dragSurface_.setBlocksAndShow(this.draggingBubble_.getSvgRoot());
|
||||
}
|
||||
};
|
||||
|
||||
exports.BubbleDragger = BubbleDragger;
|
||||
|
||||
@@ -21,116 +21,118 @@ goog.module('Blockly.utils.Coordinate');
|
||||
|
||||
/**
|
||||
* Class for representing coordinates and positions.
|
||||
* @param {number} x Left.
|
||||
* @param {number} y Top.
|
||||
* @struct
|
||||
* @constructor
|
||||
* @alias Blockly.utils.Coordinate
|
||||
*/
|
||||
const Coordinate = function(x, y) {
|
||||
const Coordinate = class {
|
||||
/**
|
||||
* X-value
|
||||
* @type {number}
|
||||
* @param {number} x Left.
|
||||
* @param {number} y Top.
|
||||
* @alias Blockly.utils.Coordinate
|
||||
*/
|
||||
this.x = x;
|
||||
constructor(x, y) {
|
||||
/**
|
||||
* X-value
|
||||
* @type {number}
|
||||
*/
|
||||
this.x = x;
|
||||
|
||||
/**
|
||||
* Y-value
|
||||
* @type {number}
|
||||
*/
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Y-value
|
||||
* @type {number}
|
||||
* Creates a new copy of this coordinate.
|
||||
* @return {!Coordinate} A copy of this coordinate.
|
||||
*/
|
||||
this.y = y;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compares coordinates for equality.
|
||||
* @param {?Coordinate} a A Coordinate.
|
||||
* @param {?Coordinate} b A Coordinate.
|
||||
* @return {boolean} True iff the coordinates are equal, or if both are null.
|
||||
*/
|
||||
Coordinate.equals = function(a, b) {
|
||||
if (a === b) {
|
||||
return true;
|
||||
clone() {
|
||||
return new Coordinate(this.x, this.y);
|
||||
}
|
||||
if (!a || !b) {
|
||||
return false;
|
||||
|
||||
/**
|
||||
* Scales this coordinate by the given scale factor.
|
||||
* @param {number} s The scale factor to use for both x and y dimensions.
|
||||
* @return {!Coordinate} This coordinate after scaling.
|
||||
*/
|
||||
scale(s) {
|
||||
this.x *= s;
|
||||
this.y *= s;
|
||||
return this;
|
||||
}
|
||||
return a.x === b.x && a.y === b.y;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the distance between two coordinates.
|
||||
* @param {!Coordinate} a A Coordinate.
|
||||
* @param {!Coordinate} b A Coordinate.
|
||||
* @return {number} The distance between `a` and `b`.
|
||||
*/
|
||||
Coordinate.distance = function(a, b) {
|
||||
const dx = a.x - b.x;
|
||||
const dy = a.y - b.y;
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
};
|
||||
/**
|
||||
* Translates this coordinate by the given offsets.
|
||||
* respectively.
|
||||
* @param {number} tx The value to translate x by.
|
||||
* @param {number} ty The value to translate y by.
|
||||
* @return {!Coordinate} This coordinate after translating.
|
||||
*/
|
||||
translate(tx, ty) {
|
||||
this.x += tx;
|
||||
this.y += ty;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the magnitude of a coordinate.
|
||||
* @param {!Coordinate} a A Coordinate.
|
||||
* @return {number} The distance between the origin and `a`.
|
||||
*/
|
||||
Coordinate.magnitude = function(a) {
|
||||
return Math.sqrt(a.x * a.x + a.y * a.y);
|
||||
};
|
||||
/**
|
||||
* Compares coordinates for equality.
|
||||
* @param {?Coordinate} a A Coordinate.
|
||||
* @param {?Coordinate} b A Coordinate.
|
||||
* @return {boolean} True iff the coordinates are equal, or if both are null.
|
||||
*/
|
||||
static equals(a, b) {
|
||||
if (a === b) {
|
||||
return true;
|
||||
}
|
||||
if (!a || !b) {
|
||||
return false;
|
||||
}
|
||||
return a.x === b.x && a.y === b.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the difference between two coordinates as a new
|
||||
* Coordinate.
|
||||
* @param {!Coordinate|!SVGPoint} a An x/y coordinate.
|
||||
* @param {!Coordinate|!SVGPoint} b An x/y coordinate.
|
||||
* @return {!Coordinate} A Coordinate representing the difference
|
||||
* between `a` and `b`.
|
||||
*/
|
||||
Coordinate.difference = function(a, b) {
|
||||
return new Coordinate(a.x - b.x, a.y - b.y);
|
||||
};
|
||||
/**
|
||||
* Returns the distance between two coordinates.
|
||||
* @param {!Coordinate} a A Coordinate.
|
||||
* @param {!Coordinate} b A Coordinate.
|
||||
* @return {number} The distance between `a` and `b`.
|
||||
*/
|
||||
static distance(a, b) {
|
||||
const dx = a.x - b.x;
|
||||
const dy = a.y - b.y;
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of two coordinates as a new Coordinate.
|
||||
* @param {!Coordinate|!SVGPoint} a An x/y coordinate.
|
||||
* @param {!Coordinate|!SVGPoint} b An x/y coordinate.
|
||||
* @return {!Coordinate} A Coordinate representing the sum of
|
||||
* the two coordinates.
|
||||
*/
|
||||
Coordinate.sum = function(a, b) {
|
||||
return new Coordinate(a.x + b.x, a.y + b.y);
|
||||
};
|
||||
/**
|
||||
* Returns the magnitude of a coordinate.
|
||||
* @param {!Coordinate} a A Coordinate.
|
||||
* @return {number} The distance between the origin and `a`.
|
||||
*/
|
||||
static magnitude(a) {
|
||||
return Math.sqrt(a.x * a.x + a.y * a.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new copy of this coordinate.
|
||||
* @return {!Coordinate} A copy of this coordinate.
|
||||
*/
|
||||
Coordinate.prototype.clone = function() {
|
||||
return new Coordinate(this.x, this.y);
|
||||
};
|
||||
/**
|
||||
* Returns the difference between two coordinates as a new
|
||||
* Coordinate.
|
||||
* @param {!Coordinate|!SVGPoint} a An x/y coordinate.
|
||||
* @param {!Coordinate|!SVGPoint} b An x/y coordinate.
|
||||
* @return {!Coordinate} A Coordinate representing the difference
|
||||
* between `a` and `b`.
|
||||
*/
|
||||
static difference(a, b) {
|
||||
return new Coordinate(a.x - b.x, a.y - b.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales this coordinate by the given scale factor.
|
||||
* @param {number} s The scale factor to use for both x and y dimensions.
|
||||
* @return {!Coordinate} This coordinate after scaling.
|
||||
*/
|
||||
Coordinate.prototype.scale = function(s) {
|
||||
this.x *= s;
|
||||
this.y *= s;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Translates this coordinate by the given offsets.
|
||||
* respectively.
|
||||
* @param {number} tx The value to translate x by.
|
||||
* @param {number} ty The value to translate y by.
|
||||
* @return {!Coordinate} This coordinate after translating.
|
||||
*/
|
||||
Coordinate.prototype.translate = function(tx, ty) {
|
||||
this.x += tx;
|
||||
this.y += ty;
|
||||
return this;
|
||||
/**
|
||||
* Returns the sum of two coordinates as a new Coordinate.
|
||||
* @param {!Coordinate|!SVGPoint} a An x/y coordinate.
|
||||
* @param {!Coordinate|!SVGPoint} b An x/y coordinate.
|
||||
* @return {!Coordinate} A Coordinate representing the sum of
|
||||
* the two coordinates.
|
||||
*/
|
||||
static sum(a, b) {
|
||||
return new Coordinate(a.x + b.x, a.y + b.y);
|
||||
}
|
||||
};
|
||||
|
||||
exports.Coordinate = Coordinate;
|
||||
|
||||
@@ -22,50 +22,54 @@ goog.module('Blockly.utils.Rect');
|
||||
|
||||
/**
|
||||
* Class for representing rectangular regions.
|
||||
* @param {number} top Top.
|
||||
* @param {number} bottom Bottom.
|
||||
* @param {number} left Left.
|
||||
* @param {number} right Right.
|
||||
* @struct
|
||||
* @constructor
|
||||
* @alias Blockly.utils.Rect
|
||||
*/
|
||||
const Rect = function(top, bottom, left, right) {
|
||||
/** @type {number} */
|
||||
this.top = top;
|
||||
const Rect = class {
|
||||
/**
|
||||
* @param {number} top Top.
|
||||
* @param {number} bottom Bottom.
|
||||
* @param {number} left Left.
|
||||
* @param {number} right Right.
|
||||
* @struct
|
||||
* @alias Blockly.utils.Rect
|
||||
*/
|
||||
constructor(top, bottom, left, right) {
|
||||
/** @type {number} */
|
||||
this.top = top;
|
||||
|
||||
/** @type {number} */
|
||||
this.bottom = bottom;
|
||||
/** @type {number} */
|
||||
this.bottom = bottom;
|
||||
|
||||
/** @type {number} */
|
||||
this.left = left;
|
||||
/** @type {number} */
|
||||
this.left = left;
|
||||
|
||||
/** @type {number} */
|
||||
this.right = right;
|
||||
};
|
||||
/** @type {number} */
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether this rectangle contains a x/y coordinate.
|
||||
*
|
||||
* @param {number} x The x coordinate to test for containment.
|
||||
* @param {number} y The y coordinate to test for containment.
|
||||
* @return {boolean} Whether this rectangle contains given coordinate.
|
||||
*/
|
||||
Rect.prototype.contains = function(x, y) {
|
||||
return x >= this.left && x <= this.right && y >= this.top && y <= this.bottom;
|
||||
};
|
||||
/**
|
||||
* Tests whether this rectangle contains a x/y coordinate.
|
||||
*
|
||||
* @param {number} x The x coordinate to test for containment.
|
||||
* @param {number} y The y coordinate to test for containment.
|
||||
* @return {boolean} Whether this rectangle contains given coordinate.
|
||||
*/
|
||||
contains(x, y) {
|
||||
return x >= this.left && x <= this.right && y >= this.top &&
|
||||
y <= this.bottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether this rectangle intersects the provided rectangle.
|
||||
* Assumes that the coordinate system increases going down and left.
|
||||
* @param {!Rect} other The other rectangle to check for
|
||||
* intersection with.
|
||||
* @return {boolean} Whether this rectangle intersects the provided rectangle.
|
||||
*/
|
||||
Rect.prototype.intersects = function(other) {
|
||||
return !(
|
||||
this.left > other.right || this.right < other.left ||
|
||||
this.top > other.bottom || this.bottom < other.top);
|
||||
/**
|
||||
* Tests whether this rectangle intersects the provided rectangle.
|
||||
* Assumes that the coordinate system increases going down and left.
|
||||
* @param {!Rect} other The other rectangle to check for
|
||||
* intersection with.
|
||||
* @return {boolean} Whether this rectangle intersects the provided rectangle.
|
||||
*/
|
||||
intersects(other) {
|
||||
return !(
|
||||
this.left > other.right || this.right < other.left ||
|
||||
this.top > other.bottom || this.bottom < other.top);
|
||||
}
|
||||
};
|
||||
|
||||
exports.Rect = Rect;
|
||||
|
||||
@@ -22,41 +22,44 @@ goog.module('Blockly.utils.Size');
|
||||
|
||||
/**
|
||||
* Class for representing sizes consisting of a width and height.
|
||||
* @param {number} width Width.
|
||||
* @param {number} height Height.
|
||||
* @struct
|
||||
* @constructor
|
||||
* @alias Blockly.utils.Size
|
||||
*/
|
||||
const Size = function(width, height) {
|
||||
const Size = class {
|
||||
/**
|
||||
* Width
|
||||
* @type {number}
|
||||
* @param {number} width Width.
|
||||
* @param {number} height Height.
|
||||
* @struct
|
||||
* @alias Blockly.utils.Size
|
||||
*/
|
||||
this.width = width;
|
||||
constructor(width, height) {
|
||||
/**
|
||||
* Width
|
||||
* @type {number}
|
||||
*/
|
||||
this.width = width;
|
||||
|
||||
/**
|
||||
* Height
|
||||
* @type {number}
|
||||
*/
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Height
|
||||
* @type {number}
|
||||
* Compares sizes for equality.
|
||||
* @param {?Size} a A Size.
|
||||
* @param {?Size} b A Size.
|
||||
* @return {boolean} True iff the sizes have equal widths and equal
|
||||
* heights, or if both are null.
|
||||
*/
|
||||
this.height = height;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compares sizes for equality.
|
||||
* @param {?Size} a A Size.
|
||||
* @param {?Size} b A Size.
|
||||
* @return {boolean} True iff the sizes have equal widths and equal
|
||||
* heights, or if both are null.
|
||||
*/
|
||||
Size.equals = function(a, b) {
|
||||
if (a === b) {
|
||||
return true;
|
||||
static equals(a, b) {
|
||||
if (a === b) {
|
||||
return true;
|
||||
}
|
||||
if (!a || !b) {
|
||||
return false;
|
||||
}
|
||||
return a.width === b.width && a.height === b.height;
|
||||
}
|
||||
if (!a || !b) {
|
||||
return false;
|
||||
}
|
||||
return a.width === b.width && a.height === b.height;
|
||||
};
|
||||
|
||||
exports.Size = Size;
|
||||
|
||||
Reference in New Issue
Block a user