diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index 4a2e57c..8e09412 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -511,7 +511,7 @@ impl Display { { let glyph_cache = &mut self.glyph_cache; - let mut rects = Rects::new(&metrics, &size_info); + let mut rects = Rects::new(); // Draw grid { @@ -521,7 +521,7 @@ impl Display { // Iterate over all non-empty cells in the grid for cell in grid_cells { // Update underline/strikeout - rects.update_lines(&size_info, &cell); + rects.update_lines(&cell, &size_info, &metrics); // Draw the cell api.render_cell(cell, glyph_cache); diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs index b4f8a01..a2844c0 100644 --- a/alacritty_terminal/src/renderer/rects.rs +++ b/alacritty_terminal/src/renderer/rects.rs @@ -11,6 +11,8 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +use std::collections::HashMap; + use font::Metrics; use crate::index::Point; @@ -32,125 +34,104 @@ impl Rect { } } -#[derive(Debug)] struct Line { - flag: Flags, - range: Option<(RenderableCell, Point)>, + rect: Rect, + start: Point, + color: Rgb, } impl Line { - fn new(flag: Flags) -> Self { - Self { flag, range: None } + /// Create a line that starts on the left of `cell` and is one cell wide + fn from_cell(cell: &RenderableCell, flag: Flags, metrics: &Metrics, size: &SizeInfo) -> Line { + let cell_x = cell.column.0 as f32 * size.cell_width; + + let (position, mut height) = match flag { + Flags::UNDERLINE => (metrics.underline_position, metrics.underline_thickness), + Flags::STRIKEOUT => (metrics.strikeout_position, metrics.strikeout_thickness), + _ => unimplemented!("Invalid flag for cell line drawing specified"), + }; + + // Make sure lines are always visible + height = height.max(1.); + + let cell_bottom = (cell.line.0 as f32 + 1.) * size.cell_height; + let baseline = cell_bottom + metrics.descent; + + let mut y = baseline - position - height / 2.; + let max_y = cell_bottom - height; + if y > max_y { + y = max_y; + } + + let rect = Rect::new(cell_x + size.padding_x, y + size.padding_y, size.cell_width, height); + + Self { start: cell.into(), color: cell.fg, rect } + } + + fn update_end(&mut self, end: Point, size: &SizeInfo) { + self.rect.width = (end.col + 1 - self.start.col).0 as f32 * size.cell_width; } } /// Rects for underline, strikeout and more. -pub struct Rects<'a> { - inner: Vec<(Rect, Rgb)>, - active_lines: Vec, - metrics: &'a Metrics, - size: &'a SizeInfo, +#[derive(Default)] +pub struct Rects { + inner: HashMap>, } -impl<'a> Rects<'a> { - pub fn new(metrics: &'a Metrics, size: &'a SizeInfo) -> Self { - let active_lines = vec![Line::new(Flags::UNDERLINE), Line::new(Flags::STRIKEOUT)]; - Self { inner: Vec::new(), active_lines, metrics, size } +impl Rects { + pub fn new() -> Self { + Self::default() } /// Convert the stored rects to rectangles for the renderer. - pub fn rects(&self) -> &Vec<(Rect, Rgb)> { - &self.inner + pub fn rects(&self) -> Vec<(Rect, Rgb)> { + self.inner + .iter() + .map(|(_, lines)| lines) + .flatten() + .map(|line| (line.rect, line.color)) + .collect() } /// Update the stored lines with the next cell info. - pub fn update_lines(&mut self, size_info: &SizeInfo, cell: &RenderableCell) { - for line in self.active_lines.iter_mut() { - match line.range { - // Check for end if line is present - Some((ref mut start, ref mut end)) => { - // No change in line - if cell.line == start.line - && cell.flags.contains(line.flag) - && cell.fg == start.fg - && cell.column == end.col + 1 - { - if size_info.cols() == cell.column && size_info.lines() == cell.line { - // Add the last rect if we've reached the end of the terminal - self.inner.push(create_rect( - &start, - cell.into(), - line.flag, - &self.metrics, - &self.size, - )); - } else { - // Update the length of the line - *end = cell.into(); - } + pub fn update_lines(&mut self, cell: &RenderableCell, size: &SizeInfo, metrics: &Metrics) { + for flag in &[Flags::UNDERLINE, Flags::STRIKEOUT] { + if !cell.flags.contains(*flag) { + continue; + } - continue; - } + // Check if there's an active line + if let Some(line) = self.inner.get_mut(flag).and_then(|lines| lines.last_mut()) { + if cell.line == line.start.line && cell.fg == line.color { + // Update the length of the line + line.update_end(cell.into(), size); - self.inner.push(create_rect(start, *end, line.flag, &self.metrics, &self.size)); + continue; + } + } - // Start a new line if the flag is present - if cell.flags.contains(line.flag) { - *start = cell.clone(); - *end = cell.into(); - } else { - line.range = None; - } - }, - // Check for new start of line + // Start new line if there currently is none + let rect = Line::from_cell(cell, *flag, metrics, size); + match self.inner.get_mut(flag) { + Some(lines) => lines.push(rect), None => { - if cell.flags.contains(line.flag) { - line.range = Some((cell.clone(), cell.into())); - } + self.inner.insert(*flag, vec![rect]); }, - }; + } } } // Add a rectangle pub fn push(&mut self, rect: Rect, color: Rgb) { - self.inner.push((rect, color)); + let line = Line { start: Point::default(), color, rect }; + + // Flag `HIDDEN` for hashmap index is arbitrary + match self.inner.get_mut(&Flags::HIDDEN) { + Some(lines) => lines.push(line), + None => { + self.inner.insert(Flags::HIDDEN, vec![line]); + }, + } } } - -/// Create a rectangle that starts on the left of `start` and ends on the right -/// of `end`, based on the given flag and size metrics. -fn create_rect( - start: &RenderableCell, - end: Point, - flag: Flags, - metrics: &Metrics, - size: &SizeInfo, -) -> (Rect, Rgb) { - let start_x = start.column.0 as f32 * size.cell_width; - let end_x = (end.col.0 + 1) as f32 * size.cell_width; - let width = end_x - start_x; - - let (position, mut height) = match flag { - Flags::UNDERLINE => (metrics.underline_position, metrics.underline_thickness), - Flags::STRIKEOUT => (metrics.strikeout_position, metrics.strikeout_thickness), - _ => unimplemented!("Invalid flag for cell line drawing specified"), - }; - - // Make sure lines are always visible - height = height.max(1.); - - let cell_bottom = (start.line.0 as f32 + 1.) * size.cell_height; - let baseline = cell_bottom + metrics.descent; - - let mut y = baseline - position - height / 2.; - let max_y = cell_bottom - height; - if y > max_y { - y = max_y; - } - - let rect = - Rect::new(start_x + size.padding_x, y.round() + size.padding_y, width, height.round()); - - (rect, start.fg) -}