mirror of
https://github.com/google/blockly.git
synced 2026-01-05 08:00:09 +01:00
* fix(build): Restore erroneously-deleted filter function This was deleted in PR #7406 as it was mainly being used to filter core/ vs. test/mocha/ deps into separate deps files - but it turns out also to be used for filtering error messages too. Oops. * refactor(tests): Migrate advanced compilation test to ES Modules * refactor(build): Migrate main.js to TypeScript This turns out to be pretty straight forward, even if it would cause crashing if one actually tried to import this module instead of just feeding it to Closure Compiler. * chore(build): Remove goog.declareModuleId calls Replace goog.declareModuleId calls with a comment recording the former module ID for posterity (or at least until we decide how to reformat the renamings file. * chore(tests): Delete closure/goog/* For the moment we still need something to serve as base.js for the benefit of closure-make-deps, so we keep a vestigial base.js around, containing only the @provideGoog declaration. * refactor(build): Remove vestigial base.js By changing slightly the command line arguments to closure-make-deps and closure-calculate-chunks the need to have any base.js is eliminated. * chore: Typo fix for PR #7415
212 lines
6.8 KiB
TypeScript
212 lines
6.8 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2018 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* Methods for dragging a bubble visually.
|
|
*
|
|
* @class
|
|
*/
|
|
// Former goog.module ID: 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);
|
|
if ((this.bubble as AnyDuringMigration).setAutoLayout) {
|
|
(this.bubble as AnyDuringMigration).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;
|
|
}
|
|
}
|