Skip to content

Commit bbaae11

Browse files
fix: show block at correct location during constrained drag (#386)
* fix: show block at correct location during constrained drag * chore: add a larger stack of blocks for testing
1 parent 6465aa2 commit bbaae11

File tree

4 files changed

+196
-6
lines changed

4 files changed

+196
-6
lines changed

src/actions/mover.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,8 @@ export class Mover {
336336
info.fakePointerEvent('pointermove', direction),
337337
info.totalDelta,
338338
);
339+
340+
info.updateTotalDelta();
339341
return true;
340342
}
341343

@@ -426,7 +428,7 @@ export class Mover {
426428
* Workspace.
427429
*/
428430
export class MoveInfo {
429-
/** Total distance moved, in screen pixels */
431+
/** Total distance moved, in workspace units. */
430432
totalDelta = new utils.Coordinate(0, 0);
431433
readonly parentNext: Connection | null;
432434
readonly parentInput: Connection | null;
@@ -468,4 +470,19 @@ export class MoveInfo {
468470
tiltY: tilts.y,
469471
});
470472
}
473+
474+
/**
475+
* The keyboard drag may have moved the block to an appropriate location
476+
* for a preview. Update the saved delta to reflect the block's new
477+
* location, so that it does not jump during the next unconstrained move.
478+
*/
479+
updateTotalDelta() {
480+
const workspace = this.block.workspace;
481+
if (!(workspace instanceof WorkspaceSvg)) throw new TypeError();
482+
483+
this.totalDelta = new utils.Coordinate(
484+
this.block.relativeCoords.x - this.startLocation.x,
485+
this.block.relativeCoords.y - this.startLocation.y,
486+
);
487+
}
471488
}

src/keyboard_drag_strategy.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,19 @@ export class KeyboardDragStrategy extends dragging.BlockDragStrategy {
4444
super.drag(newLoc);
4545

4646
// Handle the case when an unconstrained drag found a connection candidate.
47-
// The next constrained move will resume the search from the current candidate
48-
// location.
4947
// @ts-expect-error connectionCandidate is private.
5048
if (this.connectionCandidate) {
51-
this.searchNode = ASTNode.createConnectionNode(
52-
// @ts-expect-error connectionCandidate is private.
53-
(this.connectionCandidate as ConnectionCandidate).neighbour,
49+
// @ts-expect-error connectionCandidate is private.
50+
const neighbour = (this.connectionCandidate as ConnectionCandidate)
51+
.neighbour;
52+
// The next constrained move will resume the search from the current
53+
// candidate location.
54+
this.searchNode = ASTNode.createConnectionNode(neighbour);
55+
// The moving block will be positioned slightly down and to the
56+
// right of the connection it found.
57+
// @ts-expect-error block and startLoc are private.
58+
this.block.moveDuringDrag(
59+
new utils.Coordinate(neighbour.x + 10, neighbour.y + 10),
5460
);
5561
}
5662
}

test/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@
136136
<option value="simpleCircle">simple circle</option>
137137
<option value="sun">sun</option>
138138
<option value="blank">blank canvas</option>
139+
<option value="moreBlocks">more blocks</option>
139140
</select>
140141
</div>
141142
<div>

test/loadTestBlocks.js

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,171 @@ const simpleCircle = {
249249
},
250250
};
251251

252+
const moreBlocks = {
253+
'blocks': {
254+
'languageVersion': 0,
255+
'blocks': [
256+
{
257+
'type': 'p5_setup',
258+
'id': '5.{;T}3Qv}Awi:1M$:ut',
259+
'x': 0,
260+
'y': 75,
261+
'deletable': false,
262+
'inputs': {
263+
'STATEMENTS': {
264+
'block': {
265+
'type': 'p5_canvas',
266+
'id': 'spya_H-5F=K8+DhedX$y',
267+
'deletable': false,
268+
'movable': false,
269+
'fields': {
270+
'WIDTH': 400,
271+
'HEIGHT': 400,
272+
},
273+
'next': {
274+
'block': {
275+
'type': 'p5_background_color',
276+
'id': 'i/Hvi~^DYffkN/WpT_Ck',
277+
'inputs': {
278+
'COLOR': {
279+
'shadow': {
280+
'type': 'colour_picker',
281+
'id': 'B:zpi7kg+.GF_Dutd9GL',
282+
'fields': {
283+
'COLOUR': '#9999ff',
284+
},
285+
},
286+
},
287+
},
288+
},
289+
},
290+
},
291+
},
292+
},
293+
},
294+
{
295+
'type': 'p5_draw',
296+
'id': '3iI4f%2#Gmk}=OjI7(8h',
297+
'x': 0,
298+
'y': 332,
299+
'deletable': false,
300+
'inputs': {
301+
'STATEMENTS': {
302+
'block': {
303+
'type': 'simple_circle',
304+
'id': 'draw_circle_1',
305+
'inline': true,
306+
'inputs': {
307+
'COLOR': {
308+
'shadow': {
309+
'type': 'colour_picker',
310+
'id': 'gq(POne}j:hVw%C3t{vx',
311+
'fields': {
312+
'COLOUR': '#ffff00',
313+
},
314+
},
315+
},
316+
},
317+
'next': {
318+
'block': {
319+
'type': 'text_print',
320+
'id': 'J`*)bq?#`_Vq^X(DQF2t',
321+
'inputs': {
322+
'TEXT': {
323+
'shadow': {
324+
'type': 'text',
325+
'id': '6fW_sIt1t|63j}nPE1ge',
326+
'fields': {
327+
'TEXT': 'abc',
328+
},
329+
},
330+
},
331+
},
332+
'next': {
333+
'block': {
334+
'type': 'controls_if',
335+
'id': ',rP|uDy,esfrOeQrk64u',
336+
'inputs': {
337+
'IF0': {
338+
'block': {
339+
'type': 'logic_negate',
340+
'id': '8iH/,SwwTfk7iR;~m^s[',
341+
},
342+
},
343+
'DO0': {
344+
'block': {
345+
'type': 'text_print',
346+
'id': 'uSxT~QT8p%D2o)b~)Dki',
347+
'inputs': {
348+
'TEXT': {
349+
'shadow': {
350+
'type': 'text',
351+
'id': 'j|)#Di2,(L^TK)iLI3LC',
352+
'fields': {
353+
'TEXT': 'abc',
354+
},
355+
},
356+
'block': {
357+
'type': 'math_arithmetic',
358+
'id': 'mRTJ4D+(mjBnUy8c4KaT',
359+
'fields': {
360+
'OP': 'ADD',
361+
},
362+
'inputs': {
363+
'A': {
364+
'shadow': {
365+
'type': 'math_number',
366+
'id': 'hxGO;t4bA9$.~|E6Gy~H',
367+
'fields': {
368+
'NUM': 1,
369+
},
370+
},
371+
},
372+
'B': {
373+
'shadow': {
374+
'type': 'math_number',
375+
'id': 'P,$Lqn5{mFE?R)#~v|/V',
376+
'fields': {
377+
'NUM': 1,
378+
},
379+
},
380+
},
381+
},
382+
},
383+
},
384+
},
385+
},
386+
},
387+
},
388+
'next': {
389+
'block': {
390+
'type': 'text_print',
391+
'id': '-bTQ2YVSuBS/SYn[C^LX',
392+
'inputs': {
393+
'TEXT': {
394+
'shadow': {
395+
'type': 'text',
396+
'id': 'cy+0[WR6]O(x%Q;~c*0f',
397+
'fields': {
398+
'TEXT': 'abc',
399+
},
400+
},
401+
},
402+
},
403+
},
404+
},
405+
},
406+
},
407+
},
408+
},
409+
},
410+
},
411+
},
412+
},
413+
],
414+
},
415+
};
416+
252417
/**
253418
* Loads saved state from local storage into the given workspace.
254419
* @param {Blockly.Workspace} workspace Blockly workspace to load into.
@@ -259,6 +424,7 @@ export const load = function (workspace, scenarioString) {
259424
'blank': blankCanvas,
260425
'sun': sunnyDay,
261426
'simpleCircle': simpleCircle,
427+
'moreBlocks': moreBlocks,
262428
};
263429

264430
const data = JSON.stringify(scenarioMap[scenarioString]);

0 commit comments

Comments
 (0)