feat(buttonNavigation): add blocked paths to avoid diagonal navigtion in menus
This commit is contained in:
@@ -69,12 +69,12 @@ const { selectedButton: selected, selectorPosition } = useButtonNavigation({
|
||||
right: "optionsGbaMode",
|
||||
},
|
||||
optionsGbaMode: {
|
||||
down: "options",
|
||||
down: ["options", false],
|
||||
left: "optionsLanguage",
|
||||
up: "optionsStartUp",
|
||||
up: ["optionsStartUp", false],
|
||||
},
|
||||
optionsStartUp: {
|
||||
right: "optionsGbaMode",
|
||||
right: ["optionsGbaMode", false],
|
||||
down: "optionsLanguage",
|
||||
},
|
||||
|
||||
@@ -89,12 +89,12 @@ const { selectedButton: selected, selectorPosition } = useButtonNavigation({
|
||||
right: "clockTime",
|
||||
},
|
||||
clockTime: {
|
||||
down: "clock",
|
||||
down: ["clock", false],
|
||||
left: "clockAlarm",
|
||||
up: "clockDate",
|
||||
up: ["clockDate", false],
|
||||
},
|
||||
clockDate: {
|
||||
right: "clockTime",
|
||||
right: ["clockTime", false],
|
||||
down: "clockAlarm",
|
||||
},
|
||||
|
||||
@@ -104,8 +104,8 @@ const { selectedButton: selected, selectorPosition } = useButtonNavigation({
|
||||
up: "userName",
|
||||
},
|
||||
userBirthday: {
|
||||
down: "user",
|
||||
up: "userColor",
|
||||
down: ["user", false],
|
||||
up: ["userColor", false],
|
||||
right: "userName",
|
||||
},
|
||||
userName: {
|
||||
@@ -115,13 +115,13 @@ const { selectedButton: selected, selectorPosition } = useButtonNavigation({
|
||||
up: "userColor",
|
||||
},
|
||||
userMessage: {
|
||||
down: "user",
|
||||
down: ["user", false],
|
||||
left: "userName",
|
||||
up: "userColor",
|
||||
up: ["userColor", false],
|
||||
},
|
||||
userColor: {
|
||||
left: "userBirthday",
|
||||
right: "userMessage",
|
||||
left: ["userBirthday", false],
|
||||
right: ["userMessage", false],
|
||||
down: "userName",
|
||||
},
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ export const useButtonNavigation = <T extends Record<string, Rect>>({
|
||||
navigation: Record<
|
||||
keyof T,
|
||||
{
|
||||
up?: keyof T | "last";
|
||||
down?: keyof T | "last";
|
||||
left?: keyof T;
|
||||
right?: keyof T;
|
||||
up?: keyof T | "last" | [buttonName: keyof T, blocked: boolean];
|
||||
down?: keyof T | "last" | [buttonName: keyof T, blocked: boolean];
|
||||
left?: keyof T | [buttonName: keyof T, blocked: boolean];
|
||||
right?: keyof T | [buttonName: keyof T, blocked: boolean];
|
||||
horizontalMode?: "navigate" | "preview";
|
||||
}
|
||||
>;
|
||||
@@ -30,6 +30,16 @@ export const useButtonNavigation = <T extends Record<string, Rect>>({
|
||||
const selectorPosition = ref<Rect>(buttons[initialButton]!);
|
||||
const nextButton = ref<Entry | undefined>();
|
||||
|
||||
const getNavigationTarget = (
|
||||
value: Entry | [buttonName: Entry, blocked: boolean] | undefined,
|
||||
): { target: Entry | "last"; blocked: boolean } | null => {
|
||||
if (!value) return null;
|
||||
if (Array.isArray(value)) {
|
||||
return { target: value[0]!, blocked: value[1] === false };
|
||||
}
|
||||
return { target: value, blocked: false };
|
||||
};
|
||||
|
||||
const buildNavigationGraph = (
|
||||
nav: typeof navigation,
|
||||
): Map<Entry, Set<Entry>> => {
|
||||
@@ -43,31 +53,42 @@ export const useButtonNavigation = <T extends Record<string, Rect>>({
|
||||
edges.set(button, new Set());
|
||||
}
|
||||
|
||||
if (navConfig.up && navConfig.up !== "last") {
|
||||
edges.get(button)!.add(navConfig.up);
|
||||
const up = getNavigationTarget(navConfig.up);
|
||||
const down = getNavigationTarget(navConfig.down);
|
||||
const left = getNavigationTarget(navConfig.left);
|
||||
const right = getNavigationTarget(navConfig.right);
|
||||
|
||||
// handle blocked paths
|
||||
if (up && up.target !== "last" && !up.blocked) {
|
||||
edges.get(button)!.add(up.target);
|
||||
}
|
||||
if (navConfig.down && navConfig.down !== "last") {
|
||||
edges.get(button)!.add(navConfig.down);
|
||||
if (down && down.target !== "last" && !down.blocked) {
|
||||
edges.get(button)!.add(down.target);
|
||||
}
|
||||
if (navConfig.left) {
|
||||
edges.get(button)!.add(navConfig.left);
|
||||
if (left && !left.blocked) {
|
||||
edges.get(button)!.add(left.target);
|
||||
}
|
||||
if (navConfig.right) {
|
||||
edges.get(button)!.add(navConfig.right);
|
||||
if (right && !right.blocked) {
|
||||
edges.get(button)!.add(right.target);
|
||||
}
|
||||
|
||||
if (navConfig.up === "last" || navConfig.down === "last") {
|
||||
if (up?.target === "last" || down?.target === "last") {
|
||||
for (const [otherButton, otherNav] of Object.entries(nav) as [
|
||||
Entry,
|
||||
(typeof nav)[Entry],
|
||||
][]) {
|
||||
if (otherButton === button) continue;
|
||||
|
||||
const otherUp = getNavigationTarget(otherNav.up);
|
||||
const otherDown = getNavigationTarget(otherNav.down);
|
||||
const otherLeft = getNavigationTarget(otherNav.left);
|
||||
const otherRight = getNavigationTarget(otherNav.right);
|
||||
|
||||
if (
|
||||
otherNav.up === button ||
|
||||
otherNav.down === button ||
|
||||
otherNav.left === button ||
|
||||
otherNav.right === button
|
||||
(otherUp?.target === button && !otherUp.blocked) ||
|
||||
(otherDown?.target === button && !otherDown.blocked) ||
|
||||
(otherLeft?.target === button && !otherLeft.blocked) ||
|
||||
(otherRight?.target === button && !otherRight.blocked)
|
||||
) {
|
||||
edges.get(button)!.add(otherButton);
|
||||
}
|
||||
@@ -203,72 +224,90 @@ export const useButtonNavigation = <T extends Record<string, Rect>>({
|
||||
let targetButton: Entry | undefined;
|
||||
|
||||
switch (key) {
|
||||
case "NDS_UP":
|
||||
if (!currentNav.up) return;
|
||||
case "NDS_UP": {
|
||||
const upConfig = getNavigationTarget(currentNav.up);
|
||||
if (!upConfig) return;
|
||||
|
||||
if (currentNav.up === "last") {
|
||||
if (upConfig.target === "last") {
|
||||
if (nextButton.value) {
|
||||
targetButton = nextButton.value;
|
||||
} else {
|
||||
targetButton = currentNav.left ?? currentNav.right;
|
||||
const leftConfig = getNavigationTarget(currentNav.left);
|
||||
const rightConfig = getNavigationTarget(currentNav.right);
|
||||
targetButton = leftConfig?.target ?? rightConfig?.target;
|
||||
}
|
||||
} else {
|
||||
if (navigation[currentNav.up].down === "last") {
|
||||
const targetNav = navigation[upConfig.target];
|
||||
const targetDownConfig = getNavigationTarget(targetNav?.down);
|
||||
if (targetDownConfig?.target === "last") {
|
||||
nextButton.value = selectedButton.value as Entry;
|
||||
}
|
||||
targetButton = currentNav.up;
|
||||
targetButton = upConfig.target;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "NDS_DOWN":
|
||||
if (!currentNav.down) return;
|
||||
case "NDS_DOWN": {
|
||||
const downConfig = getNavigationTarget(currentNav.down);
|
||||
if (!downConfig) return;
|
||||
|
||||
if (currentNav.down === "last") {
|
||||
if (downConfig.target === "last") {
|
||||
if (nextButton.value) {
|
||||
targetButton = nextButton.value;
|
||||
} else {
|
||||
targetButton = currentNav.left ?? currentNav.right;
|
||||
const leftConfig = getNavigationTarget(currentNav.left);
|
||||
const rightConfig = getNavigationTarget(currentNav.right);
|
||||
targetButton = leftConfig?.target ?? rightConfig?.target;
|
||||
}
|
||||
} else {
|
||||
if (navigation[currentNav.down].up === "last") {
|
||||
const targetNav = navigation[downConfig.target];
|
||||
const targetUpConfig = getNavigationTarget(targetNav?.up);
|
||||
if (targetUpConfig?.target === "last") {
|
||||
nextButton.value = selectedButton.value as Entry;
|
||||
}
|
||||
targetButton = currentNav.down;
|
||||
targetButton = downConfig.target;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "NDS_LEFT":
|
||||
if (!currentNav.left) return;
|
||||
case "NDS_LEFT": {
|
||||
const leftConfig = getNavigationTarget(currentNav.left);
|
||||
if (!leftConfig) return;
|
||||
|
||||
if (currentNav.horizontalMode === "preview") {
|
||||
nextButton.value = currentNav.left;
|
||||
nextButton.value = leftConfig.target;
|
||||
} else {
|
||||
targetButton = currentNav.left;
|
||||
targetButton = leftConfig.target;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "NDS_RIGHT":
|
||||
if (!currentNav.right) return;
|
||||
case "NDS_RIGHT": {
|
||||
const rightConfig = getNavigationTarget(currentNav.right);
|
||||
if (!rightConfig) return;
|
||||
|
||||
if (currentNav.horizontalMode === "preview") {
|
||||
nextButton.value = currentNav.right;
|
||||
nextButton.value = rightConfig.target;
|
||||
} else {
|
||||
targetButton = currentNav.right;
|
||||
targetButton = rightConfig.target;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "NDS_START":
|
||||
case "NDS_A":
|
||||
case "NDS_A": {
|
||||
onButtonClick?.(selectedButton.value);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetButton) {
|
||||
animateToButton(targetButton, null);
|
||||
const path = findPath(graph, selectedButton.value, targetButton);
|
||||
animateToButton(targetButton, path);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user