范围:覆盖层的推入、弹出与渲染逻辑。覆盖层是与根树并列的多根森林,所有权在 Screen,
InteractionManager仅借用引用进行路由。
OverlayTree
覆盖层元素集合是多根森林结构,每个层以一条记录包装一个 Element:
public record Layer(Element<?> root, boolean dismissOnOutsideClick, Element<?> parent) {}
层列表按加入顺序排列,最后一项为最上层。parent 记录直系父浮层元素,仅表示该浮层由哪个元素打开,不表示最外层父元素;顶层浮层的 parent 为空。层根的 Element.parent 恒为 null:层根不进根树子列表,因此浮层元素永不被裁剪,getAbsoluteX 也不上溯。
关键行为
- 添加顺序决定视觉堆叠:最后添加的根为最顶层。
- 重新推送即移动:
child若已在树中,先从层表移除,再重新插入到列表末尾(最上层)。 - 父子关系只沿直系链维护:
Layer.parent只存直系父元素,层数较多时通过层表扫描反推子层,不单独维护 children 列表。
OverlayManager
OverlayManager 由 Screen 拥有,封装 OverlayTree、推送与移除操作以及渲染入口。
public final class OverlayManager {
private final OverlayTree tree = new OverlayTree();
public OverlayTree tree() { return tree; }
public OverlayTree.Layer push(Element<?> child) { ... } // dismissOnOutsideClick = true, parent = null
public OverlayTree.Layer push(Element<?> child, boolean dismissOnOutsideClick) { ... } // parent = null
public OverlayTree.Layer push(Element<?> parent, Element<?> child) { ... } // dismissOnOutsideClick = true
public OverlayTree.Layer push(Element<?> parent, Element<?> child, boolean dismissOnOutsideClick) { ... }
public boolean remove(Element<?> el) { ... }
public void renderOverlay(GuiGraphicsExtractor e, int mx, int my, float d) { ... }
}
InteractionManager 仅借用引用进行路由,不拥有或管理覆盖层生命周期。组件侧的分层关系如下:
组件 (Tooltip / SelectDropdown / AutoCompleteBox / ContextMenu / MessageFlyout)
│ 经 InteractionManager.active.overlays() 取借用引用
▼
OverlayManager ← 所有权:Screen;增删入口 push/remove;渲染入口 renderOverlay
▼
OverlayTree ← 纯数据结构:多根森林、索引、遍历算法
InteractionManager ← 仅输入路由:分发 / 清扫 / 命中(overlays.tree().…)
覆盖层生命周期
show()/open() → removeFromParent() → overlays().push(el, dismissOnOutsideClick)
↓
[参与输入分发 / 外部关闭 / tick / renderOverlay]
↓
close() → overlays().remove(el) → 从层列表摘除 + 父指针置空
浮层元素本身就是普通 Element 子类,没有专用的浮层基类。内容面板以字段持有,坐标链手动建立,事件手动转发(见 常见实现模式)。
dismissOnOutsideClick 与外部关闭语义
dismissOnOutsideClick | 行为 | 示例 |
|---|---|---|
false | 被动:外部交互不自动关闭 | Tooltip |
true | 外部交互时自动关闭 | MessageFlyout、ContextMenu、SelectDropdown |
渲染:覆盖层树在根树之后渲染(先根,然后按添加顺序渲染覆盖层,父元素优先)。覆盖层始终显示在元素树之上。
输入:覆盖层在根树之前命中(topmostAnywhere 先调用 OverlayTree.topmostInTree,逆序遍历,最上层优先)。dismissOnOutsideClick=true 的层在明确用户输入(点击/松开/拖拽/滚轮)分发前由 dismissOverlaysOutside 判定:光标不在本层内且该标志为真时触发 onOutsideInteraction() 钩子。新增豁免:本次输入命中的浮层,其直系父链上所有祖先层在外部关闭清扫中被豁免。判定顺序为先按层表收集命中层,再沿该层 parent 链向上扫描每层的父记录,命中的父链全部保留。
级联移除:OverlayTree.remove(el) 先按 parent 链向上找到「最早仍要留在树中的祖先层」,再将该祖先连同其下的全部子层一并从层表删除。该操作只影响被关闭层自身的子树,不改动其父层;父层仍保留在树中,除非父层也被直接 remove。
相关文档
- 交互引擎:覆盖层分发细节
- 根树与层级系统:渲染/输入流水线中的覆盖层
- 元素系统:Element 基类
- Developer: 覆盖层与浮动元素:使用者口径