1use crate::console::{ConsoleOptions, DynRenderable, Renderable, RenderResult};
8use crate::segment::Segment;
9use crate::style::Style;
10
11#[derive(Debug, Clone)]
30pub struct Lines {
31 lines: Vec<DynRenderable>,
32 highlight: Option<usize>,
33 style: Style,
34}
35
36impl Default for Lines {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42impl Lines {
43 pub fn new() -> Self {
45 Self {
46 lines: Vec::new(),
47 highlight: None,
48 style: Style::new(),
49 }
50 }
51
52 pub fn add(&mut self, renderable: impl Renderable + Send + Sync + 'static) -> &mut Self {
54 self.lines.push(DynRenderable::new(renderable));
55 self
56 }
57
58 pub fn highlight(mut self, index: usize) -> Self {
60 self.highlight = Some(index);
61 self
62 }
63
64 pub fn style(mut self, style: Style) -> Self {
66 self.style = style;
67 self
68 }
69}
70
71impl Renderable for Lines {
72 fn render(&self, options: &ConsoleOptions) -> RenderResult {
73 let mut all_lines: Vec<Vec<Segment>> = Vec::new();
74
75 for (i, item) in self.lines.iter().enumerate() {
76 let mut result = item.render(options);
77
78 if Some(i) == self.highlight {
80 for line in &mut result.lines {
81 for seg in line.iter_mut() {
82 if let Some(ref existing) = seg.style {
83 seg.style = Some(existing.clone().bold(true));
84 } else {
85 seg.style = Some(self.style.clone().bold(true));
86 }
87 }
88 }
89 } else if !self.style.is_plain() {
90 for line in &mut result.lines {
91 for seg in line.iter_mut() {
92 if seg.style.is_none() {
93 seg.style = Some(self.style.clone());
94 }
95 }
96 }
97 }
98
99 all_lines.extend(result.lines);
100 }
101
102 RenderResult {
103 lines: all_lines,
104 items: Vec::new(),
105 }
106 }
107}
108
109#[derive(Debug, Clone)]
128pub struct Renderables {
129 items: Vec<DynRenderable>,
130}
131
132impl Default for Renderables {
133 fn default() -> Self {
134 Self::new()
135 }
136}
137
138impl Renderables {
139 pub fn new() -> Self {
141 Self {
142 items: Vec::new(),
143 }
144 }
145
146 pub fn add(&mut self, renderable: impl Renderable + Send + Sync + 'static) -> &mut Self {
148 self.items.push(DynRenderable::new(renderable));
149 self
150 }
151}
152
153impl Renderable for Renderables {
154 fn render(&self, options: &ConsoleOptions) -> RenderResult {
155 let mut all_lines: Vec<Vec<Segment>> = Vec::new();
156 for item in &self.items {
157 let result = item.render(options);
158 all_lines.extend(result.lines);
159 }
160 RenderResult {
161 lines: all_lines,
162 items: Vec::new(),
163 }
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170 use crate::console::ConsoleOptions;
171
172 #[test]
173 fn test_lines_empty() {
174 let lines = Lines::new();
175 let opts = ConsoleOptions::default();
176 let result = lines.render(&opts);
177 assert!(result.lines.is_empty());
178 }
179
180 #[test]
181 fn test_lines_with_content() {
182 let mut lines = Lines::new();
183 lines.add("Hello");
184 lines.add("World");
185 let opts = ConsoleOptions::default();
186 let result = lines.render(&opts);
187 assert_eq!(result.lines.len(), 2);
188 }
189
190 #[test]
191 fn test_lines_highlight() {
192 let mut lines = Lines::new().highlight(1);
193 lines.add("First");
194 lines.add("Highlighted");
195 lines.add("Third");
196 let opts = ConsoleOptions::default();
197 let result = lines.render(&opts);
198 assert_eq!(result.lines.len(), 3);
199 }
200
201 #[test]
202 fn test_renderables_empty() {
203 let items = Renderables::new();
204 let opts = ConsoleOptions::default();
205 let result = items.render(&opts);
206 assert!(result.lines.is_empty());
207 }
208
209 #[test]
210 fn test_renderables_with_content() {
211 let mut items = Renderables::new();
212 items.add("A");
213 items.add("B");
214 items.add("C");
215 let opts = ConsoleOptions::default();
216 let result = items.render(&opts);
217 assert_eq!(result.lines.len(), 3);
218 }
219}