mirror of
https://github.com/google/blockly.git
synced 2026-01-04 15:40:08 +01:00
* chore: move functions from utils to more specific files * chore: use new names for utils functions * chore: run clang-format * chore: add deprecation warnings back to utils.js
292 lines
9.3 KiB
JavaScript
292 lines
9.3 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2018 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @fileoverview Methods for dragging a bubble visually.
|
|
*/
|
|
'use strict';
|
|
|
|
/**
|
|
* Methods for dragging a bubble visually.
|
|
* @class
|
|
*/
|
|
goog.module('Blockly.BubbleDragger');
|
|
|
|
const eventUtils = goog.require('Blockly.Events.utils');
|
|
const svgMath = goog.require('Blockly.utils.svgMath');
|
|
/* eslint-disable-next-line no-unused-vars */
|
|
const {BlockDragSurfaceSvg} = goog.requireType('Blockly.BlockDragSurfaceSvg');
|
|
const {ComponentManager} = goog.require('Blockly.ComponentManager');
|
|
const {Coordinate} = goog.require('Blockly.utils.Coordinate');
|
|
/* eslint-disable-next-line no-unused-vars */
|
|
const {IBubble} = goog.requireType('Blockly.IBubble');
|
|
/* eslint-disable-next-line no-unused-vars */
|
|
const {IDeleteArea} = goog.requireType('Blockly.IDeleteArea');
|
|
/* eslint-disable-next-line no-unused-vars */
|
|
const {IDragTarget} = goog.requireType('Blockly.IDragTarget');
|
|
/* eslint-disable-next-line no-unused-vars */
|
|
const {WorkspaceCommentSvg} = goog.requireType('Blockly.WorkspaceCommentSvg');
|
|
/* eslint-disable-next-line no-unused-vars */
|
|
const {WorkspaceSvg} = goog.requireType('Blockly.WorkspaceSvg');
|
|
/** @suppress {extraRequire} */
|
|
goog.require('Blockly.Bubble');
|
|
/** @suppress {extraRequire} */
|
|
goog.require('Blockly.Events.CommentMove');
|
|
/** @suppress {extraRequire} */
|
|
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) {
|
|
/**
|
|
* 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;
|
|
|
|
/**
|
|
* 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;
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
this.workspace_.setResizesEnabled(false);
|
|
this.draggingBubble_.setAutoLayout(false);
|
|
if (this.dragSurface_) {
|
|
this.moveToDragSurface_();
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
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.
|
|
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
|
|
*/
|
|
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);
|
|
}
|
|
// 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);
|
|
}
|
|
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());
|
|
};
|
|
|
|
exports.BubbleDragger = BubbleDragger;
|