export type ButtonConfig = [x: number, y: number, w: number, h: number]; export const useButtonNavigation = >({ buttons, initialButton, onButtonClick, navigation, }: { buttons: T; initialButton: keyof T; onButtonClick?: (buttonName: keyof T) => void; navigation: Record< keyof T, { up?: keyof T | "last"; down?: keyof T | "last"; left?: keyof T; right?: keyof T; horizontalMode?: "navigate" | "preview"; } >; }) => { const { state: modalState } = useConfirmationModal(); const selectedButton = ref(initialButton); const selectorPosition = computed(() => buttons[selectedButton.value]!); const nextButton = ref(); useScreenClick((x: number, y: number) => { if (modalState.value.isOpen) return; for (const [buttonName, config] of Object.entries(buttons) as [ keyof T, ButtonConfig, ][]) { const [sx, sy, sw, sh] = config; if (x >= sx && x <= sx + sw && y >= sy && y <= sy + sh) { if (selectedButton.value === buttonName) { onButtonClick?.(buttonName); } else { if ( (navigation[buttonName].down === "last" && navigation[selectedButton.value]!.up === buttonName) || (navigation[buttonName].up === "last" && navigation[selectedButton.value]!.down === buttonName) ) { nextButton.value = selectedButton.value; } selectedButton.value = buttonName; } break; } } }); useKeyDown((key) => { if (modalState.value.isOpen) return; const currentButton = selectedButton.value as keyof T; const currentNav = navigation[currentButton]; if (!currentNav) return; switch (key) { case "NDS_UP": if (!currentNav.up) return; if (currentNav.up === "last") { if (nextButton.value) { selectedButton.value = nextButton.value; } else { selectedButton.value = currentNav.left ?? currentNav.right; } } else { if (navigation[currentNav.up].down === "last") { nextButton.value = selectedButton.value as keyof T; } selectedButton.value = currentNav.up; } break; case "NDS_DOWN": if (!currentNav.down) return; if (currentNav.down === "last") { if (nextButton.value) { selectedButton.value = nextButton.value; } else { selectedButton.value = currentNav.left ?? currentNav.right; } } else { if (navigation[currentNav.down].up === "last") { nextButton.value = selectedButton.value as keyof T; } selectedButton.value = currentNav.down; } break; case "NDS_LEFT": if (!currentNav.left) return; if (currentNav.horizontalMode === "preview") { nextButton.value = currentNav.left; } else { selectedButton.value = currentNav.left; } break; case "NDS_RIGHT": if (!currentNav.right) return; if (currentNav.horizontalMode === "preview") { nextButton.value = currentNav.right; } else { selectedButton.value = currentNav.right; } break; case "NDS_START": case "NDS_A": onButtonClick?.(selectedButton.value); break; default: return; } }); return { selectedButton, selectorPosition, }; };