urushi/view/canvas/
cell.rs1use std::fmt;
2
3use unicode_segmentation::UnicodeSegmentation;
4
5use crate::{Grapheme, TextStyle};
6
7use super::Position;
8
9#[derive(Clone, Copy)]
11pub enum Composition {
12 Replace,
13 Overlay,
14 Custom(fn(&CanvasCell, &CellContribution) -> CanvasCell),
15}
16
17impl fmt::Debug for Composition {
18 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19 match self {
20 Self::Replace => f.write_str("Replace"),
21 Self::Overlay => f.write_str("Overlay"),
22 Self::Custom(_) => f.write_str("Custom(..)"),
23 }
24 }
25}
26
27#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct CanvasCell {
30 symbol: String,
31 style: TextStyle,
32}
33
34impl CanvasCell {
35 pub fn new(symbol: &Grapheme, style: TextStyle) -> Self {
39 validate_symbol(symbol.as_str());
40 Self {
41 symbol: symbol.as_str().to_owned(),
42 style,
43 }
44 }
45
46 pub fn get_symbol(&self) -> &str {
47 &self.symbol
48 }
49
50 pub const fn get_style(&self) -> &TextStyle {
51 &self.style
52 }
53
54 pub fn symbol(mut self, symbol: &Grapheme) -> Self {
56 validate_symbol(symbol.as_str());
57 self.symbol = symbol.as_str().to_owned();
58 self
59 }
60
61 pub fn style(mut self, style: TextStyle) -> Self {
62 self.style = style;
63 self
64 }
65}
66
67#[derive(Debug, Clone, Default, PartialEq, Eq)]
69pub struct CellContribution {
70 symbol: Option<String>,
71 style: Option<TextStyle>,
72}
73
74impl CellContribution {
75 pub fn new() -> Self {
76 Self::default()
77 }
78
79 pub fn symbol(mut self, symbol: &Grapheme) -> Self {
81 validate_symbol(symbol.as_str());
82 self.symbol = Some(symbol.as_str().to_owned());
83 self
84 }
85
86 pub fn style(mut self, style: TextStyle) -> Self {
87 self.style = Some(style);
88 self
89 }
90
91 pub fn get_symbol(&self) -> Option<&str> {
92 self.symbol.as_deref()
93 }
94
95 pub fn get_style(&self) -> Option<&TextStyle> {
96 self.style.as_ref()
97 }
98}
99
100#[derive(Debug, Clone, PartialEq, Eq)]
102pub struct PositionedCell {
103 pub position: Position,
104 pub contribution: CellContribution,
105}
106
107impl PositionedCell {
108 pub const fn new(position: Position, contribution: CellContribution) -> Self {
109 Self {
110 position,
111 contribution,
112 }
113 }
114}
115
116pub(super) fn validate_symbol(symbol: &str) {
117 assert!(
118 !symbol.chars().any(char::is_control) && symbol.graphemes(true).count() == 1,
119 "a Canvas cell symbol must be exactly one printable grapheme: {symbol:?}"
120 );
121}
122
123pub(super) fn validate_cell_glyph(symbol: &str) {
124 validate_symbol(symbol);
125 assert_eq!(
126 Grapheme::new(symbol).width(),
127 1,
128 "a rasterized cell glyph must occupy exactly one terminal cell: {symbol:?}"
129 );
130}