diff --git a/packages-user/client-modules/src/render/components/choices.tsx b/packages-user/client-modules/src/render/components/choices.tsx index 020f8d9..b3d1a03 100644 --- a/packages-user/client-modules/src/render/components/choices.tsx +++ b/packages-user/client-modules/src/render/components/choices.tsx @@ -33,6 +33,8 @@ export interface ConfirmBoxProps extends DefaultProps, TextContentProps { color?: CanvasStyle; /** 对话框边框颜色,当未设置 winskin 时生效 */ border?: CanvasStyle; + /** 按键作用域,如果需要同作用域按键,那么需要传入 */ + scope?: symbol; } export type ConfirmBoxEmits = { @@ -53,7 +55,8 @@ const confirmBoxProps = { 'winskin', 'defaultYes', 'color', - 'border' + 'border', + 'scope' ], emits: ['no', 'yes'] } satisfies SetupComponentOptions< @@ -143,7 +146,7 @@ export const ConfirmBox = defineComponent< noSize.value = [width, height]; }; - const [key] = useKey(); + const [key] = useKey(false, props.scope); key.realize('confirm', () => { if (selected.value) emit('yes'); else emit('no'); @@ -242,6 +245,8 @@ export interface ChoicesProps extends DefaultProps, TextContentProps { interval?: number; /** 默认选中的选项索引 */ selected?: number; + /** 按键作用域,如果需要同作用域按键,那么需要传入,例如系统设置 UI */ + scope?: symbol; } export type ChoicesEmits = { @@ -265,7 +270,8 @@ const choicesProps = { 'titleFill', 'pad', 'interval', - 'selected' + 'selected', + 'scope' ], emits: ['choose'] } satisfies SetupComponentOptions< @@ -464,7 +470,7 @@ export const Choices = defineComponent< selected.value = 0; }; - const [key] = useKey(); + const [key] = useKey(false, props.scope); key.realize('moveUp', () => { if (selected.value === 0) { if (pageCom.value?.now() !== 0) { diff --git a/packages-user/client-modules/src/render/ui/settings.tsx b/packages-user/client-modules/src/render/ui/settings.tsx index 12e58b3..f455343 100644 --- a/packages-user/client-modules/src/render/ui/settings.tsx +++ b/packages-user/client-modules/src/render/ui/settings.tsx @@ -25,6 +25,7 @@ import { saveWithExist } from './save'; import { compressToBase64 } from 'lz-string'; import { ViewMapUI } from './viewmap'; import { CENTER_LOC, FULL_LOC, MAIN_HEIGHT, POP_BOX_WIDTH } from '../shared'; +import { useKey } from '../use'; export interface MainSettingsProps extends Partial, @@ -62,6 +63,9 @@ export const MainSettings = defineComponent(props => { [MainChoice.Back, '返回游戏'] ]; + const [key, scope] = useKey(); + key.realize('exit', () => props.controller.close(props.instance)); + const choose = async (key: ChoiceKey) => { switch (key) { case MainChoice.SystemSetting: { @@ -125,6 +129,7 @@ export const MainSettings = defineComponent(props => { onChoose={choose} maxHeight={MAIN_HEIGHT - 64} interval={8} + scope={scope} /> ); }, mainSettingsProps); @@ -150,6 +155,9 @@ export const ReplaySettings = defineComponent(props => { [ReplayChoice.Back, '返回游戏'] ]; + const [key, scope] = useKey(); + key.realize('exit', () => props.controller.close(props.instance)); + const choose = async (key: ChoiceKey) => { switch (key) { case ReplayChoice.Start: { @@ -237,6 +245,7 @@ export const ReplaySettings = defineComponent(props => { width={POP_BOX_WIDTH} onChoose={choose} interval={8} + scope={scope} /> ); }, mainSettingsProps); @@ -260,6 +269,9 @@ export const GameInfo = defineComponent(props => { [GameInfoChoice.Back, '返回主菜单'] ]; + const [key, scope] = useKey(); + key.realize('exit', () => props.controller.close(props.instance)); + const choose = async (key: ChoiceKey) => { switch (key) { case GameInfoChoice.Statistics: { @@ -324,6 +336,7 @@ export const GameInfo = defineComponent(props => { width={POP_BOX_WIDTH} onChoose={choose} interval={8} + scope={scope} /> ); }, mainSettingsProps); @@ -351,6 +364,9 @@ export const SyncSave = defineComponent(props => { [SyncSaveChoice.Back, '返回上一级'] ]; + const [key, scope] = useKey(); + key.realize('exit', () => props.controller.close(props.instance)); + const choose = async (key: ChoiceKey) => { switch (key) { case SyncSaveChoice.ToServer: { @@ -393,6 +409,7 @@ export const SyncSave = defineComponent(props => { choices={choices} onChoose={choose} interval={8} + scope={scope} /> ); }, mainSettingsProps); @@ -404,6 +421,9 @@ export const SyncSaveSelect = defineComponent(props => { [SyncSaveChoice.Back, '返回上一级'] ]; + const [key, scope] = useKey(); + key.realize('exit', () => props.controller.close(props.instance)); + const choose = async (key: ChoiceKey) => { switch (key) { case SyncSaveChoice.AllSaves: { @@ -446,6 +466,7 @@ export const SyncSaveSelect = defineComponent(props => { choices={choices} onChoose={choose} interval={8} + scope={scope} /> ); }, mainSettingsProps); @@ -457,6 +478,9 @@ export const DownloadSaveSelect = defineComponent(props => { [SyncSaveChoice.Back, '返回上一级'] ]; + const [key, scope] = useKey(); + key.realize('exit', () => props.controller.close(props.instance)); + const choose = async (key: ChoiceKey) => { switch (key) { case SyncSaveChoice.AllSaves: { @@ -515,6 +539,7 @@ export const DownloadSaveSelect = defineComponent(props => { choices={choices} onChoose={choose} interval={8} + scope={scope} /> ); }, mainSettingsProps); @@ -526,6 +551,9 @@ export const ClearSaveSelect = defineComponent(props => { [SyncSaveChoice.Back, '返回上一级'] ]; + const [key, scope] = useKey(); + key.realize('exit', () => props.controller.close(props.instance)); + const choose = async (key: ChoiceKey) => { switch (key) { case SyncSaveChoice.AllSaves: { @@ -624,6 +652,7 @@ export const ClearSaveSelect = defineComponent(props => { choices={choices} onChoose={choose} interval={8} + scope={scope} /> ); }, mainSettingsProps); diff --git a/packages-user/client-modules/src/render/use.ts b/packages-user/client-modules/src/render/use.ts index 7df7c8b..2ff7eee 100644 --- a/packages-user/client-modules/src/render/use.ts +++ b/packages-user/client-modules/src/render/use.ts @@ -73,17 +73,22 @@ type KeyUsing = [Hotkey, symbol]; /** * 在组件中定义按键操作 * @param noScope 是否不创建新作用域 + * @param scope 指定作用域,如果 `noScope` 为 `true`,则此项无效 */ -export function useKey(noScope: boolean = false): KeyUsing { +export function useKey(noScope: boolean = false, scope?: symbol): KeyUsing { if (noScope) { return [gameKey, gameKey.scope]; } else { - const sym = Symbol(); - gameKey.use(sym); - onUnmounted(() => { - gameKey.dispose(); - }); - return [gameKey, sym]; + const sym = scope ?? Symbol(); + if (sym === gameKey.scope) { + return [gameKey, gameKey.scope]; + } else { + gameKey.use(sym); + onUnmounted(() => { + gameKey.dispose(); + }); + return [gameKey, sym]; + } } } diff --git a/packages/legacy-common/src/utils.ts b/packages/legacy-common/src/utils.ts index 6582837..7e8a0a3 100644 --- a/packages/legacy-common/src/utils.ts +++ b/packages/legacy-common/src/utils.ts @@ -34,9 +34,8 @@ export function deleteWith(arr: T[], ele: T): T[] { export function spliceBy(arr: T[], from: T): T[] { const index = arr.indexOf(from); - if (index === -1) return arr; - arr.splice(index); - return arr; + if (index === -1) return []; + return arr.splice(index); } /** diff --git a/packages/system-action/src/hotkey.ts b/packages/system-action/src/hotkey.ts index 376e96c..c1da6d5 100644 --- a/packages/system-action/src/hotkey.ts +++ b/packages/system-action/src/hotkey.ts @@ -179,7 +179,8 @@ export class Hotkey extends EventEmitter { * @param symbol 当前作用域的symbol */ use(symbol: symbol): void { - spliceBy(this.scopeStack, symbol); + if (symbol === this.scope) return; + this.dispose(symbol); this.scopeStack.push(symbol); this.scope = symbol; this.conditionMap.set(symbol, () => true); @@ -190,10 +191,12 @@ export class Hotkey extends EventEmitter { * @param symbol 要释放的作用域的symbol */ dispose(symbol: symbol = this.scopeStack.at(-1) ?? Symbol()): void { - for (const key of Object.values(this.data)) { - key.emits.delete(symbol); - } - spliceBy(this.scopeStack, symbol); + const disposed = spliceBy(this.scopeStack, symbol); + disposed.forEach(v => { + for (const key of Object.values(this.data)) { + key.emits.delete(v); + } + }); this.scope = this.scopeStack.at(-1) ?? Symbol(); }