onClose - HimxhMC/HimDSL GitHub Wiki

onClose 回调 / onClose

关闭时触发 / Fired on Close

除了点击槽位,界面被关闭时也能触发逻辑。用 gui.onClose(...) 注册:

Besides slot clicks, the GUI can fire logic on close. Register with gui.onClose(...):

gui.onClose(() -> {
    player p = @s;
    runcmd(p.name, "msg 界面已关闭");
});

注册在 GUI 上,不是槽位上。整个界面只支持一个 onClose 回调。

Registered on the GUI, not on a slot. Each GUI supports a single onClose.


什么时候触发 / When It Fires

onClose 在这些情况下触发:

onClose fires in these cases:

  • 玩家按 Esc 关闭界面。

  • 脚本调用 gui.close(p)。

  • 玩家打开另一个界面替换掉当前的。

  • Player presses Esc.

  • Script calls gui.close(p).

  • Player opens another inventory, replacing the current one.

不触发的情况:

Not fired for:

  • 玩家断线(此时没有"关闭"动作)。

  • 服务器关闭。

  • Player disconnect.

  • Server shutdown.


@s 在 onClose 里 / @s in onClose

和点击回调一样,@s 指向关闭界面的玩家:

Like click callbacks, @s is the player who closed:

gui.onClose(() -> {
    player p = @s;
    runcmd("CONSOLE", "say " + p.name + " 关闭了界面");
});

常见用途 / Common Uses

清理状态 / Cleaning State

玩家打开界面时锁定某些操作,关闭时解锁:

Lock something on open, unlock on close:

gui.onClose(() -> {
    player p = @s;
    // 例如:移除无敌标记
    runcmd("CONSOLE", "effect clear " + p.name + " resistance");
});

记录关闭日志 / Logging

gui.onClose(() -> {
    player p = @s;
    runcmd("CONSOLE", "say [GUI] " + p.name + " 离开了菜单");
});

打开另一个界面 / Opening Another

gui.onClose(() -> {
    player p = @s;
    // 假设 secondGui 是另一个已定义的 GUI
    // secondGui.open(p);
});

注意:open 是延迟下一 tick 执行的,不会与当前的关闭冲突。但不要在 onClose 里打开同一个 GUI,会造成无限循环。

Note: open is delayed to the next tick and does not clash with the current close. But do not open the same GUI inside its own onClose — infinite loop.


一个完整的例子 / A Complete Example

打开界面时给玩家加个标记,关闭时清除:

Add a flag on open, clear it on close:

bool inMenu;

int main() {
    var gui = new GUI(1, "菜单");

    var slot = new GUISlot(Material.PAPER);
    slot.name = "§a继续";
    slot.onLeftClick = () -> {
        gui.close(@s);
    };
    gui[0][4] = slot;

    gui.onClose(() -> {
        player p = @s;
        inMenu = false;
        runcmd(p.name, "msg 已退出菜单");
    });

    gui.updateAll();

    var p = @p;
    inMenu = true;
    gui.open(p);
}

inMenu 变量由闭包捕获。它在 main 里被声明,onClose lambda 和 main 都能访问同一个变量。

inMenu is captured by closure. Declared in main, both main and the onClose lambda see the same variable.


覆盖之前注册的回调 / Overwriting a Previous Callback

同一个 GUI 只能有一个 onClose。再次调用 onClose 会覆盖之前注册的:

A GUI has only one onClose. Calling onClose again overwrites the previous:

gui.onClose(() -> { /* 第一版 */ });
gui.onClose(() -> { /* 第二版 */ });    // 覆盖第一版

想清除已注册的回调,传 null:

To clear a registered callback, pass null:

gui.onClose(null);

与点击回调的关系 / Relation to Click Callbacks

onClose 和点击回调独立触发:

onClose and click callbacks fire independently:

  • 点击"关闭"按钮 → 先触发 onLeftClick,然后触发 onClose(如果按钮的 lambda 里调用了 gui.close)。

  • 玩家按 Esc → 只触发 onClose。

  • 点击一个普通槽位 → 只触发槽位回调,界面没有关闭,所以 onClose 不触发。

  • Click a "close" button → onLeftClick fires, then onClose (if the lambda called gui.close).

  • Press Esc → only onClose.

  • Click a normal slot → only the slot callback; the GUI stays open, so no onClose.

onClose 不是"每次点击后触发",是"界面真的被关闭时触发"。区别很重要。

onClose is not "after every click"; it is "when the GUI is actually closed". The distinction matters.


常见错误 / Common Mistakes

错误一:把 onClose 注册在槽位上

slot.onClose = () -> { ... };       // ✗ 槽位没有 onClose

正确写法是 gui.onClose(lambda)。

错误二:以为每次点击都触发

onClose 只在界面关闭时触发,与点击无关。

错误三:在 onClose 里打开同一个 GUI

gui.onClose(() -> {
    gui.open(@s);                   // ✗ 无限循环
});

关闭后立刻打开会让关闭再触发,进入循环。用 @BukkitRunnable 延迟几 tick 再打开。

Mistake 1: Registering on a slot

Use gui.onClose(lambda).

Mistake 2: Expecting onClose after clicks

Only when the GUI closes.

Mistake 3: Reopening the same GUI inside onClose

Infinite loop. Use @BukkitRunnable with a delay.


小结 / Summary

  • gui.onClose(lambda) 注册关闭回调。

  • 触发时机:玩家关闭、脚本关闭、切换界面。

  • @s 是关闭的玩家。

  • 每个 GUI 只有一个 onClose,重复注册会覆盖。

  • 传 null 清除。

  • 不要在 onClose 里打开同一个 GUI。

  • gui.onClose(lambda) registers the close callback.

  • Fires on player-close, script-close, or inventory switch.

  • @s is the closing player.

  • One onClose per GUI; re-registering overwrites.

  • Pass null to clear.

  • Do not reopen the same GUI inside onClose.


下一步 / Next Steps

下一章 [GUI 刷新 / GUI-Refresh] 系统讲 update / updateAll 的时机和性能影响。

Next, [GUI 刷新 / GUI-Refresh] covers when to call update / updateAll and the performance implications.