Files
msime-windows/engine/contracts/input_state_shortcuts.h
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

106 lines
4.1 KiB
C++

#pragma once
#include "key_chord.h"
#include <array>
#include <cstddef>
namespace FanyImeInputStateShortcuts
{
using FanyImeKeyChord::Binding;
enum class Action : unsigned
{
EnglishCandidates = 1,
FullWidth = 2,
Punctuation = 3
};
struct Definition
{
Action action;
std::string_view key, label, defaultBinding, editor;
};
inline constexpr std::array<Definition, 3> Definitions = {
{{Action::EnglishCandidates, "toggle_english_candidates", "英文候选", "Ctrl+Shift+KeyE", "englishCandidatesHotkey"},
{Action::FullWidth, "toggle_full_width", "全角 / 半角", "Ctrl+Shift+Space", "fullWidthHotkey"},
{Action::Punctuation, "toggle_punctuation", "中英文标点", "Ctrl+Period", "punctuationHotkey"}}};
using Values = std::array<std::string_view, Definitions.size()>;
inline constexpr Values Defaults()
{
Values values{};
for (std::size_t i = 0; i < values.size(); ++i)
values[i] = Definitions[i].defaultBinding;
return values;
}
struct Rule
{
std::string_view binding, label;
};
inline constexpr Rule Reserved[] = {
{"Ctrl+Shift+KeyF", "简繁切换"}, {"Ctrl+F9", "语音输入"}, {"Ctrl+Shift+F9", "语音输入"}};
inline constexpr Rule Warnings[] = {{"Ctrl+KeyA", "全选"},
{"Ctrl+KeyC", "复制"},
{"Ctrl+KeyV", "粘贴"},
{"Ctrl+KeyX", "剪切"},
{"Ctrl+KeyZ", "撤销"},
{"Ctrl+KeyY", "重做"},
{"Ctrl+KeyS", "保存"},
{"Ctrl+KeyF", "查找"},
{"Ctrl+KeyH", "替换"},
{"Ctrl+KeyP", "打印"},
{"Ctrl+KeyO", "打开"},
{"Ctrl+KeyN", "新建"},
{"Ctrl+KeyW", "关闭标签页或文档"},
{"Ctrl+F4", "关闭文档"},
{"Ctrl+KeyT", "新建标签页"},
{"Ctrl+KeyL", "定位地址栏"},
{"Ctrl+KeyR", "刷新"},
{"Ctrl+KeyB", "加粗"},
{"Ctrl+KeyI", "斜体"},
{"Ctrl+KeyU", "下划线"},
{"Ctrl+Shift+KeyV", "粘贴为纯文本"},
{"Ctrl+Shift+KeyZ", "重做"},
{"Ctrl+Shift+KeyS", "另存为"},
{"Ctrl+Shift+KeyT", "重新打开关闭的标签页"},
{"Ctrl+Space", "Windows 输入法切换"}};
inline std::string_view Validate(std::size_t index, std::string_view value, const Values &values)
{
Binding parsed;
if (!FanyImeKeyChord::Parse(value, parsed))
return "快捷键格式无效";
if (value.empty())
return {};
for (const auto &rule : Reserved)
if (value == rule.binding)
return rule.label;
for (std::size_t i = 0; i < values.size(); ++i)
if (i != index && value == values[i])
return Definitions[i].label;
return {};
}
struct Snapshot
{
std::array<Binding, Definitions.size()> bindings{};
constexpr Snapshot()
{
for (std::size_t i = 0; i < bindings.size(); ++i)
FanyImeKeyChord::Parse(Definitions[i].defaultBinding, bindings[i]);
}
Action Match(unsigned code, unsigned modifiers) const
{
for (std::size_t i = 0; i < bindings.size(); ++i)
if (bindings[i].Matches(code, modifiers))
return Definitions[i].action;
return static_cast<Action>(0);
}
};
inline Snapshot Resolve(const Values &values)
{
Snapshot result;
for (std::size_t i = 0; i < values.size(); ++i)
{
result.bindings[i] = {};
if (Validate(i, values[i], values).empty())
FanyImeKeyChord::Parse(values[i], result.bindings[i]);
}
return result;
}
} // namespace FanyImeInputStateShortcuts