Chat and books: open a show_dialog click event's dialog

A server may put a show_dialog click event on a chat line or a book
page; ClickEvent.Action.SHOW_DIALOG allows it from a server, and vanilla
opens the dialog through Screen.defaultHandleGameClickEvent. Horizon
dropped the click event while parsing the component (no arm for it in
component.rs, no ClickEvent variant to carry it), so clicking the text
did nothing.

The dialog now survives as ClickEvent::ShowDialog (a registry id or the
inline dialog), parses from click_event, round-trips back out, and both
click handlers (chat and BookViewScreen) open it with show_dialog_ref,
the same path a server's show_dialog packet takes.

Co-Authored-By: ZCode <agent@zcode.dev>
This commit is contained in:
masa-masa-0314
2026-10-28 22:52:46 +08:00
parent fe84cc07a1
commit 4d477067c3
5 changed files with 20 additions and 1 deletions
@@ -195,6 +195,11 @@ fn object_style(o: &serde_json::Map<String, Value>) -> Style {
Some("copy_to_clipboard") => {
text("value").map(crate::text::ClickEvent::CopyToClipboard)
}
// ClickEvent.ShowDialog.CODEC: a `minecraft:dialog` registry id or
// the dialog itself; the server may send it (isAllowedFromServer).
Some("show_dialog") => e.get("dialog").map(|dialog| {
crate::text::ClickEvent::ShowDialog(crate::scoreboard::json_to_tag(dialog))
}),
_ => None,
};
}
+8
View File
@@ -50,6 +50,9 @@ pub enum ClickEvent {
/// `ClickEvent.RunCommand`: the command, with or without its `/`.
RunCommand(String),
CopyToClipboard(String),
/// `ClickEvent.ShowDialog`: a `minecraft:dialog` registry id, or the
/// dialog itself as the component carried it.
ShowDialog(horizon_world::nbt::Tag),
/// A local file to open with the system handler.
/// Only allow this in locally constructed messages, never server/LAN text.
OpenFile(String),
@@ -557,6 +560,11 @@ fn style_json(style: &Style) -> Vec<(String, Value)> {
ClickEvent::RunCommand(command) => {
json!({ "action": "run_command", "command": command })
}
// ClickEvent.ShowDialog.CODEC: the dialog the server sent it with.
ClickEvent::ShowDialog(dialog) => json!({
"action": "show_dialog",
"dialog": crate::scoreboard::tag_to_json(dialog),
}),
ClickEvent::OpenFile(path) => json!({ "action": "open_file", "path": path }),
};
out.push(("click_event".to_owned(), event));
+3
View File
@@ -151,6 +151,9 @@ impl Game<'_> {
use horizon_client::clipboard::Clipboard;
self.clipboard.set(&value);
}
// BookViewScreen.handleClickEvent: the rest is
// Screen.defaultHandleGameClickEvent's.
ClickEvent::ShowDialog(dialog) => self.show_dialog_ref(&dialog),
_ => {}
}
}
+3
View File
@@ -786,6 +786,9 @@ impl Game<'_> {
}
}
Some(ClickEvent::RunCommand(command)) => self.click_command(&command),
// Screen.defaultHandleGameClickEvent: the dialog opens over the
// screen, and closes back to it.
Some(ClickEvent::ShowDialog(dialog)) => self.show_dialog_ref(&dialog),
// Screen.defaultHandleClickEvent: a page link means nothing in chat.
Some(ClickEvent::ChangePage(_)) | None => {}
}
+1 -1
View File
@@ -163,7 +163,7 @@ impl Game<'_> {
}
/// A `show_dialog` click event's dialog: a registry id or the dialog itself.
fn show_dialog_ref(&mut self, dialog: &Tag) {
pub(super) fn show_dialog_ref(&mut self, dialog: &Tag) {
match dialog {
Tag::String(id) => {
let id = id.to_string_lossy();