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) | 注册开始关闭时的回调(仅一次) |
完整签名
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);
}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)
签名:
PanelHandle message(String title, List<String> lines, double lifeSeconds);行为:创建临时消息弹窗(右下角滑入)。lifeSeconds 秒后自动滑出;小于等于 0 视为常驻(需主动关闭),宿主实现把负值夹到 0。lines 每行一条,超长自动截断,超出内容区的行不绘制;null 视为空内容。列表在创建时被防御性拷贝为不可变视图,之后修改原列表不影响弹窗。
示例:
ctx.panels().message("提示", List.of("谱面已导入", "共 1200 个音符"), 5.0);persistent(String title, boolean closable)
签名:
PanelHandle persistent(String title, boolean closable);行为:创建常驻弹窗(不自动过期),需主动 PanelHandle.close() 关闭。closable 决定是否显示右上角关闭按钮;false 时只能由模组主动 close()。初始内容为空,随后用 setContentLines 填充。
示例:
PanelHandle status = ctx.panels().persistent("连接状态", false);
status.setContentLines(List.of("正在等待对手…"));confirm(String title, List lines, Consumer onResult)
签名:
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("宿主未实现确认弹窗扩展点")。
典型用途:与场景跳转拦截器配合——拦截器否决后弹出本弹窗征求用户意见,确认回调内重发原跳转。
示例:
ctx.panels().confirm("离开游玩?", List.of("当前进度将丢失。"), ok -> {
if (ok) {
SceneHost host = ctx.capabilities().get(SceneHost.class);
if (host != null) {
host.pop();
}
}
});setContentLines(List lines)
签名:
void setContentLines(List<String> lines);行为:更新弹窗内容行(动态刷新,如实时状态、进度信息)。每行一条,超长自动截断;null 视为清空。传入列表被复制,调用后可安全复用原列表。下次游戏线程渲染生效。
示例:
handle.setContentLines(List.of("进度 42%", "剩余 3 个文件"));setOnClick(Runnable onClick)
签名:
void setOnClick(Runnable onClick);行为:设置点击内容区的回调,用于「点击跳转」类交互;null 表示无回调(纯提示)。
示例:
handle.setOnClick(() -> ctx.getLogger().info("弹窗被点击"));close()
签名:
void close();行为:关闭弹窗(立即开始滑出动画)。临时弹窗到期自动关闭,一般无需手动调用;持久弹窗需调用此方法关闭。
示例:
handle.close();setOnClosed(Runnable onClosed)
签名:
default void setOnClosed(Runnable onClosed);行为:注册弹窗开始关闭时的回调:无论何种原因(玩家点击关闭按钮、调用 close、宿主清空弹窗队列),都会在滑出动画开始时触发且仅触发一次。模组可借此感知弹窗被玩家关闭,并重新调用 Panels.persistent 创建新弹窗实现「重开」。null 表示取消回调。注意:在 Mod.onUnload 中主动 close 也会触发本回调,重建逻辑应有终止条件(如卸载标志位),避免关不掉。旧宿主未实现时为无操作。
示例:
handle.setOnClosed(() -> {
if (!unloading) {
reopenStatusPanel(); // 玩家关掉后自动重开
}
});线程:Panels 任意线程可调用(宿主内部入队,游戏线程统一渲染);PanelHandle 任意线程可调用。confirm 的应答回调在游戏线程同步执行——回调内不应做阻塞操作;需要在确认后跳转场景时直接调用 SceneHost.push/replace 即可(同线程同步执行,无竞态)。
稳定性:2.0 起冻结:2.x 内只增不破(禁止删签名 / 改签名 / 改语义);破坏性变更只在 major 版本发生且须提供迁移指南与至少一个大版本的弃用期。3.x 内继续只增不破。