From 5fe951552358ae1b62ffe9b1eab8ae40be21662f Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 3 Apr 2025 14:37:19 -0700 Subject: [PATCH 1/2] fix: show block at correct location during constrained drag --- src/actions/mover.ts | 19 ++++++++++++++++++- src/keyboard_drag_strategy.ts | 16 +++++++++++----- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/actions/mover.ts b/src/actions/mover.ts index 8920bf6b..faeef164 100644 --- a/src/actions/mover.ts +++ b/src/actions/mover.ts @@ -336,6 +336,8 @@ export class Mover { info.fakePointerEvent('pointermove', direction), info.totalDelta, ); + + info.updateTotalDelta(); return true; } @@ -426,7 +428,7 @@ export class Mover { * Workspace. */ export class MoveInfo { - /** Total distance moved, in screen pixels */ + /** Total distance moved, in workspace units. */ totalDelta = new utils.Coordinate(0, 0); readonly parentNext: Connection | null; readonly parentInput: Connection | null; @@ -468,4 +470,19 @@ export class MoveInfo { tiltY: tilts.y, }); } + + /** + * The keyboard drag may have moved the block to an appropriate location + * for a preview. Update the saved delta to reflect the block's new + * location, so that it does not jump during the next unconstrained move. + */ + updateTotalDelta() { + const workspace = this.block.workspace; + if (!(workspace instanceof WorkspaceSvg)) throw new TypeError(); + + this.totalDelta = new utils.Coordinate( + this.block.relativeCoords.x - this.startLocation.x, + this.block.relativeCoords.y - this.startLocation.y, + ); + } } diff --git a/src/keyboard_drag_strategy.ts b/src/keyboard_drag_strategy.ts index 91889670..a20965d2 100644 --- a/src/keyboard_drag_strategy.ts +++ b/src/keyboard_drag_strategy.ts @@ -44,13 +44,19 @@ export class KeyboardDragStrategy extends dragging.BlockDragStrategy { super.drag(newLoc); // Handle the case when an unconstrained drag found a connection candidate. - // The next constrained move will resume the search from the current candidate - // location. // @ts-expect-error connectionCandidate is private. if (this.connectionCandidate) { - this.searchNode = ASTNode.createConnectionNode( - // @ts-expect-error connectionCandidate is private. - (this.connectionCandidate as ConnectionCandidate).neighbour, + // @ts-expect-error connectionCandidate is private. + const neighbour = (this.connectionCandidate as ConnectionCandidate) + .neighbour; + // The next constrained move will resume the search from the current + // candidate location. + this.searchNode = ASTNode.createConnectionNode(neighbour); + // The moving block will be positioned slightly down and to the + // right of the connection it found. + // @ts-expect-error block and startLoc are private. + this.block.moveDuringDrag( + new utils.Coordinate(neighbour.x + 10, neighbour.y + 10), ); } } From ceb619b8c26f3ec8d7f2fbd94d4c43795177293e Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 3 Apr 2025 14:37:37 -0700 Subject: [PATCH 2/2] chore: add a larger stack of blocks for testing --- test/index.html | 1 + test/loadTestBlocks.js | 166 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+) diff --git a/test/index.html b/test/index.html index 3661cdbd..19399fa5 100644 --- a/test/index.html +++ b/test/index.html @@ -136,6 +136,7 @@ +
diff --git a/test/loadTestBlocks.js b/test/loadTestBlocks.js index 7c74135e..4bb78de1 100644 --- a/test/loadTestBlocks.js +++ b/test/loadTestBlocks.js @@ -249,6 +249,171 @@ const simpleCircle = { }, }; +const moreBlocks = { + 'blocks': { + 'languageVersion': 0, + 'blocks': [ + { + 'type': 'p5_setup', + 'id': '5.{;T}3Qv}Awi:1M$:ut', + 'x': 0, + 'y': 75, + 'deletable': false, + 'inputs': { + 'STATEMENTS': { + 'block': { + 'type': 'p5_canvas', + 'id': 'spya_H-5F=K8+DhedX$y', + 'deletable': false, + 'movable': false, + 'fields': { + 'WIDTH': 400, + 'HEIGHT': 400, + }, + 'next': { + 'block': { + 'type': 'p5_background_color', + 'id': 'i/Hvi~^DYffkN/WpT_Ck', + 'inputs': { + 'COLOR': { + 'shadow': { + 'type': 'colour_picker', + 'id': 'B:zpi7kg+.GF_Dutd9GL', + 'fields': { + 'COLOUR': '#9999ff', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + 'type': 'p5_draw', + 'id': '3iI4f%2#Gmk}=OjI7(8h', + 'x': 0, + 'y': 332, + 'deletable': false, + 'inputs': { + 'STATEMENTS': { + 'block': { + 'type': 'simple_circle', + 'id': 'draw_circle_1', + 'inline': true, + 'inputs': { + 'COLOR': { + 'shadow': { + 'type': 'colour_picker', + 'id': 'gq(POne}j:hVw%C3t{vx', + 'fields': { + 'COLOUR': '#ffff00', + }, + }, + }, + }, + 'next': { + 'block': { + 'type': 'text_print', + 'id': 'J`*)bq?#`_Vq^X(DQF2t', + 'inputs': { + 'TEXT': { + 'shadow': { + 'type': 'text', + 'id': '6fW_sIt1t|63j}nPE1ge', + 'fields': { + 'TEXT': 'abc', + }, + }, + }, + }, + 'next': { + 'block': { + 'type': 'controls_if', + 'id': ',rP|uDy,esfrOeQrk64u', + 'inputs': { + 'IF0': { + 'block': { + 'type': 'logic_negate', + 'id': '8iH/,SwwTfk7iR;~m^s[', + }, + }, + 'DO0': { + 'block': { + 'type': 'text_print', + 'id': 'uSxT~QT8p%D2o)b~)Dki', + 'inputs': { + 'TEXT': { + 'shadow': { + 'type': 'text', + 'id': 'j|)#Di2,(L^TK)iLI3LC', + 'fields': { + 'TEXT': 'abc', + }, + }, + 'block': { + 'type': 'math_arithmetic', + 'id': 'mRTJ4D+(mjBnUy8c4KaT', + 'fields': { + 'OP': 'ADD', + }, + 'inputs': { + 'A': { + 'shadow': { + 'type': 'math_number', + 'id': 'hxGO;t4bA9$.~|E6Gy~H', + 'fields': { + 'NUM': 1, + }, + }, + }, + 'B': { + 'shadow': { + 'type': 'math_number', + 'id': 'P,$Lqn5{mFE?R)#~v|/V', + 'fields': { + 'NUM': 1, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + 'next': { + 'block': { + 'type': 'text_print', + 'id': '-bTQ2YVSuBS/SYn[C^LX', + 'inputs': { + 'TEXT': { + 'shadow': { + 'type': 'text', + 'id': 'cy+0[WR6]O(x%Q;~c*0f', + 'fields': { + 'TEXT': 'abc', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + ], + }, +}; + /** * Loads saved state from local storage into the given workspace. * @param {Blockly.Workspace} workspace Blockly workspace to load into. @@ -259,6 +424,7 @@ export const load = function (workspace, scenarioString) { 'blank': blankCanvas, 'sun': sunnyDay, 'simpleCircle': simpleCircle, + 'moreBlocks': moreBlocks, }; const data = JSON.stringify(scenarioMap[scenarioString]);