Files
chr331andfanlusky 1c3bd7cb83 feat(server,ui-html,windows): 支持自定义“输入状态切换”中的快捷键 (#985)
* fix(windows): 支持标点切换快捷键改绑与取消绑定

* fix(shortcut): 对齐虚拟键并处理语音与窗口快捷键冲突

* fix(ui-html): 绑定标点快捷键前提示应用快捷键占用

* fix(ui-html): 仅对已知快捷键冲突提示确认

* feat(shortcuts): 支持自定义输入状态切换快捷键

* fix: keep shortcut editor buttons on one row

* fix(shortcuts): 限定输入状态快捷键为 Ctrl 组合

* fix: clarify shortcut override and punctuation lock behavior

* perf(server,windows): 提前筛选输入状态快捷键

* fix(windows,server,ui-html): 修正输入状态快捷键的兼容与保存问题

- windows: 未协商 InputStateShortcuts 时回退到旧的 Ctrl+Shift+E KeyEvent,
  新 DLL 配旧 Server 时英文候选快捷键不再失效
- windows: 全角快捷键与标点快捷键一样可在待处理按键后排队,不再漏给应用
- windows: Server 不可用时排队的英文候选切换按 FUNCTION_CANCEL 离线回放,
  不再丢弃其后的按键
- server: 非字符串配置值不再以 "!invalid" 作为 binding 发给设置页
- server: 仅三个输入状态快捷键路径强制回传配置快照;去掉 worker 中与
  reader 重复的协商检查;抽出 InputStateShortcutViews 去除重复转换
- ui-html: 保存后 5 秒未收到快照则恢复编辑器并提示重试

---------

Co-authored-by: fanlusky <1730976608@qq.com>
2026-10-16 03:31:19 +08:00

75 lines
2.7 KiB
C++

#pragma once
#include <string_view>
namespace FanyImeKeyChord
{
constexpr unsigned Shift = 1, Control = 2, Alt = 4, Win = 8;
struct Binding
{
unsigned key = 0, modifiers = 0;
constexpr bool Matches(unsigned code, unsigned pressed) const
{
return key != 0 && key == code && modifiers == pressed;
}
};
struct KeyName
{
std::string_view name;
unsigned key;
std::string_view label;
};
constexpr KeyName NamedKeys[] = {{"Space", 0x20, "Space"}, {"Semicolon", 0xba, ";"}, {"Equal", 0xbb, "="},
{"Comma", 0xbc, ","}, {"Minus", 0xbd, "-"}, {"Period", 0xbe, "."},
{"Slash", 0xbf, "/"}, {"Backquote", 0xc0, "`"}, {"BracketLeft", 0xdb, "["},
{"Backslash", 0xdc, "\\"}, {"BracketRight", 0xdd, "]"}, {"Quote", 0xde, "'"}};
inline constexpr unsigned KeyCode(std::string_view code)
{
if (code.size() == 4 && code.substr(0, 3) == "Key" && code[3] >= 'A' && code[3] <= 'Z')
return code[3];
if (code.size() == 6 && code.substr(0, 5) == "Digit" && code[5] >= '0' && code[5] <= '9')
return code[5];
if (code.size() == 2 && code[0] == 'F' && code[1] >= '1' && code[1] <= '9')
return 0x70 + code[1] - '1';
if (code == "F10" || code == "F11" || code == "F12")
return 0x79 + code[2] - '0';
for (const auto &entry : NamedKeys)
if (entry.name == code)
return entry.key;
return 0;
}
// Canonical Ctrl [+Shift] + one Windows virtual key. Empty disables.
inline constexpr bool Parse(std::string_view value, Binding &result)
{
result = {};
if (value.empty())
return true;
if (value.substr(0, 5) != "Ctrl+")
return false;
unsigned modifiers = Control;
value.remove_prefix(5);
if (value.substr(0, 6) == "Shift+")
{
modifiers |= Shift;
value.remove_prefix(6);
}
const unsigned key = KeyCode(value);
if (!key)
return false;
result = {key, modifiers};
return true;
}
// Stands in for a configured value that is not a string; Parse rejects it.
constexpr std::string_view InvalidValue = "!invalid";
// Used by the DLL's existing small TOML reader; invalid types stay invalid.
inline std::string_view TomlString(std::string_view value)
{
const auto begin = value.find_first_not_of(" \t\r");
if (begin == std::string_view::npos)
return InvalidValue;
value = value.substr(begin, value.find_last_not_of(" \t\r") - begin + 1);
if (value.size() >= 2 && (value.front() == '"' || value.front() == '\'') && value.back() == value.front())
return value.substr(1, value.size() - 2);
return InvalidValue;
}
} // namespace FanyImeKeyChord