1use crate::console::{ConsoleOptions, DynRenderable, RenderResult, Renderable};
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 { items: Vec::new() }
142 }
143
144 pub fn add(&mut self, renderable: impl Renderable + Send + Sync + 'static) -> &mut Self {
146 self.items.push(DynRenderable::new(renderable));
147 self
148 }
149}
150
151impl Renderable for Renderables {
152 fn render(&self, options: &ConsoleOptions) -> RenderResult {
153 let mut all_lines: Vec<Vec<Segment>> = Vec::new();
154 for item in &self.items {
155 let result = item.render(options);
156 all_lines.extend(result.lines);
157 }
158 RenderResult {
159 lines: all_lines,
160 items: Vec::new(),
161 }
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use crate::console::ConsoleOptions;
169
170 #[test]
171 fn test_lines_empty() {
172 let lines = Lines::new();
173 let opts = ConsoleOptions::default();
174 let result = lines.render(&opts);
175 assert!(result.lines.is_empty());
176 }
177
178 #[test]
179 fn test_lines_with_content() {
180 let mut lines = Lines::new();
181 lines.add("Hello");
182 lines.add("World");
183 let opts = ConsoleOptions::default();
184 let result = lines.render(&opts);
185 assert_eq!(result.lines.len(), 2);
186 }
187
188 #[test]
189 fn test_lines_highlight() {
190 let mut lines = Lines::new().highlight(1);
191 lines.add("First");
192 lines.add("Highlighted");
193 lines.add("Third");
194 let opts = ConsoleOptions::default();
195 let result = lines.render(&opts);
196 assert_eq!(result.lines.len(), 3);
197 }
198
199 #[test]
200 fn test_renderables_empty() {
201 let items = Renderables::new();
202 let opts = ConsoleOptions::default();
203 let result = items.render(&opts);
204 assert!(result.lines.is_empty());
205 }
206
207 #[test]
208 fn test_renderables_with_content() {
209 let mut items = Renderables::new();
210 items.add("A");
211 items.add("B");
212 items.add("C");
213 let opts = ConsoleOptions::default();
214 let result = items.render(&opts);
215 assert_eq!(result.lines.len(), 3);
216 }
217}