Skip to content

Feat/tab focus traversal - #62

Open
FarnaHerry wants to merge 3 commits into
sudoevolve:devfrom
FarnaHerry:feat/tab-focus-traversal
Open

Feat/tab focus traversal#62
FarnaHerry wants to merge 3 commits into
sudoevolve:devfrom
FarnaHerry:feat/tab-focus-traversal

Conversation

@FarnaHerry

Copy link
Copy Markdown
Contributor

概要

此前 Tab 键在输入后端直接被丢弃(GLFW / SDL 的按键映射都没有 Tab),
键盘焦点只能靠鼠标点击驱动。本 PR 加入纯增量的 Tab / Shift+Tab 焦点遍历:

  • Tab 前进、Shift+Tab 后退,在 focusable 元素间循环(wrap)。
  • 遍历顺序 = 绘制 / 命中顺序:对元素树前序遍历,按 zIndex 升序稳定排序
    (同 zIndex 按声明序)——即用户视觉上感知到的堆叠顺序。
  • 因为 Tab 之前是 no-op,没有任何既有行为被破坏。

变更

  • InputKey 新增 Tab,两端后端补上映射(GLFW_KEY_TABSDLK_TAB)。
  • 新增 core/dsl_focus.hcollectOrderedFocusableIds(前序收集 focusable
    元素、跳过 disabled 子树)与 nextFocusTarget(前进 / 后退 / 回绕决策)。
  • Runtime 新增公开 traverseFocus(bool reverse)focusedId(),私有
    handleFocusTraversal(const KeyboardEvent&)
  • Runtime::updateupdateTextInput 之前消费 Tab 键;被消费时不再
    转发给当前焦点输入框。

行为

  • 遍历集合 = .focusable() / .onTextInput() / .onFocusChanged() 的元素,
    与鼠标 focus 命中的集合一致;跳过 disabled / 禁用子树。
  • 无焦点时 Tab 聚焦第一个、Shift+Tab 聚焦最后一个。
  • components::button 默认只设 interactive、不设 focusable,默认不参与
    Tab(需显式 .focusable()),保持既有默认行为不变。
  • Ctrl / Cmd+Tab 不消费,行为不变。

测试与验证

测试与验证

  • 新增 headless 单测 tests/unit/focus_traversal.cpp(20 个用例:顺序、
    zIndex、disabled 子树、回绕、onFocusChanged、禁用时清焦点)。
  • window_only_probe 扩展:验证 Tab+Shift 携带方向进入输入队列。
  • 新增 windowed fixture tests/fixtures/focus_traversal_viewer.cpp 供人工
    Tab / Shift+Tab 验证。
  • 全量 Debug 构建通过;ctest -L unit 16/16、ctest -L probe 5/5 全部通过。

已知限制

  • V1 不跳过滚出视口 / 被裁剪的元素。
  • Alt+Tab 无法与普通 Tab 区分(后端只建模 ctrl / shift 修饰键)。
  • SDL 后端 Ctrl+Tab 的 \t 文本加固未包含(可选后续)。

FARNA-HERRY-OFF\farna added 3 commits August 17, 2026 08:52
Tab was previously dropped in the input backends. Add InputKey::Tab,
map GLFW_KEY_TAB / SDLK_TAB, and traverse focus among focusable
elements (z-order, wrapping, skipping disabled subtrees) in Runtime
before text input dispatch. Buttons stay non-focusable by default;
only .focusable()/.onTextInput()/.onFocusChanged() elements participate.
Headless unit test for collectOrderedFocusableIds/nextFocusTarget and
Runtime::traverseFocus (order, z-index, disabled subtrees, wrap,
onFocusChanged, focus clearing). Extend window_only_probe to verify
Tab+Shift reaches the input queue, and add a windowed
focus_traversal_viewer fixture for manual Tab/Shift+Tab testing.
Note Tab/Shift+Tab traversal in the Runtime flow, the keyboard
capability lists, and the current limitations (no scrolled/clipped
skipping, Alt+Tab indistinguishable, buttons not focusable by default).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant