fix: 设置界面不能按 esc 返回

This commit is contained in:
unanmed 2025-09-25 18:42:27 +08:00
parent e666b85ba3
commit 82d58756b7
5 changed files with 61 additions and 19 deletions

View File

@ -33,6 +33,8 @@ export interface ConfirmBoxProps extends DefaultProps, TextContentProps {
color?: CanvasStyle; color?: CanvasStyle;
/** 对话框边框颜色,当未设置 winskin 时生效 */ /** 对话框边框颜色,当未设置 winskin 时生效 */
border?: CanvasStyle; border?: CanvasStyle;
/** 按键作用域,如果需要同作用域按键,那么需要传入 */
scope?: symbol;
} }
export type ConfirmBoxEmits = { export type ConfirmBoxEmits = {
@ -53,7 +55,8 @@ const confirmBoxProps = {
'winskin', 'winskin',
'defaultYes', 'defaultYes',
'color', 'color',
'border' 'border',
'scope'
], ],
emits: ['no', 'yes'] emits: ['no', 'yes']
} satisfies SetupComponentOptions< } satisfies SetupComponentOptions<
@ -143,7 +146,7 @@ export const ConfirmBox = defineComponent<
noSize.value = [width, height]; noSize.value = [width, height];
}; };
const [key] = useKey(); const [key] = useKey(false, props.scope);
key.realize('confirm', () => { key.realize('confirm', () => {
if (selected.value) emit('yes'); if (selected.value) emit('yes');
else emit('no'); else emit('no');
@ -242,6 +245,8 @@ export interface ChoicesProps extends DefaultProps, TextContentProps {
interval?: number; interval?: number;
/** 默认选中的选项索引 */ /** 默认选中的选项索引 */
selected?: number; selected?: number;
/** 按键作用域,如果需要同作用域按键,那么需要传入,例如系统设置 UI */
scope?: symbol;
} }
export type ChoicesEmits = { export type ChoicesEmits = {
@ -265,7 +270,8 @@ const choicesProps = {
'titleFill', 'titleFill',
'pad', 'pad',
'interval', 'interval',
'selected' 'selected',
'scope'
], ],
emits: ['choose'] emits: ['choose']
} satisfies SetupComponentOptions< } satisfies SetupComponentOptions<
@ -464,7 +470,7 @@ export const Choices = defineComponent<
selected.value = 0; selected.value = 0;
}; };
const [key] = useKey(); const [key] = useKey(false, props.scope);
key.realize('moveUp', () => { key.realize('moveUp', () => {
if (selected.value === 0) { if (selected.value === 0) {
if (pageCom.value?.now() !== 0) { if (pageCom.value?.now() !== 0) {

View File

@ -25,6 +25,7 @@ import { saveWithExist } from './save';
import { compressToBase64 } from 'lz-string'; import { compressToBase64 } from 'lz-string';
import { ViewMapUI } from './viewmap'; import { ViewMapUI } from './viewmap';
import { CENTER_LOC, FULL_LOC, MAIN_HEIGHT, POP_BOX_WIDTH } from '../shared'; import { CENTER_LOC, FULL_LOC, MAIN_HEIGHT, POP_BOX_WIDTH } from '../shared';
import { useKey } from '../use';
export interface MainSettingsProps export interface MainSettingsProps
extends Partial<ChoicesProps>, extends Partial<ChoicesProps>,
@ -62,6 +63,9 @@ export const MainSettings = defineComponent<MainSettingsProps>(props => {
[MainChoice.Back, '返回游戏'] [MainChoice.Back, '返回游戏']
]; ];
const [key, scope] = useKey();
key.realize('exit', () => props.controller.close(props.instance));
const choose = async (key: ChoiceKey) => { const choose = async (key: ChoiceKey) => {
switch (key) { switch (key) {
case MainChoice.SystemSetting: { case MainChoice.SystemSetting: {
@ -125,6 +129,7 @@ export const MainSettings = defineComponent<MainSettingsProps>(props => {
onChoose={choose} onChoose={choose}
maxHeight={MAIN_HEIGHT - 64} maxHeight={MAIN_HEIGHT - 64}
interval={8} interval={8}
scope={scope}
/> />
); );
}, mainSettingsProps); }, mainSettingsProps);
@ -150,6 +155,9 @@ export const ReplaySettings = defineComponent<MainSettingsProps>(props => {
[ReplayChoice.Back, '返回游戏'] [ReplayChoice.Back, '返回游戏']
]; ];
const [key, scope] = useKey();
key.realize('exit', () => props.controller.close(props.instance));
const choose = async (key: ChoiceKey) => { const choose = async (key: ChoiceKey) => {
switch (key) { switch (key) {
case ReplayChoice.Start: { case ReplayChoice.Start: {
@ -237,6 +245,7 @@ export const ReplaySettings = defineComponent<MainSettingsProps>(props => {
width={POP_BOX_WIDTH} width={POP_BOX_WIDTH}
onChoose={choose} onChoose={choose}
interval={8} interval={8}
scope={scope}
/> />
); );
}, mainSettingsProps); }, mainSettingsProps);
@ -260,6 +269,9 @@ export const GameInfo = defineComponent<MainSettingsProps>(props => {
[GameInfoChoice.Back, '返回主菜单'] [GameInfoChoice.Back, '返回主菜单']
]; ];
const [key, scope] = useKey();
key.realize('exit', () => props.controller.close(props.instance));
const choose = async (key: ChoiceKey) => { const choose = async (key: ChoiceKey) => {
switch (key) { switch (key) {
case GameInfoChoice.Statistics: { case GameInfoChoice.Statistics: {
@ -324,6 +336,7 @@ export const GameInfo = defineComponent<MainSettingsProps>(props => {
width={POP_BOX_WIDTH} width={POP_BOX_WIDTH}
onChoose={choose} onChoose={choose}
interval={8} interval={8}
scope={scope}
/> />
); );
}, mainSettingsProps); }, mainSettingsProps);
@ -351,6 +364,9 @@ export const SyncSave = defineComponent<MainSettingsProps>(props => {
[SyncSaveChoice.Back, '返回上一级'] [SyncSaveChoice.Back, '返回上一级']
]; ];
const [key, scope] = useKey();
key.realize('exit', () => props.controller.close(props.instance));
const choose = async (key: ChoiceKey) => { const choose = async (key: ChoiceKey) => {
switch (key) { switch (key) {
case SyncSaveChoice.ToServer: { case SyncSaveChoice.ToServer: {
@ -393,6 +409,7 @@ export const SyncSave = defineComponent<MainSettingsProps>(props => {
choices={choices} choices={choices}
onChoose={choose} onChoose={choose}
interval={8} interval={8}
scope={scope}
/> />
); );
}, mainSettingsProps); }, mainSettingsProps);
@ -404,6 +421,9 @@ export const SyncSaveSelect = defineComponent<MainSettingsProps>(props => {
[SyncSaveChoice.Back, '返回上一级'] [SyncSaveChoice.Back, '返回上一级']
]; ];
const [key, scope] = useKey();
key.realize('exit', () => props.controller.close(props.instance));
const choose = async (key: ChoiceKey) => { const choose = async (key: ChoiceKey) => {
switch (key) { switch (key) {
case SyncSaveChoice.AllSaves: { case SyncSaveChoice.AllSaves: {
@ -446,6 +466,7 @@ export const SyncSaveSelect = defineComponent<MainSettingsProps>(props => {
choices={choices} choices={choices}
onChoose={choose} onChoose={choose}
interval={8} interval={8}
scope={scope}
/> />
); );
}, mainSettingsProps); }, mainSettingsProps);
@ -457,6 +478,9 @@ export const DownloadSaveSelect = defineComponent<MainSettingsProps>(props => {
[SyncSaveChoice.Back, '返回上一级'] [SyncSaveChoice.Back, '返回上一级']
]; ];
const [key, scope] = useKey();
key.realize('exit', () => props.controller.close(props.instance));
const choose = async (key: ChoiceKey) => { const choose = async (key: ChoiceKey) => {
switch (key) { switch (key) {
case SyncSaveChoice.AllSaves: { case SyncSaveChoice.AllSaves: {
@ -515,6 +539,7 @@ export const DownloadSaveSelect = defineComponent<MainSettingsProps>(props => {
choices={choices} choices={choices}
onChoose={choose} onChoose={choose}
interval={8} interval={8}
scope={scope}
/> />
); );
}, mainSettingsProps); }, mainSettingsProps);
@ -526,6 +551,9 @@ export const ClearSaveSelect = defineComponent<MainSettingsProps>(props => {
[SyncSaveChoice.Back, '返回上一级'] [SyncSaveChoice.Back, '返回上一级']
]; ];
const [key, scope] = useKey();
key.realize('exit', () => props.controller.close(props.instance));
const choose = async (key: ChoiceKey) => { const choose = async (key: ChoiceKey) => {
switch (key) { switch (key) {
case SyncSaveChoice.AllSaves: { case SyncSaveChoice.AllSaves: {
@ -624,6 +652,7 @@ export const ClearSaveSelect = defineComponent<MainSettingsProps>(props => {
choices={choices} choices={choices}
onChoose={choose} onChoose={choose}
interval={8} interval={8}
scope={scope}
/> />
); );
}, mainSettingsProps); }, mainSettingsProps);

View File

@ -73,18 +73,23 @@ type KeyUsing = [Hotkey, symbol];
/** /**
* *
* @param noScope * @param noScope
* @param scope `noScope` `true`
*/ */
export function useKey(noScope: boolean = false): KeyUsing { export function useKey(noScope: boolean = false, scope?: symbol): KeyUsing {
if (noScope) { if (noScope) {
return [gameKey, gameKey.scope]; return [gameKey, gameKey.scope];
} else { } else {
const sym = Symbol(); const sym = scope ?? Symbol();
if (sym === gameKey.scope) {
return [gameKey, gameKey.scope];
} else {
gameKey.use(sym); gameKey.use(sym);
onUnmounted(() => { onUnmounted(() => {
gameKey.dispose(); gameKey.dispose();
}); });
return [gameKey, sym]; return [gameKey, sym];
} }
}
} }
export interface ITransitionedController<T> { export interface ITransitionedController<T> {

View File

@ -34,9 +34,8 @@ export function deleteWith<T>(arr: T[], ele: T): T[] {
export function spliceBy<T>(arr: T[], from: T): T[] { export function spliceBy<T>(arr: T[], from: T): T[] {
const index = arr.indexOf(from); const index = arr.indexOf(from);
if (index === -1) return arr; if (index === -1) return [];
arr.splice(index); return arr.splice(index);
return arr;
} }
/** /**

View File

@ -179,7 +179,8 @@ export class Hotkey extends EventEmitter<HotkeyEvent> {
* @param symbol symbol * @param symbol symbol
*/ */
use(symbol: symbol): void { use(symbol: symbol): void {
spliceBy(this.scopeStack, symbol); if (symbol === this.scope) return;
this.dispose(symbol);
this.scopeStack.push(symbol); this.scopeStack.push(symbol);
this.scope = symbol; this.scope = symbol;
this.conditionMap.set(symbol, () => true); this.conditionMap.set(symbol, () => true);
@ -190,10 +191,12 @@ export class Hotkey extends EventEmitter<HotkeyEvent> {
* @param symbol symbol * @param symbol symbol
*/ */
dispose(symbol: symbol = this.scopeStack.at(-1) ?? Symbol()): void { dispose(symbol: symbol = this.scopeStack.at(-1) ?? Symbol()): void {
const disposed = spliceBy(this.scopeStack, symbol);
disposed.forEach(v => {
for (const key of Object.values(this.data)) { for (const key of Object.values(this.data)) {
key.emits.delete(symbol); key.emits.delete(v);
} }
spliceBy(this.scopeStack, symbol); });
this.scope = this.scopeStack.at(-1) ?? Symbol(); this.scope = this.scopeStack.at(-1) ?? Symbol();
} }