← 返回索引

范围:覆盖层的推入、弹出与渲染逻辑。覆盖层是与根树并列的多根森林,所有权在 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。


相关文档