mirror of
https://github.com/google/blockly.git
synced 2026-01-08 17:40:09 +01:00
fix: draggable interface and concrete dragger (#7967)
* fix: interface to match design * fix: dragger implementation * fix: rename moveToStart to revertDrag * fixup
This commit is contained in:
@@ -4,65 +4,132 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {IDragTarget} from '../interfaces/i_drag_target.js';
|
||||
import {IDeletable, isDeletable} from '../interfaces/i_deletable.js';
|
||||
import {IDragger} from '../interfaces/i_dragger.js';
|
||||
import {IDraggable} from '../interfaces/i_draggable.js';
|
||||
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';
|
||||
|
||||
export class Dragger implements IDragger {
|
||||
/** Starting location of the draggable, in workspace coordinates. */
|
||||
private startLoc: Coordinate;
|
||||
|
||||
private dragTarget: IDragTarget | null = null;
|
||||
|
||||
constructor(
|
||||
private draggable: IDraggable,
|
||||
private workspace: WorkspaceSvg,
|
||||
) {
|
||||
this.startLoc = draggable.getLocation();
|
||||
this.startLoc = draggable.getRelativeToSurfaceXY();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles any drag startup.
|
||||
*
|
||||
* @param e PointerEvent that started the drag.
|
||||
*/
|
||||
/** Handles any drag startup. */
|
||||
onDragStart(e: PointerEvent) {
|
||||
this.draggable.startDrag(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles dragging, including calculating where the element should
|
||||
* actually be moved to.
|
||||
* Handles calculating where the element should actually be moved to.
|
||||
*
|
||||
* @param e PointerEvent that continued the drag.
|
||||
* @param totalDelta The total distance, in pixels, that the mouse
|
||||
* has moved since the start of the drag.
|
||||
* @param totalDelta The total amount in pixel coordinates the mouse has moved
|
||||
* since the start of the drag.
|
||||
*/
|
||||
onDrag(e: PointerEvent, totalDelta: Coordinate) {
|
||||
this.draggable.drag(this.newLoc(totalDelta), e);
|
||||
this.moveDraggable(e, totalDelta);
|
||||
|
||||
// Must check `wouldDelete` before calling other hooks on drag targets
|
||||
// since we have documented that we would do so.
|
||||
if (isDeletable(this.draggable)) {
|
||||
(this.draggable as AnyDuringMigration).setDeleteStyle(
|
||||
this.wouldDeleteDraggable(e, this.draggable),
|
||||
);
|
||||
}
|
||||
this.updateDragTarget(e);
|
||||
}
|
||||
|
||||
/** Updates the drag target under the pointer (if there is one). */
|
||||
protected updateDragTarget(e: PointerEvent) {
|
||||
const newDragTarget = this.workspace.getDragTarget(e);
|
||||
if (this.dragTarget !== newDragTarget) {
|
||||
this.dragTarget?.onDragExit(this.draggable as AnyDuringMigration);
|
||||
newDragTarget?.onDragEnter(this.draggable as AnyDuringMigration);
|
||||
}
|
||||
newDragTarget?.onDragOver(this.draggable as AnyDuringMigration);
|
||||
this.dragTarget = newDragTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles any drag cleanup.
|
||||
*
|
||||
* @param e PointerEvent that finished the drag.
|
||||
* @param totalDelta The total distance, in pixels, that the mouse
|
||||
* has moved since the start of the drag.
|
||||
* Calculates the correct workspace coordinate for the movable and tells
|
||||
* the draggable to go to that location.
|
||||
*/
|
||||
onDragEnd(e: PointerEvent, totalDelta: Coordinate) {
|
||||
this.draggable.endDrag(this.newLoc(totalDelta), e);
|
||||
private moveDraggable(e: PointerEvent, totalDelta: Coordinate) {
|
||||
const delta = this.pixelsToWorkspaceUnits(totalDelta);
|
||||
const newLoc = Coordinate.sum(this.startLoc, delta);
|
||||
this.draggable.drag(newLoc, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates where the IDraggable should actually be moved to.
|
||||
*
|
||||
* @param totalDelta The total distance, in pixels, that the mouse
|
||||
* has moved since the start of the drag.
|
||||
* @returns The new location, in workspace coordinates.
|
||||
* Returns true if we would delete the draggable if it was dropped
|
||||
* at the current location.
|
||||
*/
|
||||
protected newLoc(totalDelta: Coordinate): Coordinate {
|
||||
protected wouldDeleteDraggable(
|
||||
e: PointerEvent,
|
||||
draggable: IDraggable & IDeletable,
|
||||
) {
|
||||
const dragTarget = this.workspace.getDragTarget(e);
|
||||
if (!dragTarget) return false;
|
||||
|
||||
const componentManager = this.workspace.getComponentManager();
|
||||
const isDeleteArea = componentManager.hasCapability(
|
||||
dragTarget.id,
|
||||
ComponentManager.Capability.DELETE_AREA,
|
||||
);
|
||||
if (!isDeleteArea) return false;
|
||||
|
||||
return (dragTarget as IDeleteArea).wouldDelete(
|
||||
draggable,
|
||||
false,
|
||||
// !!this.getConnectionCandidate(draggable, delta),
|
||||
);
|
||||
}
|
||||
|
||||
/** Handles any drag cleanup. */
|
||||
onDragEnd(e: PointerEvent) {
|
||||
const dragTarget = this.workspace.getDragTarget(e);
|
||||
if (dragTarget) {
|
||||
this.dragTarget?.onDrop(this.draggable as AnyDuringMigration);
|
||||
}
|
||||
|
||||
if (this.shouldReturnToStart(e, this.draggable)) {
|
||||
this.draggable.revertDrag();
|
||||
}
|
||||
|
||||
this.draggable.endDrag(e);
|
||||
|
||||
if (
|
||||
isDeletable(this.draggable) &&
|
||||
this.wouldDeleteDraggable(e, this.draggable)
|
||||
) {
|
||||
(this.draggable as AnyDuringMigration).dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we should return the draggable to its original location
|
||||
* at the end of the drag.
|
||||
*/
|
||||
protected shouldReturnToStart(e: PointerEvent, draggable: IDraggable) {
|
||||
const dragTarget = this.workspace.getDragTarget(e);
|
||||
if (!dragTarget) return false;
|
||||
return dragTarget.shouldPreventMove(draggable as AnyDuringMigration);
|
||||
}
|
||||
|
||||
protected pixelsToWorkspaceUnits(pixelCoord: Coordinate): Coordinate {
|
||||
const result = new Coordinate(
|
||||
totalDelta.x / this.workspace.scale,
|
||||
totalDelta.y / this.workspace.scale,
|
||||
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
|
||||
@@ -71,7 +138,6 @@ export class Dragger implements IDragger {
|
||||
const mainScale = this.workspace.options.parentWorkspace!.scale;
|
||||
result.scale(1 / mainScale);
|
||||
}
|
||||
result.translate(this.startLoc.x, this.startLoc.y);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,3 +17,8 @@ export interface IDeletable {
|
||||
*/
|
||||
isDeletable(): boolean;
|
||||
}
|
||||
|
||||
/** Returns whether the given object is an IDeletable. */
|
||||
export function isDeletable(obj: any): obj is IDeletable {
|
||||
return obj['isDeletable'] !== undefined;
|
||||
}
|
||||
|
||||
@@ -5,33 +5,29 @@
|
||||
*/
|
||||
|
||||
import {Coordinate} from '../utils/coordinate';
|
||||
import {IDragTarget} from './i_drag_target';
|
||||
|
||||
/**
|
||||
* Represents an object that can be dragged.
|
||||
*/
|
||||
export interface IDraggable extends IDragStrategy {
|
||||
/** Returns true iff the element is currently movable. */
|
||||
isMovable(): boolean;
|
||||
|
||||
/**
|
||||
* Returns the current location of the draggable in workspace
|
||||
* coordinates.
|
||||
*
|
||||
* @returns Coordinate of current location on workspace.
|
||||
*/
|
||||
getLocation(): Coordinate;
|
||||
getRelativeToSurfaceXY(): Coordinate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an object that can be dragged.
|
||||
*/
|
||||
export interface IDragStrategy {
|
||||
/** Returns true iff the element is currently movable. */
|
||||
isMovable(): boolean;
|
||||
|
||||
/**
|
||||
* Handles any drag startup (e.g moving elements to the front of the
|
||||
* workspace).
|
||||
*
|
||||
* @param e PointerEvent that started the drag; could be used to
|
||||
* @param e PointerEvent that started the drag; can be used to
|
||||
* check modifier keys, etc. May be missing when dragging is
|
||||
* triggered programatically rather than by user.
|
||||
*/
|
||||
@@ -43,15 +39,10 @@ export interface IDragStrategy {
|
||||
*
|
||||
* @param newLoc Workspace coordinate to which the draggable has
|
||||
* been dragged.
|
||||
* @param e PointerEvent that continued the drag. Should be used to
|
||||
* look up any IDragTarget the pointer is over; could also be
|
||||
* @param e PointerEvent that continued the drag. Can be
|
||||
* used to check modifier keys, etc.
|
||||
* @param target The drag target the pointer is over, if any. Could
|
||||
* be supplied as an alternative to providing a PointerEvent for
|
||||
* programatic drags.
|
||||
*/
|
||||
drag(newLoc: Coordinate, e?: PointerEvent): void;
|
||||
drag(newLoc: Coordinate, target: IDragTarget): void;
|
||||
|
||||
/**
|
||||
* Handles any drag cleanup, including e.g. connecting or deleting
|
||||
@@ -59,13 +50,11 @@ export interface IDragStrategy {
|
||||
*
|
||||
* @param newLoc Workspace coordinate at which the drag finished.
|
||||
* been dragged.
|
||||
* @param e PointerEvent that finished the drag. Should be used to
|
||||
* look up any IDragTarget the pointer is over; could also be
|
||||
* @param e PointerEvent that finished the drag. Can be
|
||||
* used to check modifier keys, etc.
|
||||
* @param target The drag target the pointer is over, if any. Could
|
||||
* be supplied as an alternative to providing a PointerEvent for
|
||||
* programatic drags.
|
||||
*/
|
||||
endDrag(newLoc: Coordinate, e?: PointerEvent): void;
|
||||
endDrag(newLoc: Coordinate, target: IDragTarget): void;
|
||||
endDrag(e?: PointerEvent): void;
|
||||
|
||||
/** Moves the draggable back to where it was at the start of the drag. */
|
||||
revertDrag(): void;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user