From eddf1039ce0a201798a5484490253e7501223df2 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 3 Apr 2025 10:41:20 -0700 Subject: [PATCH 1/3] feat: add drag direction to constrained events --- src/actions/mover.ts | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/actions/mover.ts b/src/actions/mover.ts index d5d678df..50621bc3 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, }, @@ -320,17 +321,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; } @@ -446,10 +448,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 +463,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, }); } } From c122d53dd8855b7f5385d439128bab2b174a1d00 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 3 Apr 2025 10:44:39 -0700 Subject: [PATCH 2/3] feat: use direction for unconstrained moves --- src/actions/mover.ts | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/actions/mover.ts b/src/actions/mover.ts index 50621bc3..8920bf6b 100644 --- a/src/actions/mover.ts +++ b/src/actions/mover.ts @@ -118,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]), @@ -127,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]), @@ -136,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]), @@ -145,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]), @@ -341,23 +344,17 @@ export class Mover { * 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; From ffdcd5c2fabcc40e6ddb3ee3a56b5d86ca360835 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 3 Apr 2025 11:12:41 -0700 Subject: [PATCH 3/3] feat: unpack direction info in keyboard drag strategy --- src/keyboard_drag_strategy.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) 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; + } }