ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Flutter 键盘事件体系揭秘:gen_keycodes 代码生成工具与 52 位键码平面方案

Flutter 键盘事件体系揭秘:gen_keycodes 代码生成工具与 52 位键码平面方案 Flutter 键盘事件体系揭秘gen_keycodes 代码生成工具与 52 位键码平面方案【免费下载链接】flutterFlutter makes it easy and fast to build beautiful apps for mobile and beyond项目地址: https://gitcode.com/GitHub_Trending/flutter41/flutter本文深入讲解 Flutter 仓库中dev/tools/gen_keycodes代码生成工具的工作原理它如何把 Chromium、Android、GTK、Windows 等多平台的键盘头文件信息汇总成统一数据库进而生成框架层的LogicalKeyboardKey/PhysicalKeyboardKey定义文件与各平台引擎层的键码映射文件同时完整解析 Flutter 为键盘键设计 52 位不透明键码key ID、按“平面plane”划分命名空间的具体规则帮助读者掌握自定义平台移植时规范指定键码的方法。一、工具定位一个数据库驱动的跨平台键码生成器dev/tools/gen_keycodes目录中包含一个键码生成器能够生成LogicalKeyboardKey和PhysicalKeyboardKey两个核心类的 Dart 代码。它会向 Flutter 仓库的多个位置生成文件框架framework侧keyboard_key.g.dart包含所有逻辑键和物理键的定义与列表约 5600 行keyboard_maps.g.dart包含各平台专属的不可变映射表如 Android keycode 到LogicalKeyboardKey的映射供RawKeyboardAPI 使用约 3200 行。引擎engine侧为每个平台生成一个键映射文件并生成若干用于测试目的的文件包括key_codes.g.hembedder 测试工具KeyCodes.javaAndroid 测试工具各平台生成器输出的映射文件如 Android 的KeyboardMap.java、iOS/macOS 的KeyCodeMap.mm、Windows 的flutter_key_map.cc、Web 的key_map.dart等模板分别位于 data 目录的*.tmpl文件中。这两个生成文件头部都有显式注释明确禁止手工编辑// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT // This file is generated by dev/tools/gen_keycodes/bin/gen_keycodes.dart and // should not be edited directly. // // Edit the template dev/tools/gen_keycodes/data/keyboard_key.tmpl instead. // See dev/tools/gen_keycodes/README.md for more information.也就是说要新增或修改一个键正确的做法是修改数据文件data/*.json、data/*.inc和模板如 keyboard_key.tmpl、keyboard_maps.tmpl然后重新运行生成器。二、数据来源从在线头文件到本地 JSON 数据库生成器从多种来源采集信息包括在线代码仓库Chromium、AOSP、GLFW、GNOME GTK、WinSDK 等以及 data 子目录中的手工映射文件最终把这些信息汇总为两个大 JSON 数据库文件physical_key_data.g.json全部物理键的合并数据logical_key_data.g.json全部逻辑键的合并数据。这两个文件都被签入checked in仓库因此下一次生成可以直接以它们为数据源无需联网。从 bin/gen_keycodes.dart 的源码可以看到--collect模式下在线拉取的信息包括在线来源获取内容Chromiumdom_code_data.inc物理 HID 码、dom_key_data.incWeb 逻辑键Androidkeycodes.hkeycode 常量、Generic.kl通用键盘扫描码布局WindowsWinUser.hWin32 虚拟键Linux (GTK)gdkkeysyms.hkeyvalGLFWglfw3.h而本地data目录中的手工数据文件则承担了“人工校对与补充”的职责data/README.md 给出了完整的文件清单主要包括文件作用supplemental_hid_codes.inc在 Chromium HID 码列表之上的补充物理键可覆盖 Chromium 的对应条目supplemental_key_data.inc在 Chromium 键列表之上的补充逻辑键chromium_modifiers.json将 Web 修饰键的key映射到左右两侧变体的逻辑键名用于为左右修饰键分配独立值printable.jsonFlutter 键名到可打印字符的映射用作键标签keyLabelsynonyms.json伪键如代表任意 Shift 的 shift到其所代表键集合的映射layout_goals.json布局目标键列表平台键盘管理器应为其找到映射的键值为该目标是否强制mandatoryandroid_key_name_to_name.json逻辑键名到 Android keycode 常量名的映射ios_logical_to_physical.json / macos_logical_to_physical.json逻辑键到其物理键的映射用于从keyCode推导不能从characterIgnoringModifiers得到的逻辑键windows_logical_to_window_vk.json逻辑键名到 Win32 虚拟键名的映射windows_scancode_logical_map.json物理键到逻辑键的映射用于同一个keycode含 0对应多个键、只能靠扫描码区分的情况gtk_logical_name_mapping.json、gtk_modifier_bit_mapping.json、gtk_lock_bit_mapping.json、gtk_numpad_shift.jsonGTK 的 keyval 映射、修饰/锁定状态同步、NumLock 开/关下小键盘键的统一处理web_logical_location_mapping.json由 Webkeylocation组合区分同名不同位置逻辑键如左右 Shiftglfw_key_name_to_name.json、glfw_keyboard_map_cc.tmpl、fuchsia_keyboard_map_cc.tmplGLFW/Fuchsia 模板文档标注为暂不使用数据库本身的 JSON 结构也可以直接观察。物理键条目记录了键名与多平台扫描码例如Fn: { names: { name: Fn, chromium: Fn }, scanCodes: { android: [464], usb: 18, macos: 63 } }逻辑键条目则记录统一value、各平台宏名names与平台数值values例如Space键在 GTK/Windows/Android/Fuchsia/GLFW 上分别取65408/32/62/77309870124/32。这份“一源多端”的数据正是 keyboard_maps.g.dart 中各平台映射表的直接来源。三、运行工具离线生成与在线重建两种模式工具的入口是一个 bash 脚本 dev/tools/gen_keycodes/bin/gen_keycodes。该脚本做了两件关键的事跨平台地解析自身真实路径以定位仓库根的dart可执行文件然后以--enable-asserts参数启动 gen_keycodes.dart该 Dart 程序启动时会自检断言已启用否则拒绝运行——因为生成逻辑依赖断言保证数据正确性。基于现有数据库生成无需联网/PATH/TO/ROOT/dev/tools/gen_keycodes/bin/gen_keycodes即直接读取签入的physical_key_data.g.json与logical_key_data.g.json重新生成所有输出文件。重建数据库后生成需要联网/PATH/TO/ROOT/dev/tools/gen_keycodes/bin/gen_keycodes --collect该模式会重新从 Chromium、Android、GTK、Windows、GLFW 等在线源拉取并解析头文件与本地supplemental_*.inc等补充数据合并然后写回physical_key_data.g.json和logical_key_data.g.json。这两个文件应当随提交一起签入仓库。其余选项可通过--help查看。从 bin/gen_keycodes.dart 的参数解析代码可确认完整选项列表选项默认值说明--physical-datadata/physical_key_data.g.json物理键数据文件路径--collect时写入否则读取--logical-datadata/logical_key_data.g.json逻辑键数据文件路径语义同上--codepackages/flutter/lib/src/services/keyboard_key.g.dart输出keyboard_key.g.dart的路径--mapspackages/flutter/lib/src/services/keyboard_maps.g.dart输出keyboard_maps.g.dart的路径--collect—设置后在线采集并解析各源头的头文件而非读取预解析数据并用新数据更新两个.g.json文件--help—打印帮助信息四、生成机制模板占位符替换与生成器分层从源码结构看工具的生成机制基于模板替换。base_code_gen.dart 定义了核心流程String _injectDictionary(String template, MapString, String dictionary) { var result template; for (final String key in dictionary.keys) { result result.replaceAll($key, dictionary[key] ?? $key); } return result; }即BaseCodeGenerator在模板文件中查找形如TOKEN的占位符用mappings()返回的映射表逐项替换。子类只需实现templatePath模板路径与mappings()占位符内容。在抽象类之上还有一层PlatformCodeGenerator额外要求实现outputPath(platform)以指定每个平台的输出位置。bin/gen_keycodes.dart 的主流程展示了生成器的组织方式先用KeyboardKeysCodeGenerator生成keyboard_key.g.dart键定义再用KeyboardMapsCodeGenerator生成keyboard_maps.g.dart映射表为引擎生成key_codes.g.h与KeyCodes.java两个测试工具文件通过Future.wait并发执行六个平台生成器AndroidCodeGenerator、MacOSCodeGenerator、IOSCodeGenerator、WindowsCodeGenerator、GtkCodeGenerator、WebCodeGenerator各自读取对应的平台 JSON 数据如windows_scancode_logical_map.json、gtk_modifier_bit_mapping.json、web_logical_location_mapping.json后输出到引擎树。五、Key ID Scheme52 位键码的平面命名空间方案这是本工具文档中最具移植参考价值的一部分。为了让键拥有唯一 IDFlutter 采用一种不需要自己“铸造”新编码的分配方案同时适用于逻辑键和物理键。这些编码对用户而言是不透明的不应被拆解求义因为编码方案随时可能变化而且通过 API 获取键的含义通常更可靠、更正确。但如果你要把 Flutter 移植到新平台应按以下规则指定键码。键码是一个 52 位整数这是 JavaScript 数值精度的限制所致JS 安全整数上限为 2⁵³−1除去 32 位值域后平面号最多占 20 位。整个命名空间被划分为 32 位的平面planeID 的高 20 位表示平面 ID低 32 位表示平面内的值。例如平面0x1对应范围0x1 0000 0000至0x1 FFFF FFFF。每个平面自行管理其范围内值的分配。各平面的规划如下平面 0x00Unicode 平面。逻辑键中包含按下时会产生 Unicode 字符的键包括死键但不包括功能键、Shift 这类键。值定义为对应字符的 Unicode 码点取小写、无修饰键状态下的值。例如 Key A 为0x61、Digit 1 为0x31、Colon 为0x3A、Key Ù 为0xD9。“Colon”键指无修饰符即可输出:的键见于法语布局美式布局上输出:的键是 Semicolon 键。物理键中该平面包含来自 USB HID usages 的键。平面 0x01不可打印平面。包含由 Chromium 键列表定义、且不产生 Unicode 字符的逻辑键值定义为 Chromium 键列表中的宏值。例如 CapsLock 为0x105、ArrowUp 为0x304、F1 为0x801、Hiragana 为0x716、TVPower 为0xD4B。Chromium 键列表中的部分键在 Flutter 的这个平面里并不存在最典型的就是修饰键如 Shift——它们被放在了下面的 Flutter 平面。平面 0x02Flutter 平面。包含由 Flutter 定义的键。修饰键被放在这个平面因为 Flutter 区分左右修饰键如ShiftLeft与ShiftRight而 Web 只区分出单一的 Shift。其他例子包括小键盘键和游戏手柄键。平面 0x03–0x0F保留。平面 0x10–0x1FFlutter 管理的平台平面。每个平台平面对应一个 Flutter 官方支持的 embedding具体分配为平面码平台0x11Android0x12Fuchsia0x13iOS0x14macOS0x15Gtk0x16Windows0x17Web0x18GLFW平台平面存放该平台 embedding 私有的键这通常意味着这些键尚未被 Flutter 官方认可。平面内值的分配方案由平台自行决定通常取自平台原生键事件的其他字段。平面 0x20–0x2F自定义平台平面。与 Flutter 平台平面类似但供自定义平台私有使用。这套方案在源码中有两处互相印证的实现。其一生成器的常量定义 constants.dart 明确了掩码与各平面数值const MaskConstant kValueMask MaskConstant( name: Value Mask, value: 0x00FFFFFFFF, // 键码低 32 位值部分掩码 ); const MaskConstant kPlaneMask MaskConstant( name: Plane Mask, value: 0xFF00000000, ); const MaskConstant kAndroidPlane MaskConstant.platform(platform: Android, value: 0x1100000000); const MaskConstant kIosPlane MaskConstant.platform(platform: iOS, value: 0x1300000000); // ...其二生成的 keyboard_key.g.dart 中LogicalKeyboardKey持有相同的常量valueMask 0x000ffffffff、planeMask 0x0ff00000000、各xxxPlane并提供了一个实用的派生属性bool get isAutogenerated (keyId planeMask) startOfPlatformPlanes;即键的平面号达到或超过startOfPlatformPlanes0x11时视为“自动生成”的平台私有键——这正是文档中“平台平面键尚未被官方认可”判断的程序化体现。文档还特别给出了移植新平台时的行为准则对于 Flutter 管理的平台键一旦被多个平台共享而提升到 Flutter 平面其值会被改成 Flutter 平面内的新值所有由 Flutter 管理的平台将改发新值——这对使用旧平台平面值代码是破坏性变更breaking change。因此在 Flutter 管理的平台上遇到未识别的键建议提交 issue 申请将其加入keyboard_key.g.dart而不是依赖平台平面值对自定义平台平台作者完全掌控键映射值的提升不会造成破坏因此推荐直接使用平台平面0x20–0x2F的值避免把平台专属值塞进框架。六、正确性保障生成器的单元测试生成器自身由 gen_keycodes_test.dart 覆盖测试直接加载签入的两个.g.json数据库对每个平台生成器Android、GTK、iOS、macOS、Web、Windows的输出做统一断言例如所有输出必须包含版权声明、DO NOT EDIT头并且都包含KeyA、Digit1、F1、Numpad1、ShiftLeft这些基准键。这保证了任何数据或模板改动都不会悄悄丢失基础键位也验证了各平台生成器与同一数据库之间的一致性。七、小结与延伸阅读gen_keycodes是 Flutter 键盘事件体系的“上游”应用层使用的LogicalKeyboardKey、PhysicalKeyboardKey、RawKeyboard映射表以及引擎层各平台的键码翻译表全部由它从同一份数据库生成从而保证了一个键在所有平台上拥有稳定的身份。对键盘事件感兴趣或需要移植新平台的读者建议按以下顺序深入dev/tools/gen_keycodes/README.md —— 工具总览与 Key ID Scheme 原文dev/tools/gen_keycodes/data/README.md —— 全部数据文件与模板的清单说明dev/tools/gen_keycodes/bin/gen_keycodes.dart —— 主流程、在线数据源与参数定义dev/tools/gen_keycodes/lib/constants.dart —— 平面与掩码常量packages/flutter/lib/src/services/keyboard_key.g.dart 与 packages/flutter/lib/src/services/keyboard_maps.g.dart —— 最终生成产物。【免费下载链接】flutterFlutter makes it easy and fast to build beautiful apps for mobile and beyond项目地址: https://gitcode.com/GitHub_Trending/flutter41/flutter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表