1use crate::grid::{GridLine, GridLineFlags};
2
3use super::Screen;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct ScreenCellView {
8 pub(super) text: String,
9 pub(super) width: u8,
10 pub(super) padding: bool,
11 pub(super) attr: u16,
12 pub(super) fg: crate::input::Colour,
13 pub(super) bg: crate::input::Colour,
14 pub(super) us: crate::input::Colour,
15 pub(super) link: u32,
16}
17
18impl ScreenCellView {
19 #[must_use]
21 pub fn text(&self) -> &str {
22 &self.text
23 }
24
25 #[must_use]
27 pub const fn width(&self) -> u8 {
28 self.width
29 }
30
31 #[must_use]
33 pub const fn is_padding(&self) -> bool {
34 self.padding
35 }
36
37 #[must_use]
39 pub const fn attr(&self) -> u16 {
40 self.attr
41 }
42
43 #[must_use]
45 pub const fn fg(&self) -> crate::input::Colour {
46 self.fg
47 }
48
49 #[must_use]
51 pub const fn bg(&self) -> crate::input::Colour {
52 self.bg
53 }
54
55 #[must_use]
57 pub const fn us(&self) -> crate::input::Colour {
58 self.us
59 }
60
61 #[must_use]
63 pub const fn link(&self) -> u32 {
64 self.link
65 }
66}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
70pub struct ScreenLineView {
71 pub(super) cells: Vec<ScreenCellView>,
72 pub(super) wrapped: bool,
73 pub(super) start_prompt: bool,
74 pub(super) start_output: bool,
75 pub(super) time: i64,
76}
77
78impl ScreenLineView {
79 #[must_use]
81 pub fn cells(&self) -> &[ScreenCellView] {
82 &self.cells
83 }
84
85 #[must_use]
87 pub fn cell(&self, x: u32) -> Option<&ScreenCellView> {
88 self.cells.get(x as usize)
89 }
90
91 #[must_use]
93 pub const fn wrapped(&self) -> bool {
94 self.wrapped
95 }
96
97 #[must_use]
99 pub const fn start_prompt(&self) -> bool {
100 self.start_prompt
101 }
102
103 #[must_use]
105 pub const fn start_output(&self) -> bool {
106 self.start_output
107 }
108
109 #[must_use]
111 pub const fn time(&self) -> i64 {
112 self.time
113 }
114
115 #[must_use]
117 pub fn owning_cell_x(&self, x: u32) -> Option<u32> {
118 let cell = self.cell(x)?;
119 if !cell.is_padding() {
120 return Some(x);
121 }
122
123 let mut owner = x;
124 while owner > 0 {
125 owner -= 1;
126 let candidate = self.cell(owner)?;
127 if !candidate.is_padding() {
128 let width = u32::from(candidate.width().max(1));
129 if owner.saturating_add(width) > x {
130 return Some(owner);
131 }
132 return None;
133 }
134 }
135 None
136 }
137}
138
139impl Screen {
140 #[must_use]
142 pub fn absolute_line_view(&self, absolute_y: usize) -> Option<ScreenLineView> {
143 let line = self.grid.absolute_line(absolute_y)?;
144 Some(ScreenLineView {
145 cells: line
146 .cells()
147 .iter()
148 .map(|cell| ScreenCellView {
149 text: cell.text().to_owned(),
150 width: cell.width(),
151 padding: cell.is_padding(),
152 attr: cell.attr(),
153 fg: cell.fg(),
154 bg: cell.bg(),
155 us: cell.us(),
156 link: cell.link(),
157 })
158 .collect(),
159 wrapped: line.flags().contains(GridLineFlags::WRAPPED),
160 start_prompt: line.flags().contains(GridLineFlags::START_PROMPT),
161 start_output: line.flags().contains(GridLineFlags::START_OUTPUT),
162 time: line.time(),
163 })
164 }
165
166 #[must_use]
168 pub fn clone_viewport(&self, top_line: usize, cursor_x: u32, cursor_absolute_y: usize) -> Self {
169 let size = self.grid.size();
170 let rows = usize::from(size.rows.max(1));
171 let cols = u32::from(size.cols.max(1));
172 let total_lines = self.absolute_line_count();
173 let top_line = top_line.min(total_lines.saturating_sub(rows));
174 let mut viewport = Self::new(size, 0);
175
176 viewport.mode = self.mode;
177 viewport.cursor_style = self.cursor_style;
178 viewport.title = self.title.clone();
179 viewport.window_name = self.window_name.clone();
180 viewport.path = self.path.clone();
181 viewport.title_stack = self.title_stack.clone();
182 viewport.hyperlinks = self.hyperlinks.clone();
183 viewport.active_hyperlink = self.active_hyperlink;
184 viewport.bell_count = 0;
185 viewport.utf8_config = self.utf8_config.clone();
186
187 let lines = (0..rows)
188 .map(|offset| {
189 self.grid
190 .absolute_line(top_line + offset)
191 .cloned()
192 .unwrap_or_else(|| GridLine::new(cols))
193 })
194 .collect();
195 viewport.grid.replace_visible(lines);
196
197 viewport.cursor_x = cursor_x.min(viewport.max_cursor_x());
198 viewport.cursor_y = if (top_line..top_line + rows).contains(&cursor_absolute_y) {
199 (cursor_absolute_y - top_line) as u32
200 } else {
201 0
202 };
203 viewport.pending_wrap = false;
204 viewport.saved_cursor_x = None;
205 viewport.saved_cursor_y = None;
206 viewport.saved_cursor_pending_wrap = false;
207 viewport.saved_grid = None;
208 viewport.rupper = 0;
209 viewport.rlower = u32::from(size.rows.max(1)).saturating_sub(1);
210 viewport.reset_tabs();
211 viewport
212 }
213}