1use crate::console::{Console, ConsoleOptions};
9use crate::protocol::Renderable;
10use crate::segment::Segment;
11use crate::style::{Style, StyleType};
12
13pub struct Screen {
16 renderables: Vec<Box<dyn Renderable>>,
18 style: Option<StyleType>,
19 application_mode: bool,
20}
21
22impl Screen {
23 pub fn new(renderable: Box<dyn Renderable>) -> Self {
25 Screen::from_renderables(vec![renderable])
26 }
27
28 pub fn from_renderables(renderables: Vec<Box<dyn Renderable>>) -> Self {
31 Screen {
32 renderables,
33 style: None,
34 application_mode: false,
35 }
36 }
37
38 pub fn empty() -> Self {
40 Screen::from_renderables(Vec::new())
41 }
42
43 pub fn style(mut self, style: impl Into<StyleType>) -> Self {
47 self.style = Some(style.into());
48 self
49 }
50
51 pub fn application_mode(mut self, application_mode: bool) -> Self {
54 self.application_mode = application_mode;
55 self
56 }
57}
58
59struct ScreenGroup<'a>(&'a [Box<dyn Renderable>]);
62
63impl Renderable for ScreenGroup<'_> {
64 fn rich_render(&self, console: &Console, options: &ConsoleOptions) -> Vec<Segment> {
65 let mut segments = Vec::new();
66 let mut first = true;
67 for renderable in self.0 {
68 let rendered = console.render(renderable.as_ref(), Some(options));
69 if rendered.is_empty() {
70 continue;
71 }
72 if !first {
73 segments.push(Segment::line());
74 }
75 first = false;
76 segments.extend(rendered);
77 }
78 segments
79 }
80}
81
82impl Renderable for Screen {
83 fn rich_render(&self, console: &Console, options: &ConsoleOptions) -> Vec<Segment> {
84 let style = self
85 .style
86 .as_ref()
87 .map(|style| console.get_style(style).unwrap_or_default())
88 .filter(|style| !style.is_null());
89 render_screen_with(
90 &ScreenGroup(&self.renderables),
91 style,
92 self.application_mode,
93 console,
94 options,
95 )
96 }
97
98 fn print_end_line(&self, _options: &ConsoleOptions) -> bool {
101 false
102 }
103}
104
105pub(crate) fn render_screen(
108 renderable: Option<&dyn Renderable>,
109 style: Option<&Style>,
110 console: &Console,
111 options: &ConsoleOptions,
112) -> Vec<Segment> {
113 let content: &dyn Renderable = match renderable {
115 Some(renderable) => renderable,
116 None => &ScreenGroup(&[]),
117 };
118 render_screen_with(content, style.cloned(), false, console, options)
119}
120
121fn render_screen_with(
123 content: &dyn Renderable,
124 style: Option<Style>,
125 application_mode: bool,
126 console: &Console,
127 options: &ConsoleOptions,
128) -> Vec<Segment> {
129 let width = options.size.width;
131 let height = options.size.height;
132 let render_options = options.update_dimensions(width, height);
135 let lines = console.render_lines_styled(content, &render_options, style.as_ref(), true);
136 let pad_style = style.clone();
138 let mut lines: Vec<Vec<Segment>> = lines
139 .into_iter()
140 .take(height)
141 .map(|line| Segment::adjust_line_length(&line, width, pad_style.clone()))
142 .collect();
143 while lines.len() < height {
144 lines.push(vec![Segment::new(" ".repeat(width), pad_style.clone())]);
145 }
146
147 let new_line = if application_mode {
148 Segment::new("\n\r", None)
149 } else {
150 Segment::line()
151 };
152 let mut segments = Vec::new();
153 let last = lines.len().saturating_sub(1);
154 for (index, line) in lines.into_iter().enumerate() {
155 segments.extend(line);
156 if index != last {
157 segments.push(new_line.clone());
158 }
159 }
160 segments
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166 use crate::color::ColorSystem;
167 use crate::text::Text;
168
169 fn console(width: usize, height: usize) -> Console {
170 Console::builder()
171 .force_terminal(true)
172 .color_system(Some(ColorSystem::Truecolor))
173 .width(width)
174 .height(height)
175 .build()
176 }
177
178 #[test]
179 fn fills_to_width_and_height() {
180 let screen = Screen::new(Box::new(Text::new("hi")));
181 let out = console(6, 3).capture(|c| c.print(&screen));
182 assert_eq!(out, "hi \n \n ");
185 }
186
187 #[test]
188 fn empty_screen_is_blank_rectangle() {
189 let out = console(4, 2).capture(|c| c.print(&Screen::empty()));
190 assert_eq!(out, " \n ");
191 }
192}