mirror of
https://github.com/google/blockly.git
synced 2026-05-13 07:30:10 +02:00
29e1f0cb03
* fix: relative path for deprecation utils * fix: checking if properties exist in svg_math * fix: set all timeout PIDs to AnyDuringMigration * fix: make nullability errors explicity in block drag surface * fix: make null check in events_block_change explicit * fix: make getEventWorkspace_ internal so we can access it from CommentCreateDeleteHelper * fix: rename DIV -> containerDiv in tooltip * fix: ignore backwards compat check in category * fix: set block styles to AnyDuringMigration * fix: type typo in KeyboardShortcut * fix: constants name in row measurables * fix: typecast in mutator * fix: populateProcedures type of flattened array * fix: ignore errors related to workspace comment deserialization * chore: format files * fix: renaming imports missing file extensions * fix: remove check for sound.play * fix: temporarily remove bad requireType. All `export type` statements are stripped when tsc is run. This means that when we attempt to require BlockDefinition from the block files, we get an error because it does not exist. We decided to temporarily remove the require, because this will no longer be a problem when we conver the blocks to typescript, and everything gets compiled together. * fix: bad jsdoc in array * fix: silence missing property errors Closure was complaining about inexistant properties, but they actually do exist, they're just not being transpiled by tsc in a way that closure understands. I.E. if things are initialized in a function called by the constructor, rather than in a class field or in the custructor itself, closure would error. It would also error on enums, because they are transpiled to a weird IIFE. * fix: context menu action handler not knowing the type of this. this: TypeX information gets stripped when tsc is run, so closure could not know that this was not global. Fixed this by reorganizing to use the option object directly instead of passing it to onAction to be bound to this. * fix: readd getDeveloperVars checks (should not be part of migration) This was found because ALL_DEVELOPER_VARS_WARNINGS_BY_BLOCK_TYPE was no longer being accessed. * fix: silence closure errors about overriding supertype props We propertly define the overrides in typescript, but these get removed from the compiled output, so closure doesn't know they exist. * fix: silence globalThis errors this: TypeX annotations get stripped from the compiled output, so closure can't know that we're accessing the correct things. However, typescript makes sure that this always has the correct properties, so silencing this should be fine. * fix: bad jsdoc name * chore: attempt compiling with blockly.js * fix: attempt moving the import statement above the namespace line * chore: add todo comments to block def files * chore: remove todo from context menu * chore: add comments abotu disabled errors
263 lines
9.1 KiB
TypeScript
263 lines
9.1 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2018 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @fileoverview Methods for dragging a bubble visually.
|
|
*/
|
|
|
|
/**
|
|
* Methods for dragging a bubble visually.
|
|
* @class
|
|
*/
|
|
import * as goog from '../closure/goog/goog.js';
|
|
goog.declareModuleId('Blockly.BubbleDragger');
|
|
|
|
// Unused import preserved for side-effects. Remove if unneeded.
|
|
import './bubble.js';
|
|
// Unused import preserved for side-effects. Remove if unneeded.
|
|
import './constants.js';
|
|
|
|
/* eslint-disable-next-line no-unused-vars */
|
|
import {BlockDragSurfaceSvg} from './block_drag_surface.js';
|
|
import {ComponentManager} from './component_manager.js';
|
|
/* eslint-disable-next-line no-unused-vars */
|
|
import {CommentMove} from './events/events_comment_move.js';
|
|
import * as eventUtils from './events/utils.js';
|
|
/* eslint-disable-next-line no-unused-vars */
|
|
import {IBubble} from './interfaces/i_bubble.js';
|
|
/* eslint-disable-next-line no-unused-vars */
|
|
import {IDeleteArea} from './interfaces/i_delete_area.js';
|
|
/* eslint-disable-next-line no-unused-vars */
|
|
import {IDragTarget} from './interfaces/i_drag_target.js';
|
|
import {Coordinate} from './utils/coordinate.js';
|
|
import * as svgMath from './utils/svg_math.js';
|
|
import {WorkspaceCommentSvg} from './workspace_comment_svg.js';
|
|
/* eslint-disable-next-line no-unused-vars */
|
|
import {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.
|
|
* @alias Blockly.BubbleDragger
|
|
*/
|
|
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;
|
|
private dragSurface_: BlockDragSurfaceSvg;
|
|
|
|
/**
|
|
* @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();
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
// AnyDuringMigration because: Type 'BlockDragSurfaceSvg | null' is not
|
|
// assignable to type 'BlockDragSurfaceSvg'.
|
|
this.dragSurface_ =
|
|
(svgMath.is3dSupported() && !!workspace.getBlockDragSurface() ?
|
|
workspace.getBlockDragSurface() :
|
|
null) as AnyDuringMigration;
|
|
}
|
|
|
|
/**
|
|
* Sever all links from this object.
|
|
* @suppress {checkTypes}
|
|
* @internal
|
|
*/
|
|
dispose() {
|
|
// AnyDuringMigration because: Type 'null' is not assignable to type
|
|
// 'IBubble'.
|
|
this.bubble = null as AnyDuringMigration;
|
|
// AnyDuringMigration because: Type 'null' is not assignable to type
|
|
// 'WorkspaceSvg'.
|
|
this.workspace = null as AnyDuringMigration;
|
|
// AnyDuringMigration because: Type 'null' is not assignable to type
|
|
// 'BlockDragSurfaceSvg'.
|
|
this.dragSurface_ = null as AnyDuringMigration;
|
|
}
|
|
|
|
/**
|
|
* Start dragging a bubble. This includes moving it to the drag surface.
|
|
* @internal
|
|
*/
|
|
startBubbleDrag() {
|
|
if (!eventUtils.getGroup()) {
|
|
eventUtils.setGroup(true);
|
|
}
|
|
|
|
this.workspace.setResizesEnabled(false);
|
|
this.bubble.setAutoLayout(false);
|
|
if (this.dragSurface_) {
|
|
this.moveToDragSurface_();
|
|
}
|
|
|
|
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: Event, currentDragDeltaXY: Coordinate) {
|
|
const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
|
|
const newLoc = Coordinate.sum(this.startXY_, delta);
|
|
this.bubble.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.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.
|
|
* @return 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 mouseup/touchend event.
|
|
* @param currentDragDeltaXY How far the pointer has moved from the position
|
|
* at the start of the drag, in pixel units.
|
|
* @internal
|
|
*/
|
|
endBubbleDrag(e: Event, 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.dragSurface_) {
|
|
this.dragSurface_.clearAndHide(this.workspace.getBubbleCanvas());
|
|
}
|
|
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.
|
|
* @return 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;
|
|
}
|
|
|
|
/**
|
|
* 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.bubble.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.bubble.getSvgRoot());
|
|
}
|
|
}
|