mirror of
https://github.com/google/blockly.git
synced 2026-01-08 17:40:09 +01:00
fix: bumping copied objects (#7349)
* fix: add logic for bumping pasted blocks * chore: add tests for bumping pasted blocks to the correct location * fix: add logic for bumping pasted comments * chore: add tests for bumping pasted comments
This commit is contained in:
@@ -11,6 +11,8 @@ import {IPaster} from '../interfaces/i_paster.js';
|
||||
import {State, append} from '../serialization/blocks.js';
|
||||
import {Coordinate} from '../utils/coordinate.js';
|
||||
import {WorkspaceSvg} from '../workspace_svg.js';
|
||||
import * as eventUtils from '../events/utils.js';
|
||||
import {config} from '../config.js';
|
||||
|
||||
export class BlockPaster implements IPaster<BlockCopyData, BlockSvg> {
|
||||
static TYPE = 'block';
|
||||
@@ -26,12 +28,90 @@ export class BlockPaster implements IPaster<BlockCopyData, BlockSvg> {
|
||||
copyData.blockState['x'] = coordinate.x;
|
||||
copyData.blockState['y'] = coordinate.y;
|
||||
}
|
||||
return append(copyData.blockState, workspace, {
|
||||
recordUndo: true,
|
||||
}) as BlockSvg;
|
||||
|
||||
eventUtils.disable();
|
||||
let block;
|
||||
try {
|
||||
block = append(copyData.blockState, workspace) as BlockSvg;
|
||||
moveBlockToNotConflict(block);
|
||||
} finally {
|
||||
eventUtils.enable();
|
||||
}
|
||||
|
||||
if (!block) return block;
|
||||
|
||||
if (eventUtils.isEnabled() && !block.isShadow()) {
|
||||
eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CREATE))(block));
|
||||
}
|
||||
block.select();
|
||||
return block;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the given block to a location where it does not: (1) overlap exactly
|
||||
* with any other blocks, or (2) look like it is connected to any other blocks.
|
||||
*
|
||||
* Exported for testing.
|
||||
*
|
||||
* @param block The block to move to an unambiguous location.
|
||||
* @internal
|
||||
*/
|
||||
export function moveBlockToNotConflict(block: BlockSvg) {
|
||||
const workspace = block.workspace;
|
||||
const snapRadius = config.snapRadius;
|
||||
const coord = block.getRelativeToSurfaceXY();
|
||||
const offset = new Coordinate(0, 0);
|
||||
// getRelativeToSurfaceXY is really expensive, so we want to cache this.
|
||||
const otherCoords = workspace
|
||||
.getAllBlocks(false)
|
||||
.filter((otherBlock) => otherBlock.id != block.id)
|
||||
.map((b) => b.getRelativeToSurfaceXY());
|
||||
|
||||
while (
|
||||
blockOverlapsOtherExactly(Coordinate.sum(coord, offset), otherCoords) ||
|
||||
blockIsInSnapRadius(block, offset, snapRadius)
|
||||
) {
|
||||
if (workspace.RTL) {
|
||||
offset.translate(-snapRadius, snapRadius * 2);
|
||||
} else {
|
||||
offset.translate(snapRadius, snapRadius * 2);
|
||||
}
|
||||
}
|
||||
|
||||
block!.moveTo(Coordinate.sum(coord, offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the given block coordinates are less than a delta of 1 from
|
||||
* any of the other coordinates.
|
||||
*/
|
||||
function blockOverlapsOtherExactly(
|
||||
coord: Coordinate,
|
||||
otherCoords: Coordinate[],
|
||||
): boolean {
|
||||
return otherCoords.some(
|
||||
(otherCoord) =>
|
||||
Math.abs(otherCoord.x - coord.x) <= 1 &&
|
||||
Math.abs(otherCoord.y - coord.y) <= 1,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the given block (when offset by the given amount) is close
|
||||
* enough to any other connections (within the snap radius) that it looks
|
||||
* like they could connect.
|
||||
*/
|
||||
function blockIsInSnapRadius(
|
||||
block: BlockSvg,
|
||||
offset: Coordinate,
|
||||
snapRadius: number,
|
||||
): boolean {
|
||||
return block
|
||||
.getConnections_(false)
|
||||
.some((connection) => !!connection.closest(snapRadius, offset).connection);
|
||||
}
|
||||
|
||||
export interface BlockCopyData extends ICopyData {
|
||||
blockState: State;
|
||||
typeCounts: {[key: string]: number};
|
||||
|
||||
@@ -21,9 +21,15 @@ export class WorkspaceCommentPaster
|
||||
workspace: WorkspaceSvg,
|
||||
coordinate?: Coordinate,
|
||||
): WorkspaceCommentSvg {
|
||||
const state = copyData.commentState;
|
||||
if (coordinate) {
|
||||
copyData.commentState.setAttribute('x', `${coordinate.x}`);
|
||||
copyData.commentState.setAttribute('y', `${coordinate.y}`);
|
||||
state.setAttribute('x', `${coordinate.x}`);
|
||||
state.setAttribute('y', `${coordinate.y}`);
|
||||
} else {
|
||||
const x = parseInt(state.getAttribute('x') ?? '0') + 50;
|
||||
const y = parseInt(state.getAttribute('y') ?? '0') + 50;
|
||||
state.setAttribute('x', `${x}`);
|
||||
state.setAttribute('y', `${y}`);
|
||||
}
|
||||
return WorkspaceCommentSvg.fromXmlRendered(
|
||||
copyData.commentState,
|
||||
|
||||
@@ -396,7 +396,9 @@ export function appendInternal(
|
||||
const block = appendPrivate(state, workspace, {parentConnection, isShadow});
|
||||
|
||||
eventUtils.enable();
|
||||
eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CREATE))(block));
|
||||
if (eventUtils.isEnabled()) {
|
||||
eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CREATE))(block));
|
||||
}
|
||||
eventUtils.setGroup(existingGroup);
|
||||
eventUtils.setRecordUndo(prevRecordUndo);
|
||||
|
||||
|
||||
@@ -1388,6 +1388,10 @@ export class WorkspaceSvg extends Workspace implements IASTNodeLocationSvg {
|
||||
for (let i = 0, connection; (connection = connections[i]); i++) {
|
||||
const neighbour = connection.closest(
|
||||
config.snapRadius,
|
||||
// TODO: This code doesn't work because it's passing an absolute
|
||||
// coordinate instead of a relative coordinate. Need to
|
||||
// figure out if I'm deprecating this function or if I
|
||||
// need to fix this.
|
||||
new Coordinate(blockX, blockY),
|
||||
);
|
||||
if (neighbour.connection) {
|
||||
@@ -1441,6 +1445,9 @@ export class WorkspaceSvg extends Workspace implements IASTNodeLocationSvg {
|
||||
// with any blocks.
|
||||
commentX += 50;
|
||||
commentY += 50;
|
||||
// TODO: This code doesn't work because it's using absolute coords
|
||||
// where relative coords are expected. Need to figure out what I'm
|
||||
// doing with this function and if I need to fix it.
|
||||
comment.moveBy(commentX, commentY);
|
||||
}
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user