1use compact_str::{CompactString, ToCompactString};
2use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
3
4use crate::{renderer::Rgba, Str};
5
6#[derive(Clone, PartialEq, Eq, Debug)]
7pub enum Cell {
8 Grapheme(Grapheme),
9 Pixel(Pixel),
10 Empty,
11 Continuation,
12}
13
14impl Cell {
15 pub fn set_fg(&mut self, fg: impl Into<Color>) {
16 match self {
17 Self::Grapheme(grapheme) => grapheme.fg = fg.into(),
18 Self::Pixel(pixel) => pixel.fg = fg.into(),
19 _ => {}
20 }
21 }
22
23 pub fn set_bg(&mut self, bg: impl Into<Color>) {
24 match self {
25 Self::Grapheme(grapheme) => grapheme.bg = bg.into(),
26 Self::Pixel(pixel) => pixel.bg = bg.into(),
27 _ => {}
28 }
29 }
30
31 pub fn set_attribute(&mut self, attribute: Attribute) {
32 match self {
33 Self::Grapheme(grapheme) => grapheme.attribute |= attribute,
34 Self::Pixel(pixel) => pixel.attribute |= attribute,
35 _ => {}
36 }
37 }
38}
39
40impl Cell {
41 pub(crate) fn is_same(&self, other: &Self) -> bool {
42 fn check(fg: Color, bg: Color) -> bool {
43 matches!(bg, Color::Reuse) || matches!(fg, Color::Reuse)
44 }
45
46 match (self, other) {
47 (Cell::Grapheme(left), Cell::Grapheme(right)) => {
48 (left == right) || (left.cluster == right.cluster && check(right.bg, right.fg))
49 }
50 (Cell::Grapheme(left), Cell::Pixel(right)) => {
51 compare(&left.cluster, right.char) && check(right.bg, right.fg)
52 }
53 (Cell::Pixel(left), Cell::Grapheme(right)) => {
54 compare(&right.cluster, left.char) && check(right.bg, right.fg)
55 }
56 (Cell::Pixel(left), Cell::Pixel(right)) => {
57 (left == right) || ((left.char == right.char) && check(right.bg, right.fg))
58 }
59 (Cell::Empty, Cell::Grapheme(..) | Cell::Pixel(..)) => false,
60 (Cell::Empty, Cell::Continuation | Cell::Empty) => true,
61 _ => false,
62 }
63 }
64
65 pub(crate) fn merge(mut this: &mut Self, other: Self) {
66 fn merge_fg(left_fg: &mut Color, right_fg: Color) {
67 if let (Color::Reset | Color::Set(..), ..) = (right_fg, &left_fg) {
68 *left_fg = right_fg
69 }
70 }
71
72 fn merge_bg(left_bg: &mut Color, right_bg: Color) {
73 match (right_bg, &left_bg) {
74 (Color::Set(a), Color::Set(b)) => *left_bg = Color::Set(a.blend_alpha(*b)),
75 (Color::Reset | Color::Set(..), ..) => *left_bg = right_bg,
76 _ => {}
77 }
78 }
79
80 match (&mut this, other) {
81 (Cell::Grapheme(ref mut left), Cell::Grapheme(mut right)) => {
82 merge_fg(&mut left.fg, right.fg);
83 merge_bg(&mut left.bg, right.bg);
84 left.attribute = right.attribute;
85 left.cluster = std::mem::take(&mut right.cluster);
86 }
87 (Cell::Grapheme(ref mut left), Cell::Pixel(right)) => {
88 merge_fg(&mut left.fg, right.fg);
89 merge_bg(&mut left.bg, right.bg);
90 let pixel = Pixel {
91 char: right.char,
92 fg: left.fg,
93 bg: left.bg,
94 attribute: right.attribute,
95 };
96 *this = Cell::Pixel(pixel)
97 }
98 (Cell::Pixel(ref mut left), Cell::Grapheme(mut right)) => {
99 merge_fg(&mut left.fg, right.fg);
100 merge_bg(&mut left.bg, right.bg);
101 let grapheme = Grapheme {
102 cluster: std::mem::take(&mut right.cluster),
103 fg: left.fg,
104 bg: left.bg,
105 attribute: right.attribute,
106 };
107 *this = Cell::Grapheme(grapheme)
108 }
109
110 (Cell::Pixel(ref mut left), Cell::Pixel(right)) => {
111 merge_fg(&mut left.fg, right.fg);
112 merge_bg(&mut left.bg, right.bg);
113 left.attribute = right.attribute;
114 left.char = right.char;
115 }
116
117 (_, right @ (Cell::Grapheme(..) | Cell::Pixel(..))) => *this = right,
118 _ => {}
119 }
120 }
121
122 pub(crate) fn width(&self) -> usize {
123 match self {
124 Self::Grapheme(grapheme) => grapheme.cluster.width(),
125 Self::Pixel(pixel) => pixel.char.width().unwrap_or(0),
126 Self::Empty | Self::Continuation => 0,
127 }
128 }
129
130 pub(crate) const fn is_continuation(&self) -> bool {
131 matches!(self, Self::Continuation)
132 }
133
134 pub(crate) const fn is_empty(&self) -> bool {
135 matches!(self, Self::Empty)
136 }
137
138 pub(crate) const fn fg(&self) -> Color {
139 match self {
140 Self::Grapheme(grapheme) => grapheme.fg,
141 Self::Pixel(pixel) => pixel.fg,
142 _ => unreachable!(),
143 }
144 }
145
146 pub(crate) const fn bg(&self) -> Color {
147 match self {
148 Self::Grapheme(grapheme) => grapheme.bg,
149 Self::Pixel(pixel) => pixel.bg,
150 _ => unreachable!(),
151 }
152 }
153
154 pub(crate) const fn attribute(&self) -> Attribute {
155 match self {
156 Self::Grapheme(grapheme) => grapheme.attribute,
157 Self::Pixel(pixel) => pixel.attribute,
158 _ => unreachable!(),
159 }
160 }
161}
162
163impl Default for Cell {
164 fn default() -> Self {
165 Self::Empty
166 }
167}
168
169impl From<Grapheme> for Cell {
181 fn from(value: Grapheme) -> Self {
182 Self::Grapheme(value)
183 }
184}
185
186impl From<Pixel> for Cell {
187 fn from(value: Pixel) -> Self {
188 Self::Pixel(value)
189 }
190}
191
192impl From<Rgba> for Cell {
193 fn from(bg: Rgba) -> Self {
194 Cell::Pixel(Pixel::from(bg))
195 }
196}
197
198impl<T: ToCompactString> From<T> for Cell {
199 fn from(value: T) -> Self {
200 Self::Grapheme(Grapheme::from(value))
201 }
202}
203
204#[derive(Copy, Clone, Debug, PartialEq, Eq)]
205pub struct Pixel {
206 pub(crate) char: char,
207 pub(crate) fg: Color,
208 pub(crate) bg: Color,
209 attribute: Attribute,
210}
211
212impl Default for Pixel {
213 fn default() -> Self {
214 Self::DEFAULT
215 }
216}
217
218impl From<Rgba> for Pixel {
219 fn from(value: Rgba) -> Self {
220 Self::new(' ').bg(value)
221 }
222}
223
224pub(crate) fn compare(left: &str, right: char) -> bool {
225 let mut b: [u8; 4] = [0; 4];
226 left == right.encode_utf8(&mut b)
227}
228
229impl Pixel {
230 pub(crate) const DEFAULT: Self = Self {
231 char: ' ',
232 fg: Color::Reset,
233 bg: Color::Reset,
234 attribute: Attribute::RESET,
235 };
236
237 pub const fn new(char: char) -> Self {
238 Self {
239 char,
240 fg: Color::Reset,
241 bg: Color::Reuse,
242 attribute: Attribute::RESET,
243 }
244 }
245
246 pub const fn char(mut self, char: char) -> Self {
247 self.char = char;
248 self
249 }
250
251 pub fn fg(mut self, fg: impl Into<Color>) -> Self {
252 self.fg = fg.into();
253 self
254 }
255
256 pub fn bg(mut self, bg: impl Into<Color>) -> Self {
257 self.bg = bg.into();
258 self
259 }
260
261 pub fn attribute(mut self, attribute: Attribute) -> Self {
262 self.attribute = attribute;
263 self
264 }
265}
266
267impl From<char> for Pixel {
268 fn from(char: char) -> Self {
269 Self::new(char)
270 }
271}
272
273#[derive(Clone, Debug, PartialEq, Eq)]
274pub struct Grapheme {
275 pub(crate) cluster: CompactString,
276 pub(crate) fg: Color,
277 pub(crate) bg: Color,
278 attribute: Attribute,
279}
280
281impl Grapheme {
282 pub const fn const_new(str: &'static str) -> Self {
283 Self {
284 cluster: CompactString::const_new(str),
285 fg: Color::Reset,
286 bg: Color::Reuse,
287 attribute: Attribute::RESET,
288 }
289 }
290
291 pub fn new(data: impl Into<Str>) -> Self {
292 Self {
293 cluster: data.into().into_inner(),
294 fg: Color::Reset,
295 bg: Color::Reuse,
296 attribute: Attribute::RESET,
297 }
298 }
299
300 pub fn fg(mut self, fg: impl Into<Color>) -> Self {
301 self.fg = fg.into();
302 self
303 }
304
305 pub fn bg(mut self, bg: impl Into<Color>) -> Self {
306 self.bg = bg.into();
307 self
308 }
309
310 pub fn attribute(mut self, attribute: Attribute) -> Self {
311 self.attribute = attribute;
312 self
313 }
314}
315
316impl<T: ToCompactString> From<T> for Grapheme {
317 fn from(value: T) -> Self {
318 Self::new(value)
319 }
320}
321
322#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Eq, Ord, Hash)]
342pub struct Attribute(pub u16);
343
344impl Attribute {
345 pub const RESET: Self = Self(0);
346 pub const BOLD: Self = Self(1 << 0);
347 pub const FAINT: Self = Self(1 << 1);
348 pub const ITALIC: Self = Self(1 << 2);
349 pub const UNDERLINE: Self = Self(1 << 3);
350 pub const BLINK: Self = Self(1 << 4);
351 pub const REVERSE: Self = Self(1 << 6);
352 pub const STRIKEOUT: Self = Self(1 << 8);
353}
354
355impl Attribute {
356 pub const fn is_reset(&self) -> bool {
357 self.0 == 0
358 }
359
360 pub const fn is_bold(&self) -> bool {
361 self.0 & (1 << 0) != 0
362 }
363
364 pub const fn is_faint(&self) -> bool {
365 self.0 & (1 << 1) != 0
366 }
367
368 pub const fn is_italic(&self) -> bool {
369 self.0 & (1 << 2) != 0
370 }
371
372 pub const fn is_underline(&self) -> bool {
373 self.0 & (1 << 3) != 0
374 }
375
376 pub const fn is_blink(&self) -> bool {
377 self.0 & (1 << 4) != 0
378 }
379
380 pub const fn is_reverse(&self) -> bool {
381 self.0 & (1 << 6) != 0
382 }
383
384 pub const fn is_strikeout(&self) -> bool {
385 self.0 & (1 << 8) != 0
386 }
387}
388
389impl std::ops::BitAnd for Attribute {
390 type Output = Self;
391 fn bitand(self, rhs: Self) -> Self::Output {
392 Self(self.0 & rhs.0)
393 }
394}
395impl std::ops::BitAndAssign for Attribute {
396 fn bitand_assign(&mut self, rhs: Self) {
397 *self = *self & rhs
398 }
399}
400
401impl std::ops::BitOr for Attribute {
402 type Output = Self;
403 fn bitor(self, rhs: Self) -> Self::Output {
404 Self(self.0 | rhs.0)
405 }
406}
407impl std::ops::BitOrAssign for Attribute {
408 fn bitor_assign(&mut self, rhs: Self) {
409 *self = *self | rhs
410 }
411}
412
413impl std::ops::Not for Attribute {
414 type Output = Self;
415 fn not(self) -> Self::Output {
416 Self(!self.0)
417 }
418}
419
420impl std::fmt::Debug for Attribute {
421 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
422 const FIELDS: [&str; 9] = [
423 "Bold",
424 "Faint",
425 "Italic",
426 "Underline",
427 "Blink",
428 "", "Reverse",
430 "", "Strikeout",
432 ];
433
434 let mut seen = false;
435 for (flag, repr) in (0..).zip(FIELDS) {
436 if repr.is_empty() {
437 continue;
438 }
439
440 if (self.0 >> flag) & 1 == 1 {
441 if seen {
442 f.write_str(" | ")?;
443 }
444 f.write_str(repr)?;
445 seen |= true
446 }
447 }
448
449 if !seen || self.0 == 0 {
450 f.write_str("Reset")?;
451 }
452
453 Ok(())
454 }
455}
456
457impl std::str::FromStr for Attribute {
458 type Err = String;
459 fn from_str(input: &str) -> Result<Self, Self::Err> {
460 let mut this = Self::RESET;
461 for part in input.split_terminator('+').map(<str>::trim) {
462 this |= match part {
463 s if s.eq_ignore_ascii_case("bold") => Self::BOLD,
464 s if s.eq_ignore_ascii_case("faint") => Self::FAINT,
465 s if s.eq_ignore_ascii_case("italic") => Self::ITALIC,
466 s if s.eq_ignore_ascii_case("underline") => Self::UNDERLINE,
467 s if s.eq_ignore_ascii_case("blink") => Self::BLINK,
468 s if s.eq_ignore_ascii_case("reverse") => Self::REVERSE,
469 s if s.eq_ignore_ascii_case("strikeout") => Self::STRIKEOUT,
470 attr => return Err(format!("unknown attribute: {attr}")),
471 }
472 }
473 Ok(this)
474 }
475}
476
477#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
479pub enum Color {
480 Set(Rgba),
482 #[default]
483 Reuse,
485 Reset,
487}
488
489impl From<&'static str> for Color {
490 fn from(value: &'static str) -> Self {
491 Self::Set(Rgba::hex(value))
492 }
493}
494
495impl From<Rgba> for Color {
496 fn from(value: Rgba) -> Self {
497 Self::Set(value)
498 }
499}
500
501impl<T: Into<Rgba>> From<Option<T>> for Color {
502 fn from(value: Option<T>) -> Self {
503 value.map_or(Self::Reset, |val| Self::Set(val.into()))
504 }
505}