Fix Shift-Click to Deselect Edges/Faces (#5289)

* fix selections and support deselections

* test deselections

* rever commit

* undo sloppy merge

* re-commit the test

* make ubuntu happy

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* support initial sketch

* test sketch

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
max
2025-02-11 14:16:48 +01:00
committed by GitHub
parent d4d9bf6c7f
commit 652f82e8c3
4 changed files with 400 additions and 4 deletions

View File

@ -712,6 +712,330 @@ openSketch = startSketchOn('XY')
})
})
test(`Shift-click to select and deselect edges and faces`, async ({
context,
page,
homePage,
scene,
}) => {
// Code samples
const initialCode = `sketch001 = startSketchOn('XY')
|> startProfileAt([-12, -6], %)
|> line(end = [0, 12])
|> line(end = [24, 0])
|> line(end = [0, -12])
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> extrude(%, length = -12)`
// Locators
const upperEdgeLocation = { x: 600, y: 192 }
const lowerEdgeLocation = { x: 600, y: 383 }
const faceLocation = { x: 630, y: 290 }
// Click helpers
const [clickOnUpperEdge] = scene.makeMouseHelpers(
upperEdgeLocation.x,
upperEdgeLocation.y
)
const [clickOnLowerEdge] = scene.makeMouseHelpers(
lowerEdgeLocation.x,
lowerEdgeLocation.y
)
const [clickOnFace] = scene.makeMouseHelpers(faceLocation.x, faceLocation.y)
// Colors
const edgeColorWhite: [number, number, number] = [220, 220, 220] // varies from 192 to 255
const edgeColorYellow: [number, number, number] = [251, 251, 40] // vaies from 12 to 67
const faceColorGray: [number, number, number] = [168, 168, 168]
const faceColorYellow: [number, number, number] = [155, 155, 155]
const tolerance = 40
const timeout = 150
// Setup
await test.step(`Initial test setup`, async () => {
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
// Wait for the scene and stream to load
await scene.expectPixelColor(faceColorGray, faceLocation, tolerance)
})
await test.step('Select and deselect a single edge', async () => {
await test.step('Click the edge', async () => {
await scene.expectPixelColor(
edgeColorWhite,
upperEdgeLocation,
tolerance
)
await clickOnUpperEdge()
await scene.expectPixelColor(
edgeColorYellow,
upperEdgeLocation,
tolerance
)
})
await test.step('Shift-click the same edge to deselect', async () => {
await page.keyboard.down('Shift')
await page.waitForTimeout(timeout)
await clickOnUpperEdge()
await page.waitForTimeout(timeout)
await page.keyboard.up('Shift')
await scene.expectPixelColor(
edgeColorWhite,
upperEdgeLocation,
tolerance
)
})
})
await test.step('Select and deselect multiple objects', async () => {
await test.step('Select both edges and the face', async () => {
await test.step('Select the upper edge', async () => {
await scene.expectPixelColor(
edgeColorWhite,
upperEdgeLocation,
tolerance
)
await clickOnUpperEdge()
await scene.expectPixelColor(
edgeColorYellow,
upperEdgeLocation,
tolerance
)
})
await test.step('Select the lower edge (Shift-click)', async () => {
await scene.expectPixelColor(
edgeColorWhite,
lowerEdgeLocation,
tolerance
)
await page.keyboard.down('Shift')
await page.waitForTimeout(timeout)
await clickOnLowerEdge()
await page.waitForTimeout(timeout)
await page.keyboard.up('Shift')
await scene.expectPixelColor(
edgeColorYellow,
lowerEdgeLocation,
tolerance
)
})
await test.step('Select the face (Shift-click)', async () => {
await scene.expectPixelColor(faceColorGray, faceLocation, tolerance)
await page.keyboard.down('Shift')
await page.waitForTimeout(timeout)
await clickOnFace()
await page.waitForTimeout(timeout)
await page.keyboard.up('Shift')
await scene.expectPixelColor(faceColorYellow, faceLocation, tolerance)
})
})
await test.step('Deselect them one by one', async () => {
await test.step('Deselect the face (Shift-click)', async () => {
await scene.expectPixelColor(faceColorYellow, faceLocation, tolerance)
await page.keyboard.down('Shift')
await page.waitForTimeout(timeout)
await clickOnFace()
await page.waitForTimeout(timeout)
await page.keyboard.up('Shift')
await scene.expectPixelColor(faceColorGray, faceLocation, tolerance)
})
await test.step('Deselect the lower edge (Shift-click)', async () => {
await scene.expectPixelColor(
edgeColorYellow,
lowerEdgeLocation,
tolerance
)
await page.keyboard.down('Shift')
await page.waitForTimeout(timeout)
await clickOnLowerEdge()
await page.waitForTimeout(timeout)
await page.keyboard.up('Shift')
await scene.expectPixelColor(
edgeColorWhite,
lowerEdgeLocation,
tolerance
)
})
await test.step('Deselect the upper edge (Shift-click)', async () => {
await scene.expectPixelColor(
edgeColorYellow,
upperEdgeLocation,
tolerance
)
await page.keyboard.down('Shift')
await page.waitForTimeout(timeout)
await clickOnUpperEdge()
await page.waitForTimeout(timeout)
await page.keyboard.up('Shift')
await scene.expectPixelColor(
edgeColorWhite,
upperEdgeLocation,
tolerance
)
})
})
})
})
test(`Shift-click to select and deselect sketch segments`, async ({
page,
homePage,
scene,
editor,
}) => {
// Locators
const firstPointLocation = { x: 200, y: 100 }
const secondPointLocation = { x: 800, y: 100 }
const thirdPointLocation = { x: 800, y: 400 }
const fristSegmentLocation = { x: 750, y: 100 }
const secondSegmentLocation = { x: 800, y: 150 }
const planeLocation = { x: 700, y: 200 }
// Click helpers
const [clickFirstPoint] = scene.makeMouseHelpers(
firstPointLocation.x,
firstPointLocation.y
)
const [clickSecondPoint] = scene.makeMouseHelpers(
secondPointLocation.x,
secondPointLocation.y
)
const [clickThirdPoint] = scene.makeMouseHelpers(
thirdPointLocation.x,
thirdPointLocation.y
)
const [clickFirstSegment] = scene.makeMouseHelpers(
fristSegmentLocation.x,
fristSegmentLocation.y
)
const [clickSecondSegment] = scene.makeMouseHelpers(
secondSegmentLocation.x,
secondSegmentLocation.y
)
const [clickPlane] = scene.makeMouseHelpers(
planeLocation.x,
planeLocation.y
)
// Colors
const edgeColorWhite: [number, number, number] = [220, 220, 220]
const edgeColorBlue: [number, number, number] = [20, 20, 200]
const backgroundColor: [number, number, number] = [30, 30, 30]
const tolerance = 40
const timeout = 150
// Setup
await test.step(`Initial test setup`, async () => {
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
// Wait for the scene and stream to load
await scene.expectPixelColor(
backgroundColor,
secondPointLocation,
tolerance
)
})
await test.step('Select and deselect a single sketch segment', async () => {
await test.step('Get into sketch mode', async () => {
await editor.closePane()
await page.waitForTimeout(timeout)
await page.getByRole('button', { name: 'Start Sketch' }).click()
await page.waitForTimeout(timeout)
await clickPlane()
await page.waitForTimeout(1000)
})
await test.step('Draw sketch', async () => {
await clickFirstPoint()
await page.waitForTimeout(timeout)
await clickSecondPoint()
await page.waitForTimeout(timeout)
await clickThirdPoint()
await page.waitForTimeout(timeout)
})
await test.step('Deselect line tool', async () => {
const btnLine = page.getByTestId('line')
const btnLineAriaPressed = await btnLine.getAttribute('aria-pressed')
if (btnLineAriaPressed === 'true') {
await btnLine.click()
}
await page.waitForTimeout(timeout)
})
await test.step('Select the first segment', async () => {
await page.waitForTimeout(timeout)
await clickFirstSegment()
await page.waitForTimeout(timeout)
await scene.expectPixelColor(
edgeColorBlue,
fristSegmentLocation,
tolerance
)
await scene.expectPixelColor(
edgeColorWhite,
secondSegmentLocation,
tolerance
)
})
await test.step('Select the second segment (Shift-click)', async () => {
await page.keyboard.down('Shift')
await page.waitForTimeout(timeout)
await clickSecondSegment()
await page.waitForTimeout(timeout)
await page.keyboard.up('Shift')
await scene.expectPixelColor(
edgeColorBlue,
fristSegmentLocation,
tolerance
)
await scene.expectPixelColor(
edgeColorBlue,
secondSegmentLocation,
tolerance
)
})
await test.step('Deselect the first segment', async () => {
await page.keyboard.down('Shift')
await page.waitForTimeout(timeout)
await clickFirstSegment()
await page.waitForTimeout(timeout)
await page.keyboard.up('Shift')
await scene.expectPixelColor(
edgeColorWhite,
fristSegmentLocation,
tolerance
)
await scene.expectPixelColor(
edgeColorBlue,
secondSegmentLocation,
tolerance
)
})
await test.step('Deselect the second segment', async () => {
await page.keyboard.down('Shift')
await page.waitForTimeout(timeout)
await clickSecondSegment()
await page.waitForTimeout(timeout)
await page.keyboard.up('Shift')
await scene.expectPixelColor(
edgeColorWhite,
fristSegmentLocation,
tolerance
)
await scene.expectPixelColor(
edgeColorWhite,
secondSegmentLocation,
tolerance
)
})
})
})
test(`Offset plane point-and-click`, async ({
context,
page,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -329,11 +329,83 @@ export const ModelingMachineProvider = ({
otherSelections: [],
}
} else if (setSelections.selection && editorManager.isShiftDown) {
selections = {
graphSelections: [
// selecting and deselecting multiple objects
/**
* There are two scenarios:
* 1. General case:
* When selecting and deselecting edges,
* faces or segment (during sketch edit)
* we use its artifact ID to identify the selection
* 2. Initial sketch setup:
* The artifact is not yet created
* so we use the codeRef.range
*/
let updatedSelections: typeof selectionRanges.graphSelections
// 1. General case: Artifact exists, use its ID
if (setSelections.selection.artifact?.id) {
// check if already selected
const alreadySelected = selectionRanges.graphSelections.some(
(selection) =>
selection.artifact?.id ===
setSelections.selection?.artifact?.id
)
if (
alreadySelected &&
setSelections.selection?.artifact?.id
) {
// remove it
updatedSelections = selectionRanges.graphSelections.filter(
(selection) =>
selection.artifact?.id !==
setSelections.selection?.artifact?.id
)
} else {
// add it
updatedSelections = [
...selectionRanges.graphSelections,
setSelections.selection,
],
]
}
} else {
// 2. Initial sketch setup: Artifact not yet created use codeRef.range
const selectionRange = JSON.stringify(
setSelections.selection?.codeRef?.range
)
// check if already selected
const alreadySelected = selectionRanges.graphSelections.some(
(selection) => {
const existingRange = JSON.stringify(
selection.codeRef?.range
)
return existingRange === selectionRange
}
)
if (
alreadySelected &&
setSelections.selection?.codeRef?.range
) {
// remove it
updatedSelections = selectionRanges.graphSelections.filter(
(selection) =>
JSON.stringify(selection.codeRef?.range) !==
selectionRange
)
} else {
// add it
updatedSelections = [
...selectionRanges.graphSelections,
setSelections.selection,
]
}
}
selections = {
graphSelections: updatedSelections,
otherSelections: selectionRanges.otherSelections,
}
}