Click Callbacks - HimxhMC/HimDSL GitHub Wiki
点击回调 / Click Callbacks
四种点击 / Four Click Types
HimDSL 的槽位支持四种点击回调:
A slot supports four click callbacks:
| 字段 | 触发时机 |
|---|---|
onLeftClick |
普通左键点击 |
onRightClick |
普通右键点击 |
onShiftLeftClick |
Shift + 左键 |
onShiftRightClick |
Shift + 右键 |
| Field | Trigger |
|---|---|
onLeftClick |
plain left-click |
onRightClick |
plain right-click |
onShiftLeftClick |
shift + left-click |
onShiftRightClick |
shift + right-click |
只注册你要用的。没注册的点击不做任何事——注意这是"不做任何事",不是"触发其他回调"。
Register only what you need. Unregistered clicks do nothing — note: they do nothing, they do not fall through to another callback.
用 lambda 注册 / Registering with a Lambda
回调是一个 lambda,点击时执行:
A callback is a lambda executed on click:
var slot = new GUISlot(Material.DIAMOND);
slot.name = "§a点击领取";
slot.onLeftClick = () -> {
player p = @s;
runcmd(p.name, "give " + p.name + " diamond 1");
runcmd(p.name, "msg §a你获得了钻石!");
gui.close(p);
};
回调 lambda 没有参数,用 @s 拿点击的玩家。
The lambda takes no arguments; use @s to get the clicking player.
@s 的作用 / What @s Refers To
在 GUI 点击回调里,@s 是点击者。这是 GUI 交互的核心——每个玩家打开同一个 GUI,点击时回调里的 @s 指向那个点击的玩家。
Inside a GUI click callback, @s is the clicker. This is central to GUI interaction: multiple players may have the same GUI open, and @s inside the callback refers to the one who clicked.
slot.onLeftClick = () -> {
player p = @s; // 点击的玩家
runcmd(p.name, "msg 你点了我");
};
不需要在 GUI 上"记住当前玩家"——@s 自动处理。
No need to track "the current player" on the GUI — @s handles it.
多个回调 / Multiple Callbacks
同一个槽位可以注册多种点击:
One slot may register several click types:
slot.onLeftClick = () -> {
player p = @s;
runcmd(p.name, "msg 左键:购买");
};
slot.onRightClick = () -> {
player p = @s;
runcmd(p.name, "msg 右键:查看详情");
};
两种点击各自触发各自的逻辑,互不干扰。
Each click type fires its own logic independently.
GUI 与槽位的访问 / Accessing GUI and Slot
回调里可以访问闭包捕获的变量,包括 gui 和 slot 本身:
Callbacks capture enclosing variables by closure, including gui and slot:
var gui = new GUI(1, "菜单");
slot.onLeftClick = () -> {
player p = @s;
gui.close(p); // 关闭这个 GUI
};
这让你可以在回调里调用 gui.close、修改槽位显示、重新刷新界面。
This lets callbacks call gui.close, change slot display, or refresh the interface.
常见交互模式 / Common Interaction Patterns
关闭界面 / Closing on Click
slot.onLeftClick = () -> {
player p = @s;
runcmd(p.name, "msg 操作完成");
gui.close(p);
};
扣费与发奖 / Charging and Rewarding
slot.onLeftClick = () -> {
player p = @s;
// 假设有经济系统
runcmd("CONSOLE", "eco take " + p.name + " 100");
runcmd("CONSOLE", "give " + p.name + " diamond 1");
runcmd(p.name, "msg §a购买成功");
};
切换菜单 / Switching Menus
slot.onLeftClick = () -> {
player p = @s;
gui.close(p);
// 假设 menu2 是另一个已定义的 GUI
// menu2.open(p);
};
修改自己 / Modifying the Slot
int count;
slot.onLeftClick = () -> {
count += 1;
slot.lore = {
"§7已点击: §e" + count,
"§7再点一次试试"
};
gui.update(0, 4);
};
修改槽位后必须 update,否则玩家看不到变化。
Must update after modifying a slot, or the player sees no change.
一个完整的商店例子 / A Complete Shop Example
int main() {
var gui = new GUI(3, "§6简易商店");
// 商品槽位
var diamondSlot = new GUISlot(Material.DIAMOND);
diamondSlot.name = "§b钻石";
diamondSlot.lore = {
"§7价格: §e100 金币",
"§a左键购买"
};
diamondSlot.onLeftClick = () -> {
player p = @s;
runcmd("CONSOLE", "eco take " + p.name + " 100");
runcmd("CONSOLE", "give " + p.name + " diamond 1");
runcmd(p.name, "msg §a购买成功");
gui.close(p);
};
gui[1][4] = diamondSlot;
// 关闭按钮
var closeSlot = new GUISlot(Material.BARRIER);
closeSlot.name = "§c关闭";
closeSlot.onLeftClick = () -> {
gui.close(@s);
};
gui[2][8] = closeSlot;
// 装饰玻璃
var glass = new GUISlot(Material.GRAY_STAINED_GLASS_PANE);
glass.name = " ";
for (int r = 0; r < 3; r = r + 1) {
for (int c = 0; c < 9; c = c + 1) {
if (gui[r][c].icon == Material.AIR) {
// 只在空格上铺玻璃,避免覆盖已有物品
gui[r][c] = glass;
}
}
}
gui.updateAll();
gui.open(@p);
}
注意最后一段:先放商品,后铺装饰,装饰时检查 gui[r][c].icon == Material.AIR 才写入,避免覆盖。顺序很重要。
Note the ordering: place products first, then decorate, checking gui[r][c].icon == Material.AIR before writing. Order matters.
点击类型回退 / Fallback Behavior
HimDSL 不会把 Shift 点击回退到普通点击。如果你只注册了 onLeftClick:
HimDSL does not fall back from shift-clicks to plain clicks. With only onLeftClick registered:
-
左键 → 触发
onLeftClick。 -
Shift + 左键 → 什么也不做。
-
Left-click → fires
onLeftClick. -
Shift + left-click → does nothing.
想要 Shift + 左键也响应,就显式注册:
To respond to shift-left too, register it explicitly:
slot.onLeftClick = () -> { /* 普通左键 */ };
slot.onShiftLeftClick = () -> { /* Shift 左键 */ };
或者,如果两种点击效果相同,在 lambda 里写一份逻辑,两处引用它(通过具名函数)。
Or, if both clicks do the same, put the logic in a named function and reference it from both.
常见错误 / Common Mistakes
错误一:回调里用 @p
slot.onLeftClick = () -> {
player p = @p; // ✗ 可能拿到别人
};
@p 是最近的玩家,不一定是点击者。用 @s。
错误二:修改槽位忘记刷新
slot.onLeftClick = () -> {
slot.name = "已购买";
// ✗ 忘记 gui.update(...)
};
错误三:在回调里 sleep
slot.onLeftClick = () -> {
runcmd(p.name, "msg 处理中...");
sleep(2000); // ✗ 卡主线程
runcmd(p.name, "msg 完成");
};
sleep 会阻塞服务器。用 @BukkitRunnable 拆分执行。
Mistake 1: @p inside a callback
@p is the nearest player, not necessarily the clicker. Use @s.
Mistake 2: Forgetting to refresh
After modifying the slot, call gui.update(...).
Mistake 3: sleep inside a callback
Blocks the server. Use @BukkitRunnable instead.
小结 / Summary
-
四种回调:
onLeftClick、onRightClick、onShiftLeftClick、onShiftRightClick。 -
用无参 lambda 赋值。
-
@s在回调里是点击者。 -
未注册的点击什么也不做,不会回退。
-
修改槽位后要
update。 -
不要在回调里
sleep。 -
Four callbacks:
onLeftClick,onRightClick,onShiftLeftClick,onShiftRightClick. -
Assign a no-arg lambda.
-
@sinside the callback is the clicker. -
Unregistered clicks do nothing; no fallback.
-
updateafter modifying the slot. -
No
sleepinside callbacks.
下一步 / Next Steps
下一章 [onClose 回调 / onClose] 讲界面被玩家关闭时如何响应。
Next, [onClose 回调 / onClose] covers reacting when the player closes the GUI.