diff --git a/src/actions/mover.ts b/src/actions/mover.ts index d5d678df..8920bf6b 100644 --- a/src/actions/mover.ts +++ b/src/actions/mover.ts @@ -18,6 +18,7 @@ import { import type {BlockSvg, IDragger, IDragStrategy} from 'blockly'; import {Navigation} from '../navigation'; import {KeyboardDragStrategy} from '../keyboard_drag_strategy'; +import {Direction, getXYFromDirection} from '../drag_direction'; const KeyCodes = utils.KeyCodes; const createSerializedKey = ShortcutRegistry.registry.createSerializedKey.bind( @@ -87,28 +88,28 @@ export class Mover { { name: 'Move left, constrained', preconditionFn: (workspace) => this.isMoving(workspace), - callback: (workspace) => this.moveConstrained(workspace /* , ...*/), + callback: (workspace) => this.moveConstrained(workspace, Direction.Left), keyCodes: [KeyCodes.LEFT], allowCollision: true, }, { name: 'Move right unconstrained', preconditionFn: (workspace) => this.isMoving(workspace), - callback: (workspace) => this.moveConstrained(workspace /* , ... */), + callback: (workspace) => this.moveConstrained(workspace, Direction.Right), keyCodes: [KeyCodes.RIGHT], allowCollision: true, }, { name: 'Move up, constrained', preconditionFn: (workspace) => this.isMoving(workspace), - callback: (workspace) => this.moveConstrained(workspace /* , ... */), + callback: (workspace) => this.moveConstrained(workspace, Direction.Up), keyCodes: [KeyCodes.UP], allowCollision: true, }, { name: 'Move down constrained', preconditionFn: (workspace) => this.isMoving(workspace), - callback: (workspace) => this.moveConstrained(workspace /* , ... */), + callback: (workspace) => this.moveConstrained(workspace, Direction.Down), keyCodes: [KeyCodes.DOWN], allowCollision: true, }, @@ -117,7 +118,8 @@ export class Mover { { name: 'Move left, unconstrained', preconditionFn: (workspace) => this.isMoving(workspace), - callback: (workspace) => this.moveUnconstrained(workspace, -1, 0), + callback: (workspace) => + this.moveUnconstrained(workspace, Direction.Left), keyCodes: [ createSerializedKey(KeyCodes.LEFT, [KeyCodes.ALT]), createSerializedKey(KeyCodes.LEFT, [KeyCodes.CTRL]), @@ -126,7 +128,8 @@ export class Mover { { name: 'Move right, unconstrained', preconditionFn: (workspace) => this.isMoving(workspace), - callback: (workspace) => this.moveUnconstrained(workspace, 1, 0), + callback: (workspace) => + this.moveUnconstrained(workspace, Direction.Right), keyCodes: [ createSerializedKey(KeyCodes.RIGHT, [KeyCodes.ALT]), createSerializedKey(KeyCodes.RIGHT, [KeyCodes.CTRL]), @@ -135,7 +138,7 @@ export class Mover { { name: 'Move up unconstrained', preconditionFn: (workspace) => this.isMoving(workspace), - callback: (workspace) => this.moveUnconstrained(workspace, 0, -1), + callback: (workspace) => this.moveUnconstrained(workspace, Direction.Up), keyCodes: [ createSerializedKey(KeyCodes.UP, [KeyCodes.ALT]), createSerializedKey(KeyCodes.UP, [KeyCodes.CTRL]), @@ -144,7 +147,8 @@ export class Mover { { name: 'Move down, unconstrained', preconditionFn: (workspace) => this.isMoving(workspace), - callback: (workspace) => this.moveUnconstrained(workspace, 0, 1), + callback: (workspace) => + this.moveUnconstrained(workspace, Direction.Down), keyCodes: [ createSerializedKey(KeyCodes.DOWN, [KeyCodes.ALT]), createSerializedKey(KeyCodes.DOWN, [KeyCodes.CTRL]), @@ -320,17 +324,18 @@ export class Mover { * constrained to valid attachment points (if any). * * @param workspace The workspace to move on. + * @param direction The direction to move the dragged item. * @returns True iff this action applies and has been performed. */ - moveConstrained( - workspace: WorkspaceSvg, - /* ... */ - ) { - // Not yet implemented. Absorb keystroke to avoid moving cursor. - alert(`Constrained movement not implemented. + moveConstrained(workspace: WorkspaceSvg, direction: Direction) { + if (!workspace) return false; + const info = this.moves.get(workspace); + if (!info) throw new Error('no move info for workspace'); -Use ctrl+arrow or alt+arrow (option+arrow on macOS) for unconstrained move. -Use enter to complete the move, or escape to abort.`); + info.dragger.onDrag( + info.fakePointerEvent('pointermove', direction), + info.totalDelta, + ); return true; } @@ -339,23 +344,17 @@ Use enter to complete the move, or escape to abort.`); * without constraint. * * @param workspace The workspace to move on. - * @param xDirection -1 to move left. 1 to move right. - * @param yDirection -1 to move up. 1 to move down. + * @param direction The direction to move the dragged item. * @returns True iff this action applies and has been performed. */ - moveUnconstrained( - workspace: WorkspaceSvg, - xDirection: number, - yDirection: number, - ): boolean { + moveUnconstrained(workspace: WorkspaceSvg, direction: Direction): boolean { if (!workspace) return false; const info = this.moves.get(workspace); if (!info) throw new Error('no move info for workspace'); - info.totalDelta.x += - xDirection * UNCONSTRAINED_MOVE_DISTANCE * workspace.scale; - info.totalDelta.y += - yDirection * UNCONSTRAINED_MOVE_DISTANCE * workspace.scale; + const {x, y} = getXYFromDirection(direction); + info.totalDelta.x += x * UNCONSTRAINED_MOVE_DISTANCE * workspace.scale; + info.totalDelta.y += y * UNCONSTRAINED_MOVE_DISTANCE * workspace.scale; info.dragger.onDrag(info.fakePointerEvent('pointermove'), info.totalDelta); return true; @@ -446,10 +445,11 @@ export class MoveInfo { * Create a fake pointer event for dragging. * * @param type Which type of pointer event to create. + * @param direction The direction if this movement is a constrained drag. * @returns A synthetic PointerEvent that can be consumed by Blockly's * dragging code. */ - fakePointerEvent(type: string): PointerEvent { + fakePointerEvent(type: string, direction?: Direction): PointerEvent { const workspace = this.block.workspace; if (!(workspace instanceof WorkspaceSvg)) throw new TypeError(); @@ -460,9 +460,12 @@ export class MoveInfo { this.startLocation.y + this.totalDelta.y, ), ); + const tilts = getXYFromDirection(direction); return new PointerEvent(type, { clientX: blockCoords.x, clientY: blockCoords.y, + tiltX: tilts.x, + tiltY: tilts.y, }); } } diff --git a/src/keyboard_drag_strategy.ts b/src/keyboard_drag_strategy.ts index 04a101c6..f9e5fd93 100644 --- a/src/keyboard_drag_strategy.ts +++ b/src/keyboard_drag_strategy.ts @@ -4,9 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {dragging} from 'blockly'; +import {dragging, utils} from 'blockly'; +import {Direction, getDirectionFromXY} from './drag_direction'; export class KeyboardDragStrategy extends dragging.BlockDragStrategy { + private currentDragDirection: Direction | null = null; + override startDrag(e?: PointerEvent) { super.startDrag(e); // Set position of the dragging block, so that it doesn't pop @@ -14,4 +17,20 @@ export class KeyboardDragStrategy extends dragging.BlockDragStrategy { // @ts-expect-error block and startLoc are private. this.block.moveDuringDrag(this.startLoc); } + + override drag(newLoc: utils.Coordinate, e?: PointerEvent): void { + if (!e) return; + this.currentDragDirection = getDirectionFromXY({x: e.tiltX, y: e.tiltY}); + super.drag(newLoc); + } + + /** + * Get whether the most recent drag event represents a constrained + * keyboard drag. + * + * @returns true if the current movement is constrained, otherwise false. + */ + private isConstrainedMovement(): boolean { + return !!this.currentDragDirection; + } }