Skip to content

Commit b4f06f8

Browse files
committed
fix: 修复序列快捷键在左下角快捷键提示中显示不全的问题
1 parent b2f83d2 commit b4f06f8

5 files changed

Lines changed: 228 additions & 197 deletions

File tree

app/src/components/ui/key-bind.tsx

Lines changed: 32 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Button } from "@/components/ui/button";
2-
import { formatEmacsKey, parseEmacsKey } from "@/utils/emacs";
3-
import { isLinux, isMac, isWindows } from "@/utils/platform";
2+
import { formatEmacsKey } from "@/utils/emacs";
3+
import { formatKeyBindSequence, formatSigalKeyForDisplay, getModifierDisplayTexts } from "@/utils/keyDisplay";
44
import { Check, Delete } from "lucide-react";
55
import { useCallback, useState } from "react";
66
import { useTranslation } from "react-i18next";
@@ -23,14 +23,14 @@ export default function KeyBind({
2323
const handleKeyDown = useCallback((event: KeyboardEvent) => {
2424
event.preventDefault();
2525
if (["Control", "Alt", "Shift", "Meta"].includes(event.key)) return;
26-
setValue((prev) => prev + " " + formatEmacsKey(event));
26+
setValue((prev) => `${prev} ${formatEmacsKey(event)}`);
2727
}, []);
2828

2929
const handleMouseDown = useCallback((event: MouseEvent) => {
3030
event.preventDefault();
3131
event.stopPropagation();
3232
if (event.button !== 0) {
33-
setValue((prev) => prev + " " + formatEmacsKey(event));
33+
setValue((prev) => `${prev} ${formatEmacsKey(event)}`);
3434
}
3535
}, []);
3636

@@ -42,7 +42,7 @@ export default function KeyBind({
4242
const handleWheel = useCallback((event: WheelEvent) => {
4343
event.preventDefault();
4444
event.stopPropagation();
45-
setValue((prev) => prev + " " + formatEmacsKey(event));
45+
setValue((prev) => `${prev} ${formatEmacsKey(event)}`);
4646
}, []);
4747

4848
const startInput = useCallback(() => {
@@ -66,7 +66,18 @@ export default function KeyBind({
6666
return (
6767
<>
6868
<Button onClick={startInput} variant={choosing ? "outline" : "default"} className="gap-0">
69-
{value ? parseEmacsKey(value.trim()).map((key, index) => <RenderKey key={index} data={key} />) : t("none")}
69+
{value
70+
? formatKeyBindSequence(value.trim()).map((item, index) => (
71+
<span key={index} className="not-first:before:content-[',_'] flex gap-1 font-bold">
72+
{item.modifiers.map((modifier, modIndex) => (
73+
<span className="bg-card text-foreground rounded-sm px-1 font-semibold" key={modIndex}>
74+
{modifier}
75+
</span>
76+
))}
77+
{item.key}
78+
</span>
79+
))
80+
: t("none")}
7081
</Button>
7182
{choosing && (
7283
<>
@@ -87,25 +98,27 @@ export default function KeyBind({
8798
);
8899
}
89100

90-
export function RenderKey({ data }: { data: ReturnType<typeof parseEmacsKey>[number] }) {
91-
let keyShow = data.key;
92-
if (data.key === "arrowup") {
93-
keyShow = "↑";
94-
} else if (data.key === "arrowdown") {
95-
keyShow = "↓";
96-
} else if (data.key === "arrowleft") {
97-
keyShow = "←";
98-
} else if (data.key === "arrowright") {
99-
keyShow = "→";
100-
}
101+
/**
102+
* @deprecated 使用 @/utils/keyDisplay 中的函数替代
103+
*/
104+
export function RenderKey({ data }: { data: ReturnType<typeof import("@/utils/emacs").parseEmacsKey>[number] }) {
105+
const modifiers = getModifierDisplayTexts(data);
106+
const keyShow = formatSigalKeyForDisplay(data.key);
101107
return (
102108
<span className="not-first:before:content-[',_'] flex gap-1 font-bold">
103-
<Modifiers modifiers={data} />
109+
{modifiers.map((modifier, index) => (
110+
<span className="bg-card text-foreground rounded-sm px-1 font-semibold" key={index}>
111+
{modifier}
112+
</span>
113+
))}
104114
{data.key.startsWith("<") ? <MouseButton key_={data.key} /> : keyShow}
105115
</span>
106116
);
107117
}
108118

119+
/**
120+
* @deprecated 使用 @/utils/keyDisplay 中的 getModifierDisplayTexts 替代
121+
*/
109122
export function Modifiers({
110123
modifiers,
111124
}: {
@@ -116,52 +129,7 @@ export function Modifiers({
116129
meta: boolean;
117130
};
118131
}) {
119-
const mods = [];
120-
121-
if (modifiers.control) {
122-
if (isMac) {
123-
mods.push("⌃");
124-
} else if (isWindows) {
125-
mods.push("Ctrl");
126-
} else if (isLinux) {
127-
mods.push("Ctrl");
128-
} else {
129-
mods.push("control");
130-
}
131-
}
132-
if (modifiers.alt) {
133-
if (isMac) {
134-
mods.push("⌥");
135-
} else if (isWindows) {
136-
mods.push("Alt");
137-
} else if (isLinux) {
138-
mods.push("Alt");
139-
} else {
140-
mods.push("alt");
141-
}
142-
}
143-
if (modifiers.shift) {
144-
if (isMac) {
145-
mods.push("⇧");
146-
} else if (isWindows) {
147-
mods.push("Shift");
148-
} else if (isLinux) {
149-
mods.push("Shift");
150-
} else {
151-
mods.push("shift");
152-
}
153-
}
154-
if (modifiers.meta) {
155-
if (isMac) {
156-
mods.push("⌘");
157-
} else if (isWindows) {
158-
mods.push("❖");
159-
} else if (isLinux) {
160-
mods.push("Super");
161-
} else {
162-
mods.push("meta");
163-
}
164-
}
132+
const mods = getModifierDisplayTexts(modifiers);
165133
return mods.map((modifier, index) => (
166134
<span className="bg-card text-foreground rounded-sm px-1 font-semibold" key={index}>
167135
{modifier}

app/src/core/service/controlService/shortcutKeysEngine/KeyBindHintEngine.tsx

Lines changed: 18 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { Project, service } from "@/core/Project";
22
import { allKeyBinds } from "./shortcutKeysRegister";
3-
import { parseSingleEmacsKey } from "@/utils/emacs";
3+
import { parseEmacsKey, parseSingleEmacsKey } from "@/utils/emacs";
44
import { isMac } from "@/utils/platform";
55
import { Vector } from "@graphif/data-structures";
6-
import { Rectangle } from "@graphif/shapes";
76
import { getTextSize } from "@/utils/font";
8-
import { KeyBindsUI } from "./KeyBindsUI";
7+
import { KeyBindsUI, type UIKeyBind } from "./KeyBindsUI";
8+
import { formatKeyBindSequenceToString } from "@/utils/keyDisplay";
99

1010
/**
1111
* 快捷键提示引擎
@@ -101,13 +101,14 @@ export class KeyBindHintEngine {
101101
private isKeyBindMatchModifier(key: string, modifierCombo: string): boolean {
102102
// 解析快捷键
103103
const parsed = parseSingleEmacsKey(key);
104+
const parsedList = parseEmacsKey(key);
104105

105106
// 构建快捷键的修饰键组合(存储格式)
106107
const keyModifiers: string[] = [];
107-
if (parsed.control) keyModifiers.push("C");
108-
if (parsed.alt) keyModifiers.push("A");
109-
if (parsed.shift) keyModifiers.push("S");
110-
if (parsed.meta) keyModifiers.push("M");
108+
if (parsedList.some((p) => p.control)) keyModifiers.push("C");
109+
if (parsedList.some((p) => p.alt)) keyModifiers.push("A");
110+
if (parsedList.some((p) => p.shift)) keyModifiers.push("S");
111+
if (parsedList.some((p) => p.meta)) keyModifiers.push("M");
111112

112113
const keyModifierCombo = keyModifiers.join("-");
113114

@@ -125,6 +126,7 @@ export class KeyBindHintEngine {
125126

126127
/**
127128
* 获取所有匹配的快捷键
129+
* O(N)
128130
*/
129131
private getMatchingKeyBinds(modifierCombo: string): Array<{
130132
id: string;
@@ -147,16 +149,15 @@ export class KeyBindHintEngine {
147149
if (keyBind.isGlobal) continue;
148150

149151
// 获取当前快捷键的配置
150-
const uiKeyBind = allUIKeyBinds.find((kb: any) => kb.id === keyBind.id);
152+
const uiKeyBind = allUIKeyBinds.find((kb: UIKeyBind) => kb.id === keyBind.id);
151153
if (uiKeyBind && !uiKeyBind.isEnabled) continue;
152154

153155
const key = uiKeyBind?.key || keyBind.defaultKey;
154156

155157
// 检查是否匹配当前修饰键组合
156158
if (this.isKeyBindMatchModifier(key, modifierCombo)) {
157-
// 解析快捷键获取显示用的按键
158-
const parsed = parseSingleEmacsKey(key);
159-
const displayKey = this.formatKeyForDisplay(parsed.key);
159+
// 使用复用的函数格式化按键显示
160+
const displayKey = formatKeyBindSequenceToString(key);
160161

161162
result.push({
162163
id: keyBind.id,
@@ -170,46 +171,10 @@ export class KeyBindHintEngine {
170171
return result;
171172
}
172173

173-
/**
174-
* 格式化按键显示
175-
*/
176-
private formatKeyForDisplay(key: string): string {
177-
// 特殊键映射
178-
const specialKeyMap: Record<string, string> = {
179-
arrowup: "↑",
180-
arrowdown: "↓",
181-
arrowleft: "←",
182-
arrowright: "→",
183-
enter: "↵",
184-
escape: "Esc",
185-
backspace: "⌫",
186-
delete: "Del",
187-
tab: "Tab",
188-
space: "Space",
189-
home: "Home",
190-
end: "End",
191-
pageup: "PgUp",
192-
pagedown: "PgDn",
193-
};
194-
195-
if (key in specialKeyMap) {
196-
return specialKeyMap[key];
197-
}
198-
199-
// 大写字母
200-
if (key.length === 1 && /[a-z]/.test(key)) {
201-
return key.toUpperCase();
202-
}
203-
204-
return key;
205-
}
206-
207174
/**
208175
* 获取快捷键标题
209176
*/
210177
private getKeyBindTitle(id: string): string {
211-
// 这里可以后续从翻译文件中获取
212-
// 暂时返回简化版的标题
213178
const titleMap: Record<string, string> = {
214179
saveFile: "保存文件",
215180
openFile: "打开文件",
@@ -331,60 +296,27 @@ export class KeyBindHintEngine {
331296
const lineHeight = 28;
332297
const startY = this.project.renderer.h - 140 - pageItems.length * lineHeight;
333298

334-
// 渲染背景
335-
const maxWidth = this.calculateMaxWidth(pageItems);
336-
const bgPadding = 8;
337-
const bgRect = new Rectangle(
338-
new Vector(margin - bgPadding, startY - bgPadding),
339-
new Vector(maxWidth + bgPadding * 2, pageItems.length * lineHeight + bgPadding * 2),
340-
);
341-
this.project.shapeRenderer.renderRect(
342-
bgRect,
343-
this.project.stageStyleManager.currentStyle.Background.toNewAlpha(0.9),
344-
this.project.stageStyleManager.currentStyle.StageObjectBorder.toNewAlpha(0.3),
345-
1,
346-
);
347-
348299
// 渲染每个快捷键提示
349300
for (let i = 0; i < pageItems.length; i++) {
350301
const item = pageItems[i];
351302
const y = startY + i * lineHeight;
352303

353-
// 渲染修饰键组合
354-
const modifierText = this.formatModifierCombo(this.currentModifierCombo);
355-
this.project.textRenderer.renderText(
356-
modifierText,
357-
new Vector(margin, y),
358-
14,
359-
this.project.stageStyleManager.currentStyle.StageObjectBorder,
360-
);
361-
362-
const modifierWidth = getTextSize(modifierText, 14).x;
363-
364-
// 渲染 + 号
365-
this.project.textRenderer.renderText(
366-
" + ",
367-
new Vector(margin + modifierWidth, y),
368-
14,
369-
this.project.stageStyleManager.currentStyle.effects.successShadow,
370-
);
371-
372-
const plusWidth = getTextSize(" + ", 14).x;
304+
// 使用复用的函数格式化修饰键组合
305+
let currentX = margin;
373306

374307
// 渲染按键
375308
this.project.textRenderer.renderText(
376309
item.displayKey,
377-
new Vector(margin + modifierWidth + plusWidth, y),
310+
new Vector(currentX, y),
378311
16,
379312
this.project.stageStyleManager.currentStyle.effects.flash,
380313
);
381-
382-
const keyWidth = getTextSize(item.displayKey, 16).x;
314+
currentX += getTextSize(item.displayKey, 16).x;
383315

384316
// 渲染标题
385317
this.project.textRenderer.renderText(
386318
item.title,
387-
new Vector(margin + modifierWidth + plusWidth + keyWidth + 15, y + 2),
319+
new Vector(currentX + 15, y + 2),
388320
12,
389321
this.project.stageStyleManager.currentStyle.DetailsDebugText,
390322
);
@@ -393,48 +325,12 @@ export class KeyBindHintEngine {
393325
// 渲染页码指示器
394326
if (totalPages > 1) {
395327
const pageIndicator = `${actualPage + 1}/${totalPages}`;
396-
const indicatorWidth = getTextSize(pageIndicator, 12).x;
397328
this.project.textRenderer.renderText(
398329
pageIndicator,
399-
new Vector(margin + maxWidth - indicatorWidth, startY - 20),
330+
new Vector(margin, startY - 20),
400331
12,
401332
this.project.stageStyleManager.currentStyle.DetailsDebugText,
402333
);
403334
}
404335
}
405-
406-
/**
407-
* 格式化修饰键组合显示
408-
*/
409-
private formatModifierCombo(combo: string): string {
410-
if (!combo) return "";
411-
412-
const parts = combo.split("-");
413-
const displayMap: Record<string, string> = {
414-
C: isMac ? "⌘" : "Ctrl",
415-
A: isMac ? "⌥" : "Alt",
416-
S: "⇧",
417-
M: isMac ? "⌃" : "Win",
418-
};
419-
420-
return parts.map((p) => displayMap[p] || p).join("");
421-
}
422-
423-
/**
424-
* 计算最大宽度
425-
*/
426-
private calculateMaxWidth(items: Array<{ displayKey: string; title: string }>): number {
427-
let maxWidth = 0;
428-
const modifierWidth = getTextSize(this.formatModifierCombo(this.currentModifierCombo), 14).x;
429-
const plusWidth = getTextSize(" + ", 14).x;
430-
431-
for (const item of items) {
432-
const keyWidth = getTextSize(item.displayKey, 16).x;
433-
const titleWidth = getTextSize(item.title, 12).x;
434-
const totalWidth = modifierWidth + plusWidth + keyWidth + 15 + titleWidth;
435-
maxWidth = Math.max(maxWidth, totalWidth);
436-
}
437-
438-
return maxWidth;
439-
}
440336
}

0 commit comments

Comments
 (0)