Files
blockly/core/bubble_dragger.ts
Rachel Fenichel 1d1a927628 chore: remove alias comments (#6816)
* chore: remove alias comments

* chore: format

* chore: remove extra newlines

* chore: fix bad replaces
2023-02-06 10:08:55 -08:00

205 lines
6.8 KiB
TypeScript

/**
* @license
* Copyright 2018 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Methods for dragging a bubble visually.
*
* @class
*/
import * as goog from '../closure/goog/goog.js';
goog.declareModuleId('Blockly.BubbleDragger');
import {ComponentManager} from './component_manager.js';
import type {CommentMove} from './events/events_comment_move.js';
import * as eventUtils from './events/utils.js';
import type {IBubble} from './interfaces/i_bubble.js';
import type {IDeleteArea} from './interfaces/i_delete_area.js';
import type {IDragTarget} from './interfaces/i_drag_target.js';
import {Coordinate} from './utils/coordinate.js';
import {WorkspaceCommentSvg} from './workspace_comment_svg.js';
import type {WorkspaceSvg} from './workspace_svg.js';
/**
* 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.
*/
export class BubbleDragger {
/** Which drag target the mouse pointer is over, if any. */
private dragTarget_: IDragTarget|null = null;
/** Whether the bubble would be deleted if dropped immediately. */
private wouldDeleteBubble_ = false;
private readonly startXY_: Coordinate;
/**
* @param bubble The item on the bubble canvas to drag.
* @param workspace The workspace to drag on.
*/
constructor(private bubble: IBubble, private workspace: WorkspaceSvg) {
/**
* The location of the top left corner of the dragging bubble's body at the
* beginning of the drag, in workspace coordinates.
*/
this.startXY_ = this.bubble.getRelativeToSurfaceXY();
}
/**
* Start dragging a bubble.
*
* @internal
*/
startBubbleDrag() {
if (!eventUtils.getGroup()) {
eventUtils.setGroup(true);
}
this.workspace.setResizesEnabled(false);
this.bubble.setAutoLayout(false);
this.bubble.setDragging && this.bubble.setDragging(true);
}
/**
* Execute a step of bubble dragging, based on the given event. Update the
* display accordingly.
*
* @param e The most recent move event.
* @param currentDragDeltaXY How far the pointer has moved from the position
* at the start of the drag, in pixel units.
* @internal
*/
dragBubble(e: PointerEvent, currentDragDeltaXY: Coordinate) {
const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
const newLoc = Coordinate.sum(this.startXY_, delta);
this.bubble.moveDuringDrag(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.bubble);
this.dragTarget_ && this.dragTarget_.onDragEnter(this.bubble);
}
this.dragTarget_ && this.dragTarget_.onDragOver(this.bubble);
}
/**
* Whether ending the drag would delete the bubble.
*
* @param dragTarget The drag target that the bubblee is currently over.
* @returns Whether dropping the bubble immediately would delete the block.
*/
private shouldDelete_(dragTarget: IDragTarget|null): boolean {
if (dragTarget) {
const componentManager = this.workspace.getComponentManager();
const isDeleteArea = componentManager.hasCapability(
dragTarget.id, ComponentManager.Capability.DELETE_AREA);
if (isDeleteArea) {
return (dragTarget as IDeleteArea).wouldDelete(this.bubble, 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 updateCursorDuringBubbleDrag_() {
this.bubble.setDeleteStyle(this.wouldDeleteBubble_);
}
/**
* Finish a bubble drag and put the bubble back on the workspace.
*
* @param e The pointerup event.
* @param currentDragDeltaXY How far the pointer has moved from the position
* at the start of the drag, in pixel units.
* @internal
*/
endBubbleDrag(e: PointerEvent, currentDragDeltaXY: Coordinate) {
// Make sure internal state is fresh.
this.dragBubble(e, currentDragDeltaXY);
const preventMove =
this.dragTarget_ && this.dragTarget_.shouldPreventMove(this.bubble);
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.bubble.moveTo(newLoc.x, newLoc.y);
if (this.dragTarget_) {
this.dragTarget_.onDrop(this.bubble);
}
if (this.wouldDeleteBubble_) {
// Fire a move event, so we know where to go back to for an undo.
this.fireMoveEvent_();
this.bubble.dispose();
} else {
// Put everything back onto the bubble canvas.
if (this.bubble.setDragging) {
this.bubble.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.bubble instanceof WorkspaceCommentSvg) {
const event = new (eventUtils.get(eventUtils.COMMENT_MOVE))(
this.bubble) as CommentMove;
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 pixelCoord A coordinate with x and y values in CSS pixel units.
* @returns The input coordinate divided by the workspace scale.
*/
private pixelsToWorkspaceUnits_(pixelCoord: Coordinate): Coordinate {
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;
}
}