ratatui_core/buffer/
cell.rs1use compact_str::CompactString;
2
3use crate::style::{Color, Modifier, Style};
4use crate::symbols::merge::MergeStrategy;
5
6#[derive(Debug, Default, Clone)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct Cell {
10 symbol: Option<CompactString>,
19
20 pub fg: Color,
22
23 pub bg: Color,
25
26 #[cfg(feature = "underline-color")]
28 pub underline_color: Color,
29
30 pub modifier: Modifier,
32
33 pub skip: bool,
35}
36
37impl Cell {
38 pub const EMPTY: Self = Self {
40 symbol: None,
41 fg: Color::Reset,
42 bg: Color::Reset,
43 #[cfg(feature = "underline-color")]
44 underline_color: Color::Reset,
45 modifier: Modifier::empty(),
46 skip: false,
47 };
48
49 pub const fn new(symbol: &'static str) -> Self {
56 Self {
57 symbol: Some(CompactString::const_new(symbol)),
58 ..Self::EMPTY
59 }
60 }
61
62 #[must_use]
66 pub fn symbol(&self) -> &str {
67 self.symbol.as_ref().map_or(" ", |s| s.as_str())
68 }
69
70 pub fn merge_symbol(&mut self, symbol: &str, strategy: MergeStrategy) -> &mut Self {
108 let merged_symbol = self
109 .symbol
110 .as_ref()
111 .map_or(symbol, |s| strategy.merge(s, symbol));
112 self.symbol = Some(CompactString::new(merged_symbol));
113 self
114 }
115
116 pub fn set_symbol(&mut self, symbol: &str) -> &mut Self {
118 self.symbol = Some(CompactString::new(symbol));
119 self
120 }
121
122 pub(crate) fn append_symbol(&mut self, symbol: &str) -> &mut Self {
126 self.symbol.get_or_insert_default().push_str(symbol);
127 self
128 }
129
130 pub fn set_char(&mut self, ch: char) -> &mut Self {
132 let mut buf = [0; 4];
133 self.symbol = Some(CompactString::new(ch.encode_utf8(&mut buf)));
134 self
135 }
136
137 pub const fn set_fg(&mut self, color: Color) -> &mut Self {
139 self.fg = color;
140 self
141 }
142
143 pub const fn set_bg(&mut self, color: Color) -> &mut Self {
145 self.bg = color;
146 self
147 }
148
149 pub fn set_style<S: Into<Style>>(&mut self, style: S) -> &mut Self {
154 let style = style.into();
155 if let Some(c) = style.fg {
156 self.fg = c;
157 }
158 if let Some(c) = style.bg {
159 self.bg = c;
160 }
161 #[cfg(feature = "underline-color")]
162 if let Some(c) = style.underline_color {
163 self.underline_color = c;
164 }
165 self.modifier.insert(style.add_modifier);
166 self.modifier.remove(style.sub_modifier);
167 self
168 }
169
170 #[must_use]
172 pub const fn style(&self) -> Style {
173 Style {
174 fg: Some(self.fg),
175 bg: Some(self.bg),
176 #[cfg(feature = "underline-color")]
177 underline_color: Some(self.underline_color),
178 add_modifier: self.modifier,
179 sub_modifier: Modifier::empty(),
180 }
181 }
182
183 pub const fn set_skip(&mut self, skip: bool) -> &mut Self {
188 self.skip = skip;
189 self
190 }
191
192 pub fn reset(&mut self) {
194 *self = Self::EMPTY;
195 }
196}
197
198impl PartialEq for Cell {
199 fn eq(&self, other: &Self) -> bool {
205 let symbols_eq = self.symbol() == other.symbol();
207
208 #[cfg(feature = "underline-color")]
209 let underline_color_eq = self.underline_color == other.underline_color;
210 #[cfg(not(feature = "underline-color"))]
211 let underline_color_eq = true;
212
213 symbols_eq
214 && underline_color_eq
215 && self.fg == other.fg
216 && self.bg == other.bg
217 && self.modifier == other.modifier
218 && self.skip == other.skip
219 }
220}
221
222impl Eq for Cell {}
223
224impl core::hash::Hash for Cell {
225 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
230 self.symbol().hash(state);
231 self.fg.hash(state);
232 self.bg.hash(state);
233 #[cfg(feature = "underline-color")]
234 self.underline_color.hash(state);
235 self.modifier.hash(state);
236 self.skip.hash(state);
237 }
238}
239
240impl From<char> for Cell {
241 fn from(ch: char) -> Self {
242 let mut cell = Self::EMPTY;
243 cell.set_char(ch);
244 cell
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251
252 #[test]
253 fn new() {
254 let cell = Cell::new("あ");
255 assert_eq!(
256 cell,
257 Cell {
258 symbol: Some(CompactString::const_new("あ")),
259 fg: Color::Reset,
260 bg: Color::Reset,
261 #[cfg(feature = "underline-color")]
262 underline_color: Color::Reset,
263 modifier: Modifier::empty(),
264 skip: false,
265 }
266 );
267 }
268
269 #[test]
270 fn empty() {
271 let cell = Cell::EMPTY;
272 assert_eq!(cell.symbol(), " ");
273 }
274
275 #[test]
276 fn set_symbol() {
277 let mut cell = Cell::EMPTY;
278 cell.set_symbol("あ"); assert_eq!(cell.symbol(), "あ");
280 cell.set_symbol("👨👩👧👦"); assert_eq!(cell.symbol(), "👨👩👧👦");
282 }
283
284 #[test]
285 fn append_symbol() {
286 let mut cell = Cell::EMPTY;
287 cell.set_symbol("あ"); cell.append_symbol("\u{200B}"); assert_eq!(cell.symbol(), "あ\u{200B}");
290 }
291
292 #[test]
293 fn set_char() {
294 let mut cell = Cell::EMPTY;
295 cell.set_char('あ'); assert_eq!(cell.symbol(), "あ");
297 }
298
299 #[test]
300 fn set_fg() {
301 let mut cell = Cell::EMPTY;
302 cell.set_fg(Color::Red);
303 assert_eq!(cell.fg, Color::Red);
304 }
305
306 #[test]
307 fn set_bg() {
308 let mut cell = Cell::EMPTY;
309 cell.set_bg(Color::Red);
310 assert_eq!(cell.bg, Color::Red);
311 }
312
313 #[test]
314 fn set_style() {
315 let mut cell = Cell::EMPTY;
316 cell.set_style(Style::new().fg(Color::Red).bg(Color::Blue));
317 assert_eq!(cell.fg, Color::Red);
318 assert_eq!(cell.bg, Color::Blue);
319 }
320
321 #[test]
322 fn set_skip() {
323 let mut cell = Cell::EMPTY;
324 cell.set_skip(true);
325 assert!(cell.skip);
326 }
327
328 #[test]
329 fn reset() {
330 let mut cell = Cell::EMPTY;
331 cell.set_symbol("あ");
332 cell.set_fg(Color::Red);
333 cell.set_bg(Color::Blue);
334 cell.set_skip(true);
335 cell.reset();
336 assert_eq!(cell.symbol(), " ");
337 assert_eq!(cell.fg, Color::Reset);
338 assert_eq!(cell.bg, Color::Reset);
339 assert!(!cell.skip);
340 }
341
342 #[test]
343 fn style() {
344 let cell = Cell::EMPTY;
345 assert_eq!(
346 cell.style(),
347 Style {
348 fg: Some(Color::Reset),
349 bg: Some(Color::Reset),
350 #[cfg(feature = "underline-color")]
351 underline_color: Some(Color::Reset),
352 add_modifier: Modifier::empty(),
353 sub_modifier: Modifier::empty(),
354 }
355 );
356 }
357
358 #[test]
359 fn default() {
360 let cell = Cell::default();
361 assert_eq!(cell.symbol(), " ");
362 }
363
364 #[test]
365 fn cell_eq() {
366 let cell1 = Cell::new("あ");
367 let cell2 = Cell::new("あ");
368 assert_eq!(cell1, cell2);
369 }
370
371 #[test]
372 fn cell_ne() {
373 let cell1 = Cell::new("あ");
374 let cell2 = Cell::new("い");
375 assert_ne!(cell1, cell2);
376 }
377}