1. 输入接口对齐

将元素上的鼠标、键盘、滚轮回调改为事件对象。例如:

@Override
public boolean mousePressed(MousePressedEvent e) {
    super.mousePressed(e);
    // ...
}

旧参数 (mx, my, btn, dx, dy) 迁移为:

  • e.x(), e.y()
  • e.button()
  • e.dx(), e.dy()
  • e.horizontal(), e.vertical()
  • e.modifiers()

键盘事件直接读取 key(), scancode(), modifiers();IME 读取 text(), caretPosition(), blocks(), focusedBlock()。

2. 容器/子元素迁移

如果代码仍直接遍历 LayoutElement 的内部子元素,替换为 Element.children() 的公开访问。子元素的输入状态不再保存在 LayoutElement 中。

对应动作:

  • 移除 LayoutElement.dispatchToTopmost(...) 的直接调用;
  • 移除容器为某元素维护 pressOwner 的字段或本地标记。

3. 浮层窗口迁移

原来的 OverlayNode 与 blocksMouse 已删除,迁移对应关系为:

  • OverlayTree.push(node) → OverlayTree.push(root, dismissOnOutsideClick)
  • OverlayNode.blocking → 已不存在,改用命中链路判断
  • OverlayTree.onOutsideInteraction 旁路判断 → 由 InteractionManager.dismissOverlaysOutside 统一向各层发送 onOutsideInteraction()

如果旧代码手工清理浮层树结构,改为调用 OverlayTree.remove(root)。

4. 焦点与键盘迁移

检查旧的 FocusableElement 子类,确保键盘链路不再自行查找焦点:

  • keyPressed、charTyped、preeditUpdated 继续返回 boolean;
  • 元素内不手写“当前元素是否为焦点”的分支,焦点判定由 InteractionManager 的 resolveKeyboardReceiver() 完成;
  • 若旧逻辑依赖在字段里缓存接收者,改为在事件进入时按路由读取。

5. tick 迁移

将子元素 tick 递归合并到统一驱动:

  • 在调用 UiTickDriver.onRenderFrame(manager) 的位置,LayoutElement 内部不再递归 tick();
  • OverlayTree.tickAll() 旧入口不存在,改由 TickWalker.forEachElement(root, overlays, Element::tick) 触发;
  • InteractionManager.tick() 旧入口同样不存在:它直接转发到 TickWalker.forEachElement(root, overlays.tree(), Element::tick),按 20 TPS 运行;若与驱动器同时调用,每帧状态推进两次,即弹簧翻倍、动画速度加倍且不报错。Screen.tick() 中改为调用 uiTickDriver.onRenderFrame(interactionManager);需要其他节拍时直接调用 TickWalker.forEachElement,该方法是 public static。
  • 任何“仅在父元素可见时递归调用子元素 tick()”的手写逻辑,现由 TickWalker 处理。

常见注意事项

  • HoverEvent 不再区分 mouseEntered/mouseMoved/mouseExited 的事件类型;如果代码按事件名判定动作,则以调用方的分支为准。
  • PreeditUpdatedEvent.toPlatformPreedit() 是唯一直接适配 MC PreeditEvent 的方法,不再自行构造平台 PreeditEvent。
  • LayoutElement 只做布局与渲染,不再持有输入分发的中间状态。
  • OverlayTree.Layer 是扁平记录,不再按“节点嵌套父子链”的旧模型维护层级关系。
  • InteractionManager 的命中顺序以浮层树优先为准;根树仅作为浮层树的后备。