Theseus 模组文档
API 参考 / 入口与生命周期

Panels 与 PanelHandle


Panels 是 复古桌面风格弹窗管理入口,经 ModContext.panels() 获取;PanelHandle 是弹窗句柄,由 Panels.message / Panels.persistent / Panels.confirm 返回,用于对已显示的右下角弹窗做后续控制(动态更新内容、设置点击回调、手动关闭)。宿主在屏幕右下角渲染滑入面板(渐变标题栏、双层 bevel 边框),与游戏内 BGM 信息面板同款组件;多弹窗可同时存在,新弹窗覆盖旧弹窗之上。

方法速览

方法用途
Panels.message(String, List<String>, double)创建临时弹窗,到期自动滑出
Panels.persistent(String, boolean)创建常驻弹窗,需主动关闭
Panels.confirm(String, List<String>, Consumer<Boolean>)创建「确定 / 取消」确认弹窗
PanelHandle.setContentLines(List<String>)动态更新内容行
PanelHandle.setOnClick(Runnable)设置点击内容区的回调
PanelHandle.close()立即开始滑出并关闭
PanelHandle.setOnClosed(Runnable)注册开始关闭时的回调(仅一次)

完整签名

java
public interface Panels {

    PanelHandle message(String title, List<String> lines, double lifeSeconds);

    PanelHandle persistent(String title, boolean closable);

    default PanelHandle confirm(String title, List<String> lines,
                                java.util.function.Consumer<Boolean> onResult);
}
java
public interface PanelHandle {

    void setContentLines(List<String> lines);

    void setOnClick(Runnable onClick);

    void close();

    default void setOnClosed(Runnable onClosed);
}

message(String title, List lines, double lifeSeconds)

签名:

java
PanelHandle message(String title, List<String> lines, double lifeSeconds);

行为:创建临时消息弹窗(右下角滑入)。lifeSeconds 秒后自动滑出;小于等于 0 视为常驻(需主动关闭),宿主实现把负值夹到 0。lines 每行一条,超长自动截断,超出内容区的行不绘制;null 视为空内容。列表在创建时被防御性拷贝为不可变视图,之后修改原列表不影响弹窗。

示例:

java
ctx.panels().message("提示", List.of("谱面已导入", "共 1200 个音符"), 5.0);

persistent(String title, boolean closable)

签名:

java
PanelHandle persistent(String title, boolean closable);

行为:创建常驻弹窗(不自动过期),需主动 PanelHandle.close() 关闭。closable 决定是否显示右上角关闭按钮;false 时只能由模组主动 close()。初始内容为空,随后用 setContentLines 填充。

示例:

java
PanelHandle status = ctx.panels().persistent("连接状态", false);
status.setContentLines(List.of("正在等待对手…"));

confirm(String title, List lines, Consumer onResult)

签名:

java
default PanelHandle confirm(String title, List<String> lines, java.util.function.Consumer<Boolean> onResult);

行为:确认弹窗(复古桌面双按钮:确定 / 取消),常驻面板(不自动过期),Enter / Space = 确定、Esc = 取消。用户应答或弹窗被关闭后回调恰好一次:确定 → accept(true);取消 → accept(false);其它关闭原因(标题栏 X / 宿主清理 / 句柄 close())→ 视为未确认,accept(false)。close() 视为取消。回调异常被宿主吞掉,不影响面板关闭。宿主未实现该扩展点时 default 实现抛 UnsupportedOperationException("宿主未实现确认弹窗扩展点")。

典型用途:与场景跳转拦截器配合——拦截器否决后弹出本弹窗征求用户意见,确认回调内重发原跳转。

示例:

java
ctx.panels().confirm("离开游玩?", List.of("当前进度将丢失。"), ok -> {
    if (ok) {
        SceneHost host = ctx.capabilities().get(SceneHost.class);
        if (host != null) {
            host.pop();
        }
    }
});

setContentLines(List lines)

签名:

java
void setContentLines(List<String> lines);

行为:更新弹窗内容行(动态刷新,如实时状态、进度信息)。每行一条,超长自动截断;null 视为清空。传入列表被复制,调用后可安全复用原列表。下次游戏线程渲染生效。

示例:

java
handle.setContentLines(List.of("进度 42%", "剩余 3 个文件"));

setOnClick(Runnable onClick)

签名:

java
void setOnClick(Runnable onClick);

行为:设置点击内容区的回调,用于「点击跳转」类交互;null 表示无回调(纯提示)。

示例:

java
handle.setOnClick(() -> ctx.getLogger().info("弹窗被点击"));

close()

签名:

java
void close();

行为:关闭弹窗(立即开始滑出动画)。临时弹窗到期自动关闭,一般无需手动调用;持久弹窗需调用此方法关闭。

示例:

java
handle.close();

setOnClosed(Runnable onClosed)

签名:

java
default void setOnClosed(Runnable onClosed);

行为:注册弹窗开始关闭时的回调:无论何种原因(玩家点击关闭按钮、调用 close、宿主清空弹窗队列),都会在滑出动画开始时触发且仅触发一次。模组可借此感知弹窗被玩家关闭,并重新调用 Panels.persistent 创建新弹窗实现「重开」。null 表示取消回调。注意:在 Mod.onUnload 中主动 close 也会触发本回调,重建逻辑应有终止条件(如卸载标志位),避免关不掉。旧宿主未实现时为无操作。

示例:

java
handle.setOnClosed(() -> {
    if (!unloading) {
        reopenStatusPanel();      // 玩家关掉后自动重开
    }
});

线程:Panels 任意线程可调用(宿主内部入队,游戏线程统一渲染);PanelHandle 任意线程可调用。confirm 的应答回调在游戏线程同步执行——回调内不应做阻塞操作;需要在确认后跳转场景时直接调用 SceneHost.push/replace 即可(同线程同步执行,无竞态)。

稳定性:2.0 起冻结:2.x 内只增不破(禁止删签名 / 改签名 / 改语义);破坏性变更只在 major 版本发生且须提供迁移指南与至少一个大版本的弃用期。3.x 内继续只增不破。