Skip to content

Commit

Permalink
Fix/zoom in grid move (#6052)
Browse files Browse the repository at this point in the history
**Problem:**

Moving grid cells (incl. drag-duplicating them) doesn't work with zoom >
1.

**Fix:**

Accommodate for the canvas scale when detecting target cells in the
strategy helper.

Fixes #6051
  • Loading branch information
ruggi authored Jul 10, 2024
1 parent bdb8b8e commit b1e856e
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { CanvasVector } from '../../../../core/shared/math-utils'
import {
offsetPoint,
rectContainsPoint,
scaleRect,
windowRectangle,
type WindowPoint,
} from '../../../../core/shared/math-utils'
Expand All @@ -22,12 +23,12 @@ import type { GridCellCoordinates } from '../../controls/grid-controls'
import { gridCellCoordinates } from '../../controls/grid-controls'
import * as EP from '../../../../core/shared/element-path'

export function getGridCellUnderMouse(mousePoint: WindowPoint) {
return getGridCellAtPoint(mousePoint, false)
export function getGridCellUnderMouse(mousePoint: WindowPoint, canvasScale: number) {
return getGridCellAtPoint(mousePoint, canvasScale, false)
}

function getGridCellUnderMouseRecursive(mousePoint: WindowPoint) {
return getGridCellAtPoint(mousePoint, true)
function getGridCellUnderMouseRecursive(mousePoint: WindowPoint, canvasScale: number) {
return getGridCellAtPoint(mousePoint, canvasScale, true)
}

const gridCellTargetIdPrefix = 'grid-cell-target-'
Expand All @@ -46,14 +47,19 @@ function isGridCellTargetId(id: string): boolean {

function getGridCellAtPoint(
windowPoint: WindowPoint,
canvasScale: number,
duplicating: boolean,
): { id: string; coordinates: GridCellCoordinates } | null {
function maybeRecursivelyFindCellAtPoint(elements: Element[]): Element | null {
// If this used during duplication, the canvas controls will be in the way and we need to traverse the children too.
for (const element of elements) {
if (isGridCellTargetId(element.id)) {
const rect = element.getBoundingClientRect()
if (rectContainsPoint(windowRectangle(rect), windowPoint)) {
const domRect = element.getBoundingClientRect()
const windowRect =
canvasScale > 1
? scaleRect(windowRectangle(domRect), canvasScale)
: windowRectangle(domRect)
if (rectContainsPoint(windowRect, windowPoint)) {
return element
}
}
Expand Down Expand Up @@ -119,7 +125,12 @@ export function runGridRearrangeMove(
canvasOffset,
)

const newTargetCell = getTargetCell(customState.targetCell, duplicating, mouseWindowPoint)
const newTargetCell = getTargetCell(
customState.targetCell,
canvasScale,
duplicating,
mouseWindowPoint,
)
if (newTargetCell == null) {
return {
commands: [],
Expand Down Expand Up @@ -176,13 +187,14 @@ export function runGridRearrangeMove(

function getTargetCell(
previousTargetCell: GridCellCoordinates | null,
canvasScale: number,
duplicating: boolean,
mouseWindowPoint: WindowPoint,
): GridCellCoordinates | null {
let cell = previousTargetCell ?? null
const cellUnderMouse = duplicating
? getGridCellUnderMouseRecursive(mouseWindowPoint)
: getGridCellUnderMouse(mouseWindowPoint)
? getGridCellUnderMouseRecursive(mouseWindowPoint, canvasScale)
: getGridCellUnderMouse(mouseWindowPoint, canvasScale)
if (cellUnderMouse != null) {
cell = cellUnderMouse.coordinates
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,40 @@
import * as EP from '../../../../core/shared/element-path'
import { getRectCenter, localRectangle } from '../../../../core/shared/math-utils'
import { selectComponentsForTest } from '../../../../utils/utils.test-utils'
import CanvasActions from '../../canvas-actions'
import { GridCellTestId } from '../../controls/grid-controls'
import { mouseDragFromPointToPoint } from '../../event-helpers.test-utils'
import { renderTestEditorWithCode } from '../../ui-jsx.test-utils'
import { gridCellTargetId } from './grid-helpers'

describe('grid rearrange move strategy', () => {
it('can rearrange elements on a grid', async () => {
const editor = await renderTestEditorWithCode(ProjectCode, 'await-first-dom-report')

const elementPathToDrag = EP.fromString('sb/scene/grid/aaa')

await selectComponentsForTest(editor, [elementPathToDrag])

const sourceGridCell = editor.renderedDOM.getByTestId(GridCellTestId(elementPathToDrag))
const targetGridCell = editor.renderedDOM.getByTestId(
gridCellTargetId(EP.fromString('sb/scene/grid'), 2, 3),
)

await mouseDragFromPointToPoint(
sourceGridCell,
{
x: sourceGridCell.getBoundingClientRect().x + 10,
y: sourceGridCell.getBoundingClientRect().y + 10,
},
getRectCenter(
localRectangle({
x: targetGridCell.getBoundingClientRect().x,
y: targetGridCell.getBoundingClientRect().y,
width: targetGridCell.getBoundingClientRect().width,
height: targetGridCell.getBoundingClientRect().height,
}),
),
)
const { gridRowStart, gridRowEnd, gridColumnStart, gridColumnEnd } = await runMoveTest({
scale: 1,
})
expect({ gridRowStart, gridRowEnd, gridColumnStart, gridColumnEnd }).toEqual({
gridColumnEnd: '7',
gridColumnStart: '3',
gridRowEnd: '4',
gridRowStart: '2',
})
})
it('can rearrange elements on a grid (zoom out)', async () => {
const { gridRowStart, gridRowEnd, gridColumnStart, gridColumnEnd } = await runMoveTest({
scale: 0.5,
})
expect({ gridRowStart, gridRowEnd, gridColumnStart, gridColumnEnd }).toEqual({
gridColumnEnd: '7',
gridColumnStart: '3',
gridRowEnd: '4',
gridRowStart: '2',
})
})

const { gridRowStart, gridRowEnd, gridColumnStart, gridColumnEnd } =
editor.renderedDOM.getByTestId('aaa').style
it('can rearrange elements on a grid (zoom in)', async () => {
const { gridRowStart, gridRowEnd, gridColumnStart, gridColumnEnd } = await runMoveTest({
scale: 2,
})
expect({ gridRowStart, gridRowEnd, gridColumnStart, gridColumnEnd }).toEqual({
gridColumnEnd: '7',
gridColumnStart: '3',
Expand All @@ -46,6 +44,42 @@ describe('grid rearrange move strategy', () => {
})
})

async function runMoveTest(props: { scale: number }) {
const editor = await renderTestEditorWithCode(ProjectCode, 'await-first-dom-report')

const elementPathToDrag = EP.fromString('sb/scene/grid/aaa')

await selectComponentsForTest(editor, [elementPathToDrag])

await editor.dispatch([CanvasActions.zoom(props.scale)], true)

const sourceGridCell = editor.renderedDOM.getByTestId(GridCellTestId(elementPathToDrag))
const targetGridCell = editor.renderedDOM.getByTestId(
gridCellTargetId(EP.fromString('sb/scene/grid'), 2, 3),
)

const sourceRect = sourceGridCell.getBoundingClientRect()
const targetRect = targetGridCell.getBoundingClientRect()

await mouseDragFromPointToPoint(
sourceGridCell,
{
x: sourceRect.x * (props.scale > 1 ? props.scale : 1) + 10,
y: sourceRect.y * (props.scale > 1 ? props.scale : 1) + 10,
},
getRectCenter(
localRectangle({
x: targetRect.x * (props.scale > 1 ? props.scale : 1),
y: targetRect.y * (props.scale > 1 ? props.scale : 1),
width: targetRect.width,
height: targetRect.height,
}),
),
)

return editor.renderedDOM.getByTestId('aaa').style
}

const ProjectCode = `import * as React from 'react'
import { Scene, Storyboard, Placeholder } from 'utopia-api'
Expand Down

0 comments on commit b1e856e

Please sign in to comment.