mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
Native mods: read and write the open chat box (native_chat_input, native_set_chat_input)
A client mod that converts text as it's typed (Emoji Type's :code: to emoji) reads the chat box's text and cursor and writes the result back as an edit, so completions refresh as for typing. Only what typing lets in reaches the box. Stacked on native_open_chat. Part of #10107 Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
ac8a09ef0a
commit
53d0ea1320
3 files changed
+59
No files matched your search
@@ -737,6 +737,32 @@ impl Game<'_> {
|
||||
self.right = false;
|
||||
self.capture(false);
|
||||
}
|
||||
/// The open chat box's text and its cursor, counted in characters; `None`
|
||||
/// while the chat screen isn't open. API @API:chat_input@.
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn native_chat_input(&self) -> Option<(String, usize)> {
|
||||
let screen = self.ui.chat_screen.as_ref()?;
|
||||
Some((screen.input.value.clone(), screen.input.cursor))
|
||||
}
|
||||
/// Replaces the open chat box's text and puts the cursor at `cursor`
|
||||
/// (characters, clamped to the text), as an edit: completions refresh as
|
||||
/// they do for typing. What typing can't put in the box isn't put there
|
||||
/// either (`chat_box_text`), and the 256-character limit holds. An error
|
||||
/// while the chat screen isn't open. API @API:chat_input@.
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn native_set_chat_input(&mut self, text: &str, cursor: usize) -> Result<(), String> {
|
||||
let m = Measure(&self.gui.atlas.font);
|
||||
let c = Commands {
|
||||
dispatcher: chat_tree(&self.remote),
|
||||
registries: &self.ui.registries,
|
||||
};
|
||||
let Some(screen) = self.ui.chat_screen.as_mut() else {
|
||||
return Err("the chat isn't open".into());
|
||||
};
|
||||
screen.suggest_command(&m, &c, &chat_box_text(text));
|
||||
screen.input.move_cursor_to(&m, cursor, false);
|
||||
Ok(())
|
||||
}
|
||||
pub(super) fn close_chat(&mut self, reason: ChatExit) {
|
||||
// InBedChatScreen.onClose: leaving the screen means getting up; the
|
||||
// screen goes once the player has woken (`tick_in_bed_chat`).
|
||||
@@ -1679,6 +1705,14 @@ impl Game<'_> {
|
||||
}
|
||||
}
|
||||
/// `yyyy-MM-dd_HH.mm.ss[_n].png` (Util.getFilenameFormattedDateTime, in local time).
|
||||
/// What a mod may write into the chat box: the characters `ChatScreen`'s
|
||||
/// typing lets in (`StringUtil.isAllowedChatCharacter`: no control
|
||||
/// characters, no DEL, no section sign).
|
||||
fn chat_box_text(text: &str) -> String {
|
||||
text.chars()
|
||||
.filter(|&ch| ch >= ' ' && ch != '\u{7f}' && ch != '§')
|
||||
.collect()
|
||||
}
|
||||
pub fn screenshot_name(dir: &std::path::Path) -> String {
|
||||
// Util.getFilenameFormattedDateTime: the local date and time.
|
||||
let base = chrono::Local::now().format("%Y-%m-%d_%H.%M.%S").to_string();
|
||||
@@ -1710,6 +1744,14 @@ const LINK_COPY: usize = 1;
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// A mod's text reaches the chat box as typing would leave it: emoji and
|
||||
/// accents stay, control characters, DEL and the section sign go.
|
||||
#[test]
|
||||
fn a_mods_chat_text_keeps_what_typing_allows() {
|
||||
assert_eq!(super::chat_box_text("hi :smile: → 😀 é"), "hi :smile: → 😀 é");
|
||||
assert_eq!(super::chat_box_text("a\nb\tc\u{7f}d§6e"), "abcd6e");
|
||||
}
|
||||
|
||||
/// `ChatScreen.shouldDiscardDraft`.
|
||||
#[test]
|
||||
fn draft_discard_follows_the_exit_reason() {
|
||||
|
||||
@@ -3241,6 +3241,13 @@ edits and sends it, or presses Escape, and it goes into the chat history then li
|
||||
256-character limit and its character filter. It is an error with no player in a world, and while another screen
|
||||
(the chat itself included) is open. Bundles need `"hooks": @API:open_chat@`.
|
||||
|
||||
While the chat is open, `native_chat_input()` (API @API:chat_input@, `crates/horizon-client/src/game/chat.rs`) gives its
|
||||
text and cursor (counted in characters), `None` when it's closed, and `native_set_chat_input(text, cursor)` replaces
|
||||
them, as an edit: completions refresh as they do for typing, so a mod that turns `:smile:` into an emoji as it's typed
|
||||
runs from `native_screen_char_typed_pre` and writes the result back. The cursor is clamped to the text. Only what typing
|
||||
lets in reaches the box (no control characters, DEL or section sign), within its 256-character limit. Writing is an
|
||||
error while the chat isn't open. Bundles need `"hooks": @API:chat_input@`.
|
||||
|
||||
### Packets a client mod sends
|
||||
|
||||
API 409, `crates/horizon-client/src/game/native_net.rs`: `native_send_packet(packet)` sends one
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"hooks": [],
|
||||
"helpers": [
|
||||
{"name": "chat_input", "target": "horizon_client::game::chat::Game<'_>::native_chat_input"},
|
||||
{"name": "set_chat_input", "target": "horizon_client::game::chat::Game<'_>::native_set_chat_input"}
|
||||
],
|
||||
"rust_summary": "a client mod reads and writes the open chat box (`native_chat_input`, `native_set_chat_input`).",
|
||||
"doc_summary": "`native_chat_input()` gives the open chat box's text and cursor, `native_set_chat_input(text, cursor)` replaces them as an edit (completions refresh); an error while the chat is closed. Bundles need \"hooks\": @API:chat_input@.",
|
||||
"comments": ["crates/horizon-client/src/game/chat.rs"]
|
||||
}
|
||||
Reference in new issue
Block a user