feat(shortcut): h/a/d

This commit is contained in:
zhangheng
2026-04-03 17:00:09 +08:00
parent b6ae2a6a29
commit bb727d52be
7 changed files with 449 additions and 83 deletions

View File

@@ -56,6 +56,7 @@ import {
import { useTopToolsStore } from "./useTopToolsStore"
import { findGroupKey } from "./util"
import { safeClone } from "./utils/clone"
import { toggleObjectHideByShortcut } from "./utils/objectVisibility"
import { labelTypeMap } from "./utils/constants"
// Splitter component - will need custom implementation or alternative
@@ -233,9 +234,11 @@ const LabelPage = ({
const {
editMode,
setEditMode,
pressA,
setPressA,
activeOperation,
setActiveOperation,
setDrawOption,
showObjectList,
showGroupList,
showTaskList,
@@ -1357,6 +1360,12 @@ const LabelPage = ({
}, [editMode, setDrawType])
const { setShift, focusInput } = useKeyEventStore()
const drawAction = useKeyboardStore((state) => state.drawAction)
const drawShortcutSnapshotRef = useRef<{
activeOperation: string
drawOption: "default" | "intersect" | "unite"
editMode: boolean
} | null>(null)
const handleShortcut = useCallback(
(key: string, ctrl: boolean) => {
@@ -1376,6 +1385,132 @@ const LabelPage = ({
[getOperationSchema, setActiveOperation]
)
const resolveSelectedObjectContext = useCallback(() => {
const imageId = useBottomToolsStore.getState().activeImage
const selectedIds = useObjectStore.getState().selectedPath[imageId] || []
if (selectedIds.length !== 1) return null
const objectId = selectedIds[0]
const imageLabel = useLabelStore.getState().label.get(imageId)
if (!imageLabel) return null
let operationId = ""
for (const [categoryId, paths] of imageLabel.entries()) {
if (paths.some((path) => path[0] === objectId)) {
operationId = categoryId
break
}
}
if (!operationId) return null
const selectedItem = usePaperStore.getState().getItemById(objectId)
if (!selectedItem?.data?.type) return null
return {
imageId,
objectId,
operationId,
objectType: selectedItem.data.type as string,
}
}, [])
const restoreDrawShortcutSnapshot = useCallback(() => {
useKeyboardStore.getState().setDrawAction("none")
const snapshot = drawShortcutSnapshotRef.current
if (!snapshot) return
setActiveOperation(snapshot.activeOperation)
setDrawOption(snapshot.drawOption)
setEditMode(snapshot.editMode)
drawShortcutSnapshotRef.current = null
}, [setActiveOperation, setDrawOption, setEditMode])
const enterSelectedObjectDrawMode = useCallback(
(mode: "add" | "subtract") => {
const selectedContext = resolveSelectedObjectContext()
if (!selectedContext) {
notifications.show({
color: "yellow",
message: "请先选中单个对象",
})
return
}
if (
mode === "add" &&
!["polygon", "brush"].includes(selectedContext.objectType)
) {
notifications.show({
color: "yellow",
message: "新增区域仅支持 polygon/brush 类型对象",
})
return
}
if (mode === "subtract" && selectedContext.objectType !== "polygon") {
notifications.show({
color: "yellow",
message: "删除区域仅支持 polygon 类型对象",
})
return
}
if (!drawShortcutSnapshotRef.current) {
const topState = useTopToolsStore.getState()
drawShortcutSnapshotRef.current = {
activeOperation: topState.activeOperation,
drawOption: topState.drawOption,
editMode: topState.editMode,
}
}
setActiveOperation(selectedContext.operationId)
setEditMode(true)
setDrawOption("default")
useKeyboardStore.getState().setDrawAction(mode)
setPressA(true)
if (!useTopToolsStore.getState().pressA) {
notifications.show({
color: "yellow",
message: "当前对象无法进入区域编辑模式",
})
restoreDrawShortcutSnapshot()
}
},
[
resolveSelectedObjectContext,
restoreDrawShortcutSnapshot,
setActiveOperation,
setDrawOption,
setEditMode,
setPressA,
]
)
const handleToggleHideSelectedObject = useCallback(() => {
const selectedContext = resolveSelectedObjectContext()
if (!selectedContext) {
notifications.show({
color: "yellow",
message: "请先选中单个对象",
})
return
}
const currentHideStatus =
useObjectStore.getState().pathStatus[
selectedContext.imageId + selectedContext.objectId
]?.HIDE || false
toggleObjectHideByShortcut(
selectedContext.imageId,
selectedContext.operationId,
selectedContext.objectId,
!currentHideStatus
)
}, [resolveSelectedObjectContext])
const handleMergeSelectedObjects = useCallback(() => {
const activeImage = useBottomToolsStore.getState().activeImage
const selectedIds =
@@ -1518,6 +1653,27 @@ const LabelPage = ({
console.log(e.key)
const mode = usePaperStore.getState().mode
const lowerKey = e.key.toLowerCase()
if (e.repeat && ["a", "d", "h"].includes(lowerKey)) return
if (!e.ctrlKey && !e.metaKey && !e.altKey && lowerKey === "h") {
e.preventDefault()
handleToggleHideSelectedObject()
return
}
if (!e.ctrlKey && !e.metaKey && !e.altKey && lowerKey === "a") {
e.preventDefault()
enterSelectedObjectDrawMode("add")
return
}
if (!e.ctrlKey && !e.metaKey && !e.altKey && lowerKey === "d") {
e.preventDefault()
enterSelectedObjectDrawMode("subtract")
return
}
if (e.altKey && e.key.toLowerCase() === "x") {
e.preventDefault()
@@ -1561,11 +1717,6 @@ const LabelPage = ({
paperContainerRef.current.handleCreatePathGroup()
}
if (e.key === "a") {
e.preventDefault()
setPressA(true)
}
if (e.key === "Control") {
e.preventDefault()
useKeyboardStore.getState().setCtrl(true)
@@ -1746,19 +1897,27 @@ const LabelPage = ({
}
}, [
editMode,
enterSelectedObjectDrawMode,
focusInput,
getOperationSchema,
handleToggleHideSelectedObject,
handleMergeSelectedObjects,
handleShortcut,
isView,
projectDetail?.label_type,
setEditMode,
setGroupScale,
setDrawOption,
setPressA,
setScale,
setShift,
])
useEffect(() => {
if (drawAction === "none" || pressA) return
restoreDrawShortcutSnapshot()
}, [drawAction, pressA, restoreDrawShortcutSnapshot])
const setGlobalLoading = useCallback((flag: boolean) => {
setLoading(flag)
}, [])
@@ -1766,6 +1925,8 @@ const LabelPage = ({
// 切换图片时清空复制保存的数组
useEffect(() => {
useKeyboardStore.getState().setCopyDataIds([])
useKeyboardStore.getState().setDrawAction("none")
drawShortcutSnapshotRef.current = null
}, [activeImage])
const handleBeforeUnload = (e: any) => {