mc_chat/style/
mod.rs

1use crate::component::ChatComponent;
2
3#[cfg(feature = "serde")]
4use serde::Deserialize;
5#[cfg(feature = "serde")]
6mod serde_support;
7
8/// The version number of the Minecraft protocol for 1.7
9pub const VERSION_1_7: u32 = 4;
10/// The version number of the Minecraft protocol for 1.8
11pub const VERSION_1_8: u32 = 47;
12/// The version number of the Minecraft protocol for 1.15
13pub const VERSION_1_15: u32 = 573;
14/// The version number of the Minecraft protocol for 1.16
15pub const VERSION_1_16: u32 = 735;
16
17/// The style of a [`ChatComponent`]
18#[derive(Clone, Debug)]
19#[cfg_attr(feature = "serde", derive(Deserialize))]
20pub struct ComponentStyle {
21    #[cfg_attr(
22        feature = "serde",
23        serde(skip, default = "serde_support::default_style_version")
24    )]
25    version: u32,
26    bold: Option<bool>,
27    italic: Option<bool>,
28    underlined: Option<bool>,
29    strikethrough: Option<bool>,
30    obfuscated: Option<bool>,
31    color: Option<ChatColor>,
32    /// This field is ignored for versions older than 1.8
33    insertion: Option<String>,
34    /// This field is ignored for versions older than 1.16
35    font: Option<String>,
36    #[cfg_attr(feature = "serde", serde(rename = "clickEvent"))]
37    click_event: Option<ClickEvent>,
38    #[cfg_attr(feature = "serde", serde(rename = "hoverEvent"))]
39    hover_event: Option<HoverEvent>,
40}
41
42impl ComponentStyle {
43    pub fn v1_7() -> Self {
44        ComponentStyle::with_version(4)
45    }
46
47    pub fn v1_8() -> Self {
48        ComponentStyle::with_version(47)
49    }
50
51    pub fn v1_15() -> Self {
52        ComponentStyle::with_version(573)
53    }
54
55    pub fn v1_16() -> Self {
56        ComponentStyle::with_version(735)
57    }
58
59    pub fn with_version(version: u32) -> Self {
60        ComponentStyle {
61            version,
62            bold: None,
63            italic: None,
64            underlined: None,
65            strikethrough: None,
66            obfuscated: None,
67            color: None,
68            insertion: None,
69            font: None,
70            click_event: None,
71            hover_event: None,
72        }
73    }
74
75    pub fn set_color(&mut self, color: Option<ChatColor>) {
76        self.color = color;
77    }
78
79    pub fn color(mut self, color: Option<ChatColor>) -> Self {
80        self.set_color(color);
81        self
82    }
83
84    pub fn set_color_if_absent(&mut self, color: ChatColor) {
85        if self.color.is_none() {
86            self.color = Some(color);
87        }
88    }
89
90    pub fn color_if_absent(mut self, color: ChatColor) -> Self {
91        self.set_color_if_absent(color);
92        self
93    }
94
95    pub fn set_bold(&mut self, bold: bool) {
96        self.bold = Some(bold);
97    }
98
99    pub fn bold(mut self, bold: bool) -> Self {
100        self.set_bold(bold);
101        self
102    }
103
104    pub fn set_italic(&mut self, italic: bool) {
105        self.italic = Some(italic);
106    }
107
108    pub fn italic(mut self, italic: bool) -> Self {
109        self.set_italic(italic);
110        self
111    }
112
113    pub fn set_underlined(&mut self, underlined: bool) {
114        self.underlined = Some(underlined);
115    }
116
117    pub fn underlined(mut self, underlined: bool) -> Self {
118        self.set_underlined(underlined);
119        self
120    }
121
122    pub fn set_strikethrough(&mut self, strikethrough: bool) {
123        self.strikethrough = Some(strikethrough);
124    }
125
126    pub fn strikethrough(mut self, strikethrough: bool) -> Self {
127        self.set_strikethrough(strikethrough);
128        self
129    }
130
131    pub fn set_obfuscated(&mut self, obfuscated: bool) {
132        self.obfuscated = Some(obfuscated);
133    }
134
135    pub fn obfuscated(mut self, obfuscated: bool) -> Self {
136        self.set_obfuscated(obfuscated);
137        self
138    }
139
140    pub fn set_font<T: Into<String>>(&mut self, font: Option<T>) {
141        self.font = font.map(|font| font.into());
142    }
143
144    pub fn font<T: Into<String>>(mut self, font: Option<T>) -> Self {
145        self.set_font(font);
146        self
147    }
148
149    pub fn set_insertion<T: Into<String>>(&mut self, insertion: Option<T>) {
150        self.insertion = insertion.map(|insertion| insertion.into());
151    }
152
153    pub fn insertion<T: Into<String>>(mut self, insertion: Option<T>) -> Self {
154        self.set_insertion(insertion);
155        self
156    }
157
158    pub fn set_click_event(&mut self, click_event: Option<ClickEvent>) {
159        self.click_event = click_event;
160    }
161
162    pub fn click_event(mut self, click_event: Option<ClickEvent>) -> Self {
163        self.set_click_event(click_event);
164        self
165    }
166
167    pub fn set_hover_event(&mut self, hover_event: Option<HoverEvent>) {
168        self.hover_event = hover_event;
169    }
170
171    pub fn hover_event(mut self, hover_event: Option<HoverEvent>) -> Self {
172        self.set_hover_event(hover_event);
173        self
174    }
175
176    pub fn get_color(&self) -> Option<&ChatColor> {
177        self.color.as_ref()
178    }
179
180    pub fn get_bold(&self) -> Option<bool> {
181        self.bold
182    }
183
184    pub fn get_italic(&self) -> Option<bool> {
185        self.italic
186    }
187
188    pub fn get_underlined(&self) -> Option<bool> {
189        self.underlined
190    }
191
192    pub fn get_strikethrough(&self) -> Option<bool> {
193        self.strikethrough
194    }
195
196    pub fn get_obfuscated(&self) -> Option<bool> {
197        self.obfuscated
198    }
199
200    pub fn get_font(&self) -> Option<&String> {
201        if self.version >= 713 {
202            self.font.as_ref()
203        } else {
204            None
205        }
206    }
207
208    pub fn get_insertion(&self) -> Option<&String> {
209        if self.version >= 5 {
210            self.insertion.as_ref()
211        } else {
212            None
213        }
214    }
215
216    pub fn get_click_event(&self) -> Option<&ClickEvent> {
217        self.click_event.as_ref()
218    }
219
220    pub fn get_hover_event(&self) -> Option<&HoverEvent> {
221        self.hover_event.as_ref()
222    }
223
224    pub fn change_version(&mut self, to: u32) {
225        self.version = to;
226    }
227
228    /// Resets all fields to default (being [`None`]).
229    pub fn reset(&mut self) {
230        self.bold = None;
231        self.italic = None;
232        self.underlined = None;
233        self.strikethrough = None;
234        self.obfuscated = None;
235        self.color = None;
236        self.insertion = None;
237        self.font = None;
238        self.click_event = None;
239        self.hover_event = None;
240    }
241}
242
243/// The different colors a [`ChatComponent`] can have.
244/// ## TODO
245/// Automatically find nearest value when serializing [`ChatColor::Custom`] for older versions
246#[derive(Clone, Debug)]
247pub enum ChatColor {
248    Black,
249    DarkBlue,
250    DarkGreen,
251    DarkCyan,
252    DarkRed,
253    Purple,
254    Gold,
255    Gray,
256    DarkGray,
257    Blue,
258    Green,
259    Cyan,
260    Red,
261    Pink,
262    Yellow,
263    White,
264    /// This field is ignored for versions older than 1.16.
265    ///
266    /// See [`ChatColor::custom()`].
267    Custom(String),
268    Reset,
269}
270
271impl ChatColor {
272    pub fn custom<T: Into<String>>(color: T) -> ChatColor {
273        ChatColor::Custom(color.into())
274    }
275}
276
277/// A ClickEvent useful in a chat message or book.
278#[derive(Clone, Debug)]
279#[cfg_attr(feature = "serde", derive(Deserialize))]
280#[cfg_attr(feature = "serde", serde(try_from = "serde_support::ClickEventData"))]
281pub enum ClickEvent {
282    OpenUrl(String),
283    RunCommand(String),
284    SuggestCommand(String),
285    ChangePage(u32),
286    /// This field is ignored for versions older than 1.15.
287    CopyToClipBoard(String),
288}
289
290impl ClickEvent {
291    pub fn url<T: Into<String>>(url: T) -> Self {
292        Self::OpenUrl(url.into())
293    }
294
295    pub fn run_command<T: Into<String>>(cmd: T) -> Self {
296        Self::RunCommand(cmd.into())
297    }
298
299    pub fn suggest_command<T: Into<String>>(cmd: T) -> Self {
300        Self::SuggestCommand(cmd.into())
301    }
302
303    pub fn page<T: Into<u32>>(page: T) -> Self {
304        Self::ChangePage(page.into())
305    }
306
307    pub fn clipboard<T: Into<String>>(str: T) -> Self {
308        Self::CopyToClipBoard(str.into())
309    }
310}
311
312/// A HoverEvent useful in a chat message or book.
313/// ## TODO
314/// Change 'value' field to 'contents' when serializing for 1.16+,
315/// also add more sophisticated `item` and `entity` data structures
316#[derive(Clone, Debug)]
317#[cfg_attr(feature = "serde", derive(Deserialize))]
318#[cfg_attr(feature = "serde", serde(try_from = "serde_support::HoverEventData"))]
319pub enum HoverEvent {
320    ShowText(Box<ChatComponent>),
321    ShowItem(String),
322    ShowEntity(String),
323}