feat(label): add right select

This commit is contained in:
zhangheng
2026-04-10 11:21:30 +08:00
parent 6aff25a38f
commit 98d697a542

View File

@@ -318,6 +318,229 @@ const hidePaperContextMenu = () => {
}) })
} }
const PAPER_OBJECT_TYPES = ["rectangle", "polygon", "brush", "point"] as const
const PAPER_CONTEXT_HIT_TYPES = [
...PAPER_OBJECT_TYPES,
"circle",
"mask",
"pathBuff",
"pathCircle",
] as const
const isPaperObjectType = (type: unknown) =>
typeof type === "string" &&
PAPER_OBJECT_TYPES.includes(type as (typeof PAPER_OBJECT_TYPES)[number])
const isPaperContextHitType = (type: unknown) =>
typeof type === "string" &&
PAPER_CONTEXT_HIT_TYPES.includes(
type as (typeof PAPER_CONTEXT_HIT_TYPES)[number]
)
const getPaperCursorByMode = (
mode: PaperState["mode"] = usePaperStore.getState().mode
) => {
if (
["polygon", "rectangle", "brush", "point", "support"].includes(mode || "")
) {
return "crosshair"
}
return "default"
}
const syncPaperCursorByMode = (
mode: PaperState["mode"] = usePaperStore.getState().mode
) => {
if (usePaperStore.getState().el) {
usePaperStore.getState().el!.style.cursor = getPaperCursorByMode(mode)
}
}
const clearPaperSelectedObjects = () => {
const group = usePaperStore.getState().group
if (!group) return
group
.getItems({
match: (item: paper.Item) => !!item.data?.selected,
})
.forEach((item) => {
item.data.selected = false
})
usePaperSupportStore.getState().clearAll()
const activeImage = useBottomToolsStore.getState().activeImage
const selectedIds = [
...(useObjectStore.getState().selectedPath[activeImage] || []),
]
selectedIds.forEach((id) => {
usePaperStore
.getState()
.getItemsById(id)
.forEach((item) => {
if (
(item instanceof paper.Path || item instanceof paper.CompoundPath) &&
["rectangle", "polygon"].includes(item.data?.type || "")
) {
item.fillColor = item.data?.blankColor || item.fillColor || null
}
})
})
selectedIds.forEach((id) => {
useObjectStore.getState().updateSelectedPath(activeImage, "DELETE", id)
})
}
const applyPaperSelectedPath = (item: paper.Path | paper.CompoundPath) => {
if (item.data.type === "rectangle" && item instanceof paper.Path) {
item.data.selected = true
usePaperSupportStore.getState().handleBufferPaths(item)
usePaperSupportStore.getState().handleCircles(item)
item.fillColor = item.data.fillColor || item.fillColor || null
} else if (item.data.type === "brush") {
item.data.selected = true
if (item instanceof paper.Path) {
usePaperSupportStore.getState().handleMask(item)
usePaperSupportStore.getState().handleBufferPaths(item)
usePaperSupportStore.getState().handleCircles(item)
}
} else if (item.data.type === "point" && item instanceof paper.Path) {
item.data.selected = true
usePaperSupportStore.getState().handleMask(item)
usePaperSupportStore.getState().handleText(item)
usePaperSupportStore.getState().handleBufferPaths(item)
usePaperSupportStore.getState().handleCircles(item)
} else if (item.data.type === "polygon") {
item.data.selected = true
if (!(item instanceof paper.Path) || !item.data?.isHollowPolygon) {
item.fillColor = item.data.fillColor || item.fillColor || null
}
}
item.bringToFront()
}
const getPaperSelectionPath = (
id: number,
localPoint: paper.Point,
preferredItem?: paper.Item | null
) => {
if (
preferredItem instanceof paper.Path &&
isPaperObjectType(preferredItem.data?.type)
) {
return preferredItem
}
const objectPaths = usePaperStore
.getState()
.getItemsById(id)
.filter(
(item): item is paper.Path =>
item instanceof paper.Path && isPaperObjectType(item.data?.type)
)
if (!objectPaths.length) return null
if (objectPaths.length === 1) return objectPaths[0]
let nearestPath = objectPaths[0]
for (let index = 1; index < objectPaths.length; index += 1) {
const item = objectPaths[index]
if (
item.getNearestPoint(localPoint).getDistance(localPoint) <
nearestPath.getNearestPoint(localPoint).getDistance(localPoint)
) {
nearestPath = item
}
}
return nearestPath
}
const trySelectPaperObjectByPoint = (clickPoint: paper.Point) => {
const group = usePaperStore.getState().group
if (!group) return false
const hitResult = group.hitTest(clickPoint, {
segments: true,
stroke: true,
curves: true,
fill: true,
guides: false,
tolerance: usePaperStore.getState().paperScope
? 8 / useTopToolsStore.getState().scale
: 0,
match: (hit: paper.HitResult) =>
!!hit.item.data?.id && isPaperContextHitType(hit.item.data?.type),
})
const id = hitResult?.item?.data?.id
if (!id) return false
clearPaperSelectedObjects()
const localPoint = group.globalToLocal(clickPoint)
const selectedPath = getPaperSelectionPath(id, localPoint, hitResult?.item)
const operationId = (
selectedPath?.data?.operationId ||
usePaperStore.getState().getItemById(id)?.data?.operationId ||
hitResult?.item?.data?.operationId ||
""
).toString()
usePaperStore
.getState()
.getItemsById(id)
.forEach((item) => {
if (!(item instanceof paper.Path || item instanceof paper.CompoundPath))
return
if (!isPaperObjectType(item.data?.type)) return
if (item.data.type === "polygon") {
applyPaperSelectedPath(item)
return
}
if (
item instanceof paper.CompoundPath ||
(selectedPath && item === selectedPath)
) {
applyPaperSelectedPath(item)
}
})
const activeImage = useBottomToolsStore.getState().activeImage
useObjectStore.getState().updateSelectedPath(activeImage, "ADD", id)
const category = useTopToolsStore
.getState()
.objectOperations.find(
(item) => item.category_id.toString() === operationId
)
const { top, left } = getShowContextMenuPosition(
clickPoint,
(category || {}) as Project.LabelSchemaList,
5,
5
)
useOtherToolsStore.getState().setShowContextMenu({
showContextMenu: true,
position: { top, left },
imageId:
selectedPath?.data?.imageId ||
hitResult?.item?.data?.imageId ||
activeImage,
operationId,
id,
})
return true
}
let isMiddleMousePanning = false let isMiddleMousePanning = false
let hoveredBufferPathInfo: { let hoveredBufferPathInfo: {
id: number id: number
@@ -3530,10 +3753,16 @@ export const usePaperStore = create<PaperState>((set) => ({
if (startMiddleMousePan(e.event)) return if (startMiddleMousePan(e.event)) return
if (e.event.button === 2) { if (e.event.button === 2) {
e.event.preventDefault() e.event.preventDefault()
if (polygon || points.length) {
hidePaperContextMenu() hidePaperContextMenu()
undoPolygonPoint() undoPolygonPoint()
return return
} }
if (!trySelectPaperObjectByPoint(e.point)) {
hidePaperContextMenu()
}
return
}
if (e.event.button !== 0) return if (e.event.button !== 0) return
isMouseDown = true isMouseDown = true
@@ -4113,8 +4342,18 @@ export const usePaperStore = create<PaperState>((set) => ({
let rect: paper.Path.Rectangle | null let rect: paper.Path.Rectangle | null
let rectangleTool = paperRectangleTool let rectangleTool = paperRectangleTool
rectangleTool.onMouseDown = (e: { event: MouseEvent }) => { rectangleTool.onMouseDown = (e: {
event: MouseEvent
point: paper.Point
}) => {
if (startMiddleMousePan(e.event)) return if (startMiddleMousePan(e.event)) return
if (e.event.button === 2) {
e.event.preventDefault()
if (!trySelectPaperObjectByPoint(e.point)) {
hidePaperContextMenu()
}
return
}
if (e.event && e.event.button !== 2) if (e.event && e.event.button !== 2)
useOtherToolsStore.getState().setShowContextMenu({ useOtherToolsStore.getState().setShowContextMenu({
showContextMenu: false, showContextMenu: false,
@@ -4463,10 +4702,16 @@ export const usePaperStore = create<PaperState>((set) => ({
if (startMiddleMousePan(e.event)) return if (startMiddleMousePan(e.event)) return
if (e.event.button === 2) { if (e.event.button === 2) {
e.event.preventDefault() e.event.preventDefault()
if (myPath || points.length) {
hidePaperContextMenu() hidePaperContextMenu()
undoBrushPoint() undoBrushPoint()
return return
} }
if (!trySelectPaperObjectByPoint(e.point)) {
hidePaperContextMenu()
}
return
}
if (e.event.button !== 0) return if (e.event.button !== 0) return
// 绘制时去除选中辅助 // 绘制时去除选中辅助
@@ -4934,10 +5179,16 @@ export const usePaperStore = create<PaperState>((set) => ({
if (startMiddleMousePan(e.event)) return if (startMiddleMousePan(e.event)) return
if (e.event.button === 2) { if (e.event.button === 2) {
e.event.preventDefault() e.event.preventDefault()
if (myPath || points.length) {
hidePaperContextMenu() hidePaperContextMenu()
undoPoint() undoPoint()
return return
} }
if (!trySelectPaperObjectByPoint(e.point)) {
hidePaperContextMenu()
}
return
}
if (e.event.button !== 0) return if (e.event.button !== 0) return
// 绘制时去除选中辅助 // 绘制时去除选中辅助
@@ -5426,7 +5677,10 @@ export const usePaperStore = create<PaperState>((set) => ({
clearHoveredBufferPathHighlight() clearHoveredBufferPathHighlight()
circle.strokeWidth = 4 circle.strokeWidth = 4
circle.fillColor = new paper.Color("#FFF") circle.fillColor = new paper.Color("#FFF")
if (usePaperStore.getState().el) { if (
usePaperStore.getState().mode === "pan" &&
usePaperStore.getState().el
) {
usePaperStore.getState().el!.style.cursor = "move" usePaperStore.getState().el!.style.cursor = "move"
} }
} }
@@ -5436,9 +5690,7 @@ export const usePaperStore = create<PaperState>((set) => ({
} }
circle.strokeWidth = 2 circle.strokeWidth = 2
circle.fillColor = color circle.fillColor = color
if (usePaperStore.getState().el) { syncPaperCursorByMode()
usePaperStore.getState().el!.style.cursor = "default"
}
} }
return circle return circle
}, },
@@ -5485,7 +5737,7 @@ export const usePaperStore = create<PaperState>((set) => ({
!hoveredPathCircleInfo?.item.parent && !hoveredPathCircleInfo?.item.parent &&
!hoveredBufferPathInfo !hoveredBufferPathInfo
) { ) {
usePaperStore.getState().el!.style.cursor = "default" syncPaperCursorByMode()
} }
} }
const scheduleClear = () => { const scheduleClear = () => {
@@ -5564,45 +5816,31 @@ export const usePaperStore = create<PaperState>((set) => ({
switch (modeParam) { switch (modeParam) {
case "pan": case "pan":
usePaperStore.getState().panTool?.activate() usePaperStore.getState().panTool?.activate()
if (usePaperStore.getState().el) { syncPaperCursorByMode(modeParam)
usePaperStore.getState().el!.style.cursor = "default"
}
break break
case "polygon": case "polygon":
usePaperStore.getState().polygonTool?.activate() usePaperStore.getState().polygonTool?.activate()
if (usePaperStore.getState().el) { syncPaperCursorByMode(modeParam)
usePaperStore.getState().el!.style.cursor = "crosshair"
}
break break
case "rectangle": case "rectangle":
usePaperStore.getState().rectangleTool?.activate() usePaperStore.getState().rectangleTool?.activate()
if (usePaperStore.getState().el) { syncPaperCursorByMode(modeParam)
usePaperStore.getState().el!.style.cursor = "crosshair"
}
break break
case "brush": case "brush":
usePaperStore.getState().brushTool?.activate() usePaperStore.getState().brushTool?.activate()
if (usePaperStore.getState().el) { syncPaperCursorByMode(modeParam)
usePaperStore.getState().el!.style.cursor = "crosshair"
}
break break
case "point": case "point":
usePaperStore.getState().pointTool?.activate() usePaperStore.getState().pointTool?.activate()
if (usePaperStore.getState().el) { syncPaperCursorByMode(modeParam)
usePaperStore.getState().el!.style.cursor = "crosshair"
}
break break
case "support": case "support":
usePaperStore.getState().supportTool?.activate() usePaperStore.getState().supportTool?.activate()
if (usePaperStore.getState().el) { syncPaperCursorByMode(modeParam)
usePaperStore.getState().el!.style.cursor = "crosshair"
}
break break
default: default:
usePaperStore.getState().viewTool?.activate() usePaperStore.getState().viewTool?.activate()
if (usePaperStore.getState().el) { syncPaperCursorByMode(modeParam)
usePaperStore.getState().el!.style.cursor = "default"
}
break break
} }
}, },