1use super::super::{Grid, Pos, TileExtra, to_grixy_pos};
5#[cfg(test)]
6use crate::color::Style;
7use crate::color::Tint;
8#[cfg(test)]
9use crate::tile::Tile;
10use crate::tile::TileFlags;
11use grixy::ops::GridRead;
12
13impl Grid {
14 pub(crate) fn set_extra(&mut self, layer: u8, x: u16, y: u16, extra: TileExtra) {
22 if x >= self.width || y >= self.height {
23 return;
24 }
25 let pos = to_grixy_pos(Pos::new(x, y));
26 let idx = usize::from(y) * usize::from(self.width) + usize::from(x);
27 let lb = self.layer_or_alloc(layer);
28 if extra.is_empty() {
29 lb.buf[pos].flags.remove(TileFlags::HAS_EXTRA);
30 lb.extras.remove(&idx);
31 } else {
32 lb.buf[pos].flags.insert(TileFlags::HAS_EXTRA);
33 lb.extras.insert(idx, extra);
34 }
35 }
36
37 #[must_use]
49 pub fn tint(&self, layer: u8, x: u16, y: u16) -> Tint {
50 let Some(lb) = self.layer(layer) else {
51 return Tint::None;
52 };
53 let Some(tile) = lb.buf.get(to_grixy_pos(Pos::new(x, y))) else {
54 return Tint::None;
55 };
56 let idx = usize::from(y) * usize::from(self.width) + usize::from(x);
57 lb.tint_for(idx, tile)
58 }
59
60 pub fn set_tint(&mut self, layer: u8, x: u16, y: u16, tint: Tint) {
70 if x >= self.width || y >= self.height {
71 return;
72 }
73 let idx = usize::from(y) * usize::from(self.width) + usize::from(x);
74 let pos = to_grixy_pos(Pos::new(x, y));
75 let lb = self.layer_or_alloc(layer);
76 let grapheme = if lb.buf[pos].flags.contains(TileFlags::HAS_EXTRA) {
79 lb.extras.get(&idx).and_then(|e| e.grapheme.clone())
80 } else {
81 None
82 };
83 let entry = TileExtra { grapheme, tint };
84 if entry.is_empty() {
85 lb.buf[pos].flags.remove(TileFlags::HAS_EXTRA);
86 lb.extras.remove(&idx);
87 } else {
88 lb.buf[pos].flags.insert(TileFlags::HAS_EXTRA);
89 lb.extras.insert(idx, entry);
90 }
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[cfg(feature = "egc")]
105 #[test]
106 fn tint_round_trips_and_defaults_to_none() {
107 let mut g = Grid::new(4, 4);
108 assert_eq!(g.tint(0, 1, 1), Tint::None);
109
110 g.write_grapheme(0, 1, 1, "@", Style::default());
111 g.set_tint(0, 1, 1, Tint::multiply(128, 64, 32));
112 assert_eq!(g.tint(0, 1, 1), Tint::multiply(128, 64, 32));
113
114 g.set_tint(0, 1, 1, Tint::None);
116 assert_eq!(g.tint(0, 1, 1), Tint::None);
117 }
118
119 #[test]
120 fn tint_is_per_layer_and_per_cell() {
121 let mut g = Grid::new(4, 4);
122 g.set_tint(0, 1, 1, Tint::multiply(10, 20, 30));
123 g.set_tint(3, 1, 1, Tint::mix(1, 2, 3, 4));
124
125 assert_eq!(g.tint(0, 1, 1), Tint::multiply(10, 20, 30));
126 assert_eq!(g.tint(3, 1, 1), Tint::mix(1, 2, 3, 4));
127 assert_eq!(g.tint(0, 1, 2), Tint::None);
128 assert_eq!(g.tint(1, 1, 1), Tint::None);
129 }
130
131 #[test]
132 fn tint_out_of_bounds_reads_none_and_writes_nothing() {
133 let mut g = Grid::new(2, 2);
134 g.set_tint(0, 9, 9, Tint::multiply(1, 2, 3));
135 assert_eq!(g.tint(0, 9, 9), Tint::None);
136 assert_eq!(g.tint(0, 0, 0), Tint::None);
137 }
138
139 #[cfg(feature = "egc")]
140 #[test]
141 fn writing_a_glyph_over_a_tinted_cell_drops_the_tint() {
142 let mut g = Grid::new(4, 4);
143 g.write_grapheme(0, 1, 1, "@", Style::default());
144 g.set_tint(0, 1, 1, Tint::multiply(128, 128, 128));
145
146 g.write_grapheme(0, 1, 1, "#", Style::default());
149 assert_eq!(g.tint(0, 1, 1), Tint::None);
150 }
151
152 #[test]
153 fn put_tile_drops_the_tint() {
154 let mut g = Grid::new(4, 4);
155 g.set_tint(0, 1, 1, Tint::multiply(128, 128, 128));
156 g.put_tile(0, Pos::new(1, 1), Tile::new('x', Style::default()));
157 assert_eq!(g.tint(0, 1, 1), Tint::None);
158 }
159
160 #[cfg(feature = "egc")]
161 #[test]
162 fn a_tint_and_a_grapheme_share_one_entry_without_clobbering_each_other() {
163 let mut g = Grid::new(4, 4);
164 g.write_grapheme(0, 1, 1, "e\u{0301}", Style::default());
165 g.set_tint(0, 1, 1, Tint::multiply(128, 128, 128));
166
167 assert_eq!(crate::grid::grapheme_at(&g, 0, 1, 1), Some("e\u{0301}"));
169 assert_eq!(g.tint(0, 1, 1), Tint::multiply(128, 128, 128));
170
171 g.set_tint(0, 1, 1, Tint::None);
173 assert_eq!(crate::grid::grapheme_at(&g, 0, 1, 1), Some("e\u{0301}"));
174 assert_eq!(g.tint(0, 1, 1), Tint::None);
175 }
176
177 #[cfg(feature = "egc")]
178 #[test]
179 fn a_tint_alone_keeps_grapheme_reads_answering_none() {
180 let mut g = Grid::new(4, 4);
181 g.write_grapheme(0, 1, 1, "@", Style::default());
182 g.set_tint(0, 1, 1, Tint::multiply(128, 128, 128));
183
184 assert_eq!(crate::grid::grapheme_at(&g, 0, 1, 1), None);
187 assert_eq!(g.tint(0, 1, 1), Tint::multiply(128, 128, 128));
188 }
189}