diff --git a/core/block_svg.ts b/core/block_svg.ts index b7327802db9..4274362178c 100644 --- a/core/block_svg.ts +++ b/core/block_svg.ts @@ -36,7 +36,7 @@ import type {Input} from './inputs/input.js'; import type {IASTNodeLocationSvg} from './interfaces/i_ast_node_location_svg.js'; import type {IBoundedElement} from './interfaces/i_bounded_element.js'; import type {ICopyable} from './interfaces/i_copyable.js'; -import type {IDraggable} from './interfaces/i_draggable.old.js'; +import type {IDragStrategy, IDraggable} from './interfaces/i_draggable.js'; import {IIcon} from './interfaces/i_icon.js'; import * as internalConstants from './internal_constants.js'; import {ASTNode} from './keyboard_nav/ast_node.js'; @@ -60,6 +60,7 @@ import type {WorkspaceSvg} from './workspace_svg.js'; import * as renderManagement from './render_management.js'; import {IconType} from './icons/icon_types.js'; import {BlockCopyData, BlockPaster} from './clipboard/block_paster.js'; +import {BlockDragStrategy} from './dragging/block_drag_strategy.js'; /** * Class for a block's SVG representation. @@ -154,6 +155,8 @@ export class BlockSvg */ relativeCoords = new Coordinate(0, 0); + private dragStrategy: IDragStrategy = new BlockDragStrategy(this); + /** * @param workspace The block's workspace. * @param prototypeName Name of the language object containing type-specific @@ -1622,4 +1625,34 @@ export class BlockSvg add, ); } + + /** Sets the drag strategy for this block. */ + setDragStrategy(dragStrategy: IDragStrategy) { + this.dragStrategy = dragStrategy; + } + + /** Returns whether this block is movable or not. */ + override isMovable(): boolean { + return this.dragStrategy.isMovable(); + } + + /** Starts a drag on the block. */ + startDrag(e?: PointerEvent): void { + this.dragStrategy.startDrag(e); + } + + /** Drags the block to the given location. */ + drag(newLoc: Coordinate, e?: PointerEvent): void { + this.dragStrategy.drag(newLoc, e); + } + + /** Ends the drag on the block. */ + endDrag(e?: PointerEvent): void { + this.dragStrategy.endDrag(e); + } + + /** Moves the block back to where it was at the start of a drag. */ + revertDrag(): void { + this.dragStrategy.revertDrag(); + } } diff --git a/core/blockly.ts b/core/blockly.ts index 233fa31a768..dfcc6a1cb1f 100644 --- a/core/blockly.ts +++ b/core/blockly.ts @@ -40,6 +40,7 @@ import * as comments from './comments.js'; import * as Css from './css.js'; import {DeleteArea} from './delete_area.js'; import * as dialog from './dialog.js'; +import {Dragger} from './dragging/dragger.js'; import {DragTarget} from './drag_target.js'; import * as dropDownDiv from './dropdowndiv.js'; import * as Events from './events/events.js'; @@ -466,6 +467,7 @@ export {ContextMenuRegistry}; export {comments}; export {Cursor}; export {DeleteArea}; +export {Dragger}; export {DragTarget}; export const DropDownDiv = dropDownDiv; export {Field, FieldConfig, FieldValidator, UnattachedFieldError}; diff --git a/core/dragging/block_drag_strategy.ts b/core/dragging/block_drag_strategy.ts index ea28173b279..559a6080a0b 100644 --- a/core/dragging/block_drag_strategy.ts +++ b/core/dragging/block_drag_strategy.ts @@ -390,6 +390,9 @@ export class BlockDragStrategy implements IDragStrategy { ); } + this.startChildConn = null; + this.startParentConn = null; + this.connectionPreviewer!.hidePreview(); this.connectionCandidate = null; diff --git a/core/dragging/dragger.ts b/core/dragging/dragger.ts index d435e02bbef..ded1dda10a9 100644 --- a/core/dragging/dragger.ts +++ b/core/dragging/dragger.ts @@ -12,6 +12,7 @@ import {Coordinate} from '../utils/coordinate.js'; import {WorkspaceSvg} from '../workspace_svg.js'; import {ComponentManager} from '../component_manager.js'; import {IDeleteArea} from '../interfaces/i_delete_area.js'; +import * as registry from '../registry.js'; export class Dragger implements IDragger { private startLoc: Coordinate; @@ -137,3 +138,5 @@ export class Dragger implements IDragger { return result; } } + +registry.register(registry.Type.DRAGGER, registry.DEFAULT, Dragger); diff --git a/core/gesture.ts b/core/gesture.ts index 2aed48d79e4..3ac499b3254 100644 --- a/core/gesture.ts +++ b/core/gesture.ts @@ -28,7 +28,6 @@ import type {IBlockDragger} from './interfaces/i_block_dragger.js'; import type {IBubble} from './interfaces/i_bubble.js'; import type {IFlyout} from './interfaces/i_flyout.js'; import * as internalConstants from './internal_constants.js'; -import * as registry from './registry.js'; import * as Tooltip from './tooltip.js'; import * as Touch from './touch.js'; import {Coordinate} from './utils/coordinate.js'; @@ -36,6 +35,8 @@ import {WorkspaceCommentSvg} from './workspace_comment_svg.js'; import {WorkspaceDragger} from './workspace_dragger.js'; import type {WorkspaceSvg} from './workspace_svg.js'; import type {IIcon} from './interfaces/i_icon.js'; +import {IDragger} from './interfaces/i_dragger.js'; +import * as registry from './registry.js'; /** * Note: In this file "start" refers to pointerdown @@ -116,8 +117,7 @@ export class Gesture { /** The object tracking a bubble drag, or null if none is in progress. */ private bubbleDragger: BubbleDragger | null = null; - /** The object tracking a block drag, or null if none is in progress. */ - private blockDragger: IBlockDragger | null = null; + private dragger: IDragger | null = null; /** * The object tracking a workspace or flyout workspace drag, or null if none @@ -212,9 +212,6 @@ export class Gesture { } this.boundEvents.length = 0; - if (this.blockDragger) { - this.blockDragger.dispose(); - } if (this.workspaceDragger) { this.workspaceDragger.dispose(); } @@ -230,7 +227,7 @@ export class Gesture { const changed = this.updateDragDelta(currentXY); // Exceeded the drag radius for the first time. if (changed) { - this.updateIsDragging(); + this.updateIsDragging(e); Touch.longStop(); } this.mostRecentEvent = e; @@ -334,17 +331,17 @@ export class Gesture { * * @returns True if a block is being dragged. */ - private updateIsDraggingBlock(): boolean { + private updateIsDraggingBlock(e: PointerEvent): boolean { if (!this.targetBlock) { return false; } if (this.flyout) { if (this.updateIsDraggingFromFlyout()) { - this.startDraggingBlock(); + this.startDraggingBlock(e); return true; } } else if (this.targetBlock.isMovable()) { - this.startDraggingBlock(); + this.startDraggingBlock(e); return true; } return false; @@ -384,7 +381,7 @@ export class Gesture { * the drag radius is exceeded. It should be called no more than once per * gesture. */ - private updateIsDragging() { + private updateIsDragging(e: PointerEvent) { // Sanity check. if (this.calledUpdateIsDragging) { throw Error('updateIsDragging_ should only be called once per gesture.'); @@ -397,7 +394,7 @@ export class Gesture { return; } // Then check if it was a block drag. - if (this.updateIsDraggingBlock()) { + if (this.updateIsDraggingBlock(e)) { return; } // Then check if it's a workspace drag. @@ -405,20 +402,18 @@ export class Gesture { } /** Create a block dragger and start dragging the selected block. */ - private startDraggingBlock() { - const BlockDraggerClass = registry.getClassFromOptions( - registry.Type.BLOCK_DRAGGER, + private startDraggingBlock(e: PointerEvent) { + this.dragging = true; + + const DraggerClass = registry.getClassFromOptions( + registry.Type.DRAGGER, this.creatorWorkspace.options, true, ); - this.dragging = true; - this.blockDragger = new BlockDraggerClass!( - this.targetBlock, - this.startWorkspace_, - ); - this.blockDragger!.startDrag(this.currentDragDeltaXY, this.healStack); - this.blockDragger!.drag(this.mostRecentEvent, this.currentDragDeltaXY); + this.dragger = new DraggerClass!(this.targetBlock!, this.startWorkspace_!); + this.dragger.onDragStart(e); + this.dragger.onDrag(e, this.currentDragDeltaXY); } /** Create a bubble dragger and start dragging the selected bubble. */ @@ -587,8 +582,8 @@ export class Gesture { this.updateFromEvent(e); if (this.workspaceDragger) { this.workspaceDragger.drag(this.currentDragDeltaXY); - } else if (this.blockDragger) { - this.blockDragger.drag(this.mostRecentEvent, this.currentDragDeltaXY); + } else if (this.dragger) { + this.dragger.onDrag(this.mostRecentEvent, this.currentDragDeltaXY); } else if (this.bubbleDragger) { this.bubbleDragger.dragBubble( this.mostRecentEvent, @@ -631,8 +626,8 @@ export class Gesture { // not matter, because the three types of dragging are exclusive. if (this.bubbleDragger) { this.bubbleDragger.endBubbleDrag(e, this.currentDragDeltaXY); - } else if (this.blockDragger) { - this.blockDragger.endDrag(e, this.currentDragDeltaXY); + } else if (this.dragger) { + this.dragger.onDragEnd(e, this.currentDragDeltaXY); } else if (this.workspaceDragger) { this.workspaceDragger.endDrag(this.currentDragDeltaXY); } else if (this.isBubbleClick()) { @@ -797,8 +792,8 @@ export class Gesture { this.mostRecentEvent, this.currentDragDeltaXY, ); - } else if (this.blockDragger) { - this.blockDragger.endDrag(this.mostRecentEvent, this.currentDragDeltaXY); + } else if (this.dragger) { + this.dragger.onDragEnd(this.mostRecentEvent, this.currentDragDeltaXY); } else if (this.workspaceDragger) { this.workspaceDragger.endDrag(this.currentDragDeltaXY); } @@ -1227,20 +1222,6 @@ export class Gesture { return this.gestureHasStarted; } - /** - * Get a list of the insertion markers that currently exist. Block drags have - * 0, 1, or 2 insertion markers. - * - * @returns A possibly empty list of insertion marker blocks. - * @internal - */ - getInsertionMarkers(): BlockSvg[] { - if (this.blockDragger) { - return this.blockDragger.getInsertionMarkers(); - } - return []; - } - /** * Gets the current dragger if an item is being dragged. Null if nothing is * being dragged. @@ -1249,7 +1230,9 @@ export class Gesture { * progress. */ getCurrentDragger(): WorkspaceDragger | BubbleDragger | IBlockDragger | null { - return this.blockDragger ?? this.workspaceDragger ?? this.bubbleDragger; + // TODO: Change this to return the `dragger`, when we get rid of the last + // other dragger. + return this.workspaceDragger ?? this.bubbleDragger; } /** diff --git a/core/registry.ts b/core/registry.ts index 3645a6fdf80..69b80050472 100644 --- a/core/registry.ts +++ b/core/registry.ts @@ -24,6 +24,7 @@ import type {ToolboxItem} from './toolbox/toolbox_item.js'; import type {IPaster} from './interfaces/i_paster.js'; import type {ICopyData, ICopyable} from './interfaces/i_copyable.js'; import type {IConnectionPreviewer} from './interfaces/i_connection_previewer.js'; +import type {IDragger} from './interfaces/i_dragger.js'; /** * A map of maps. With the keys being the type and name of the class we are @@ -97,6 +98,8 @@ export class Type<_T> { static BLOCK_DRAGGER = new Type('blockDragger'); + static DRAGGER = new Type('dragger'); + /** @internal */ static SERIALIZER = new Type('serializer'); diff --git a/core/workspace_svg.ts b/core/workspace_svg.ts index 2d49485dd95..c26aa61c536 100644 --- a/core/workspace_svg.ts +++ b/core/workspace_svg.ts @@ -1264,12 +1264,10 @@ export class WorkspaceSvg extends Workspace implements IASTNodeLocationSvg { blocks[i].queueRender(); } - if (this.currentGesture_) { - const imList = this.currentGesture_.getInsertionMarkers(); - for (let i = 0; i < imList.length; i++) { - imList[i].queueRender(); - } - } + this.getTopBlocks() + .flatMap((block) => block.getDescendants(false)) + .filter((block) => block.isInsertionMarker()) + .forEach((block) => block.queueRender()); renderManagement .finishQueuedRenders()