diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dd71e5..97f46d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Resize events are not send to the shell anymore if dimensions haven't changed - Minor performance issues with underline and strikeout checks - Rare bug which would extend underline and strikeout beyond the end of line +- Cursors not spanning two lines when over double-width characters +- Incorrect cursor dimensions if the font offset isn't `0` ## Version 0.3.0 diff --git a/Cargo.lock b/Cargo.lock index 43cee93..77e6be5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,7 +33,6 @@ dependencies = [ name = "alacritty" version = "0.3.0" dependencies = [ - "arraydeque 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -129,11 +128,6 @@ dependencies = [ "scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "arraydeque" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "arrayvec" version = "0.4.10" @@ -2863,7 +2857,6 @@ dependencies = [ "checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" "checksum arc-swap 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "bc4662175ead9cd84451d5c35070517777949a2ed84551764129cedb88384841" "checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" -"checksum arraydeque 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f0ffd3d69bd89910509a5d31d1f1353f38ccffdd116dd0099bbd6627f7bd8ad8" "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" diff --git a/Cargo.toml b/Cargo.toml index d1ad1bc..f0206d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,6 @@ log = "0.4" clap = "2" fnv = "1" unicode-width = "0.1" -arraydeque = "0.4" glutin = { version = "0.21.0-rc2", features = ["icon_loading"] } env_logger = "0.6.0" base64 = "0.10.0" diff --git a/font/src/lib.rs b/font/src/lib.rs index f23d0a0..38a6f82 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -213,6 +213,7 @@ impl ::std::ops::Add for Size { } } +#[derive(Clone)] pub struct RasterizedGlyph { pub c: char, pub width: i32, @@ -314,6 +315,7 @@ impl fmt::Debug for RasterizedGlyph { } } +#[derive(Copy, Clone)] pub struct Metrics { pub average_advance: f64, pub line_height: f64, diff --git a/src/cursor.rs b/src/cursor.rs new file mode 100644 index 0000000..268f35f --- /dev/null +++ b/src/cursor.rs @@ -0,0 +1,98 @@ +// Copyright 2016 Joe Wilm, The Alacritty Project Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +//! Helpers for creating different cursor glyphs from font metrics + +use std::cmp; + +use font::{Metrics, RasterizedGlyph}; + +use crate::ansi::CursorStyle; + +/// Width/Height of the cursor relative to the font width +pub const CURSOR_WIDTH_PERCENTAGE: i32 = 15; + +pub fn get_cursor_glyph( + cursor: CursorStyle, + metrics: Metrics, + offset_x: i8, + offset_y: i8, + is_wide: bool, +) -> RasterizedGlyph { + // Calculate the cell metrics + let height = metrics.line_height as i32 + i32::from(offset_y); + let mut width = metrics.average_advance as i32 + i32::from(offset_x); + let line_width = cmp::max(width * CURSOR_WIDTH_PERCENTAGE / 100, 1); + + // Double the cursor width if it's above a double-width glyph + if is_wide { + width *= 2; + } + + match cursor { + CursorStyle::HollowBlock => get_box_cursor_glyph(height, width, line_width), + CursorStyle::Underline => get_underline_cursor_glyph(width, line_width), + CursorStyle::Beam => get_beam_cursor_glyph(height, line_width), + CursorStyle::Block => get_block_cursor_glyph(height, width), + } +} + +// Returns a custom underline cursor character +pub fn get_underline_cursor_glyph(width: i32, line_width: i32) -> RasterizedGlyph { + // Create a new rectangle, the height is relative to the font width + let buf = vec![255u8; (width * line_width * 3) as usize]; + + // Create a custom glyph with the rectangle data attached to it + RasterizedGlyph { c: ' ', top: line_width, left: 0, height: line_width, width, buf } +} + +// Returns a custom beam cursor character +pub fn get_beam_cursor_glyph(height: i32, line_width: i32) -> RasterizedGlyph { + // Create a new rectangle that is at least one pixel wide + let buf = vec![255u8; (line_width * height * 3) as usize]; + + // Create a custom glyph with the rectangle data attached to it + RasterizedGlyph { c: ' ', top: height, left: 0, height, width: line_width, buf } +} + +// Returns a custom box cursor character +pub fn get_box_cursor_glyph(height: i32, width: i32, line_width: i32) -> RasterizedGlyph { + // Create a new box outline rectangle + let mut buf = Vec::with_capacity((width * height * 3) as usize); + for y in 0..height { + for x in 0..width { + if y < line_width + || y >= height - line_width + || x < line_width + || x >= width - line_width + { + buf.append(&mut vec![255u8; 3]); + } else { + buf.append(&mut vec![0u8; 3]); + } + } + } + + // Create a custom glyph with the rectangle data attached to it + RasterizedGlyph { c: ' ', top: height, left: 0, height, width, buf } +} + +// Returns a custom block cursor character +pub fn get_block_cursor_glyph(height: i32, width: i32) -> RasterizedGlyph { + // Create a completely filled glyph + let buf = vec![255u8; (width * height * 3) as usize]; + + // Create a custom glyph with the rectangle data attached to it + RasterizedGlyph { c: ' ', top: height, left: 0, height, width, buf } +} diff --git a/src/display.rs b/src/display.rs index 9e902b9..07ffdd0 100644 --- a/src/display.rs +++ b/src/display.rs @@ -435,10 +435,11 @@ impl Display { let size_info = *terminal.size_info(); let visual_bell_intensity = terminal.visual_bell.intensity(); let background_color = terminal.background_color(); + let metrics = self.glyph_cache.font_metrics(); let window_focused = self.window.is_focused; let grid_cells: Vec = - terminal.renderable_cells(config, window_focused).collect(); + terminal.renderable_cells(config, window_focused, metrics).collect(); // Get message from terminal to ignore modifications after lock is dropped let message_buffer = terminal.message_buffer_mut().message(); @@ -479,7 +480,6 @@ impl Display { { let glyph_cache = &mut self.glyph_cache; - let metrics = glyph_cache.font_metrics(); let mut rects = Rects::new(&metrics, &size_info); // Draw grid diff --git a/src/lib.rs b/src/lib.rs index 92ccb5a..10c1faa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,6 +31,7 @@ pub mod macros; pub mod ansi; pub mod cli; pub mod config; +mod cursor; pub mod display; pub mod event; pub mod event_loop; diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 4785122..5182a6f 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -32,7 +32,7 @@ use crate::gl::types::*; use crate::index::{Column, Line}; use crate::renderer::rects::{Rect, Rects}; use crate::term::color::Rgb; -use crate::term::{self, cell, RenderableCell}; +use crate::term::{self, cell, RenderableCell, RenderableCellContent}; pub mod rects; @@ -950,11 +950,11 @@ impl<'a> RenderApi<'a> { .map(|(i, c)| RenderableCell { line, column: col + i, - chars: { + inner: RenderableCellContent::Chars({ let mut chars = [' '; cell::MAX_ZEROWIDTH_CHARS + 1]; chars[0] = c; chars - }, + }), bg: color.unwrap_or(Rgb { r: 0, g: 0, b: 0 }), fg: Rgb { r: 0, g: 0, b: 0 }, flags: cell::Flags::empty(), @@ -983,6 +983,13 @@ impl<'a> RenderApi<'a> { } pub fn render_cell(&mut self, cell: RenderableCell, glyph_cache: &mut GlyphCache) { + // loader.load_glyph(&rasterized) + if let RenderableCellContent::Raw(ref raw) = cell.inner { + let glyph = self.load_glyph(raw); + self.add_render_item(&cell, &glyph); + return; + } + // Get font key for cell // FIXME this is super inefficient. let font_key = if cell.flags.contains(cell::Flags::BOLD) { @@ -996,8 +1003,10 @@ impl<'a> RenderApi<'a> { // Don't render text of HIDDEN cells let mut chars = if cell.flags.contains(cell::Flags::HIDDEN) { [' '; cell::MAX_ZEROWIDTH_CHARS + 1] + } else if let RenderableCellContent::Chars(chars) = cell.inner { + chars } else { - cell.chars + unimplemented!(); }; // Render tabs as spaces in case the font doesn't support it diff --git a/src/renderer/rects.rs b/src/renderer/rects.rs index 2e5cd85..b4f8a01 100644 --- a/src/renderer/rects.rs +++ b/src/renderer/rects.rs @@ -96,7 +96,7 @@ impl<'a> Rects<'a> { // Start a new line if the flag is present if cell.flags.contains(line.flag) { - *start = *cell; + *start = cell.clone(); *end = cell.into(); } else { line.range = None; @@ -105,7 +105,7 @@ impl<'a> Rects<'a> { // Check for new start of line None => { if cell.flags.contains(line.flag) { - line.range = Some((*cell, cell.into())); + line.range = Some((cell.clone(), cell.into())); } }, }; diff --git a/src/term/mod.rs b/src/term/mod.rs index 48eedef..9e879ad 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -18,7 +18,8 @@ use std::ops::{Index, IndexMut, Range, RangeInclusive}; use std::time::{Duration, Instant}; use std::{io, mem, ptr}; -use arraydeque::ArrayDeque; +use copypasta::{Clipboard, Load, Store}; +use font::{self, RasterizedGlyph, Size}; use glutin::MouseCursor; use unicode_width::UnicodeWidthChar; @@ -26,6 +27,7 @@ use crate::ansi::{ self, Attr, CharsetIndex, Color, CursorStyle, Handler, NamedColor, StandardCharset, }; use crate::config::{Config, VisualBellAnimation}; +use crate::cursor; use crate::grid::{ BidirectionalIterator, DisplayIter, Grid, GridCell, IndexRegion, Indexed, Scroll, ViewportPosition, @@ -37,8 +39,6 @@ use crate::selection::{self, Locations, Selection}; use crate::term::cell::{Cell, Flags, LineLength}; use crate::term::color::Rgb; use crate::url::{Url, UrlParser}; -use copypasta::{Clipboard, Load, Store}; -use font::{self, Size}; #[cfg(windows)] use crate::tty; @@ -158,12 +158,12 @@ pub struct RenderableCellsIter<'a> { grid: &'a Grid, cursor: &'a Point, cursor_offset: usize, - mode: TermMode, + cursor_cell: Option, + cursor_style: CursorStyle, config: &'a Config, colors: &'a color::List, selection: Option>, url_highlight: &'a Option>, - cursor_cells: ArrayDeque<[Indexed; 3]>, } impl<'a> RenderableCellsIter<'a> { @@ -176,6 +176,7 @@ impl<'a> RenderableCellsIter<'a> { config: &'b Config, selection: Option, cursor_style: CursorStyle, + metrics: font::Metrics, ) -> RenderableCellsIter<'b> { let grid = &term.grid; @@ -224,203 +225,143 @@ impl<'a> RenderableCellsIter<'a> { } } + // Load cursor glyph + let cursor = &term.cursor.point; + let cursor_cell = + if term.mode.contains(mode::TermMode::SHOW_CURSOR) && grid.contains(cursor) { + let offset_x = config.font().offset().x; + let offset_y = config.font().offset().y; + + let is_wide = grid[cursor].flags.contains(cell::Flags::WIDE_CHAR) + && (cursor.col + 1) < grid.num_cols(); + Some(cursor::get_cursor_glyph(cursor_style, metrics, offset_x, offset_y, is_wide)) + } else { + None + }; + RenderableCellsIter { - cursor: &term.cursor.point, + cursor, cursor_offset, grid, inner, - mode: term.mode, selection: selection_range, url_highlight: &grid.url_highlight, config, colors: &term.colors, - cursor_cells: ArrayDeque::new(), - } - .initialize(cursor_style) - } - - fn push_cursor_cells(&mut self, original: Cell, cursor: Cell, wide: Cell) { - // Prints the char under the cell if cursor is situated on a non-empty cell - self.cursor_cells - .push_back(Indexed { line: self.cursor.line, column: self.cursor.col, inner: original }) - .expect("won't exceed capacity"); - - // Prints the cursor - self.cursor_cells - .push_back(Indexed { line: self.cursor.line, column: self.cursor.col, inner: cursor }) - .expect("won't exceed capacity"); - - // If cursor is over a wide (2 cell size) character, - // print the second cursor cell - if self.is_wide_cursor(&cursor) { - self.cursor_cells - .push_back(Indexed { - line: self.cursor.line, - column: self.cursor.col + 1, - inner: wide, - }) - .expect("won't exceed capacity"); + cursor_cell, + cursor_style, } } +} - fn populate_block_cursor(&mut self) { - let cell = &self.grid[self.cursor]; - let text_color = self.config.cursor_text_color().unwrap_or(cell.bg); - let cursor_color = self.config.cursor_cursor_color().unwrap_or(cell.fg); +#[derive(Clone, Debug)] +pub enum RenderableCellContent { + Chars([char; cell::MAX_ZEROWIDTH_CHARS + 1]), + Raw(RasterizedGlyph), +} - let original_cell = self.grid[self.cursor]; +#[derive(Clone, Debug)] +pub struct RenderableCell { + /// A _Display_ line (not necessarily an _Active_ line) + pub line: Line, + pub column: Column, + pub inner: RenderableCellContent, + pub fg: Rgb, + pub bg: Rgb, + pub bg_alpha: f32, + pub flags: cell::Flags, +} - let mut cursor_cell = self.grid[self.cursor]; - cursor_cell.fg = text_color; - cursor_cell.bg = cursor_color; +impl RenderableCell { + fn new(config: &Config, colors: &color::List, cell: Indexed, selected: bool) -> Self { + // Lookup RGB values + let mut fg_rgb = Self::compute_fg_rgb(config, colors, cell.fg, cell.flags); + let mut bg_rgb = Self::compute_bg_rgb(colors, cell.bg); - let mut wide_cell = cursor_cell; - wide_cell.c = ' '; - - self.push_cursor_cells(original_cell, cursor_cell, wide_cell); - } - - fn populate_char_cursor(&mut self, cursor_cell_char: char, wide_cell_char: char) { - let original_cell = self.grid[self.cursor]; - - let mut cursor_cell = self.grid[self.cursor]; - let cursor_color = self.config.cursor_cursor_color().unwrap_or(cursor_cell.fg); - cursor_cell.c = cursor_cell_char; - cursor_cell.fg = cursor_color; - - let mut wide_cell = cursor_cell; - wide_cell.c = wide_cell_char; - - self.push_cursor_cells(original_cell, cursor_cell, wide_cell); - } - - fn populate_underline_cursor(&mut self) { - self.populate_char_cursor(font::UNDERLINE_CURSOR_CHAR, font::UNDERLINE_CURSOR_CHAR); - } - - fn populate_beam_cursor(&mut self) { - self.populate_char_cursor(font::BEAM_CURSOR_CHAR, ' '); - } - - fn populate_box_cursor(&mut self) { - self.populate_char_cursor(font::BOX_CURSOR_CHAR, ' '); - } - - #[inline] - fn is_wide_cursor(&self, cell: &Cell) -> bool { - cell.flags.contains(cell::Flags::WIDE_CHAR) && (self.cursor.col + 1) < self.grid.num_cols() - } - - /// Populates list of cursor cells with the original cell - fn populate_no_cursor(&mut self) { - self.cursor_cells - .push_back(Indexed { - line: self.cursor.line, - column: self.cursor.col, - inner: self.grid[self.cursor], - }) - .expect("won't exceed capacity"); - } - - fn initialize(mut self, cursor_style: CursorStyle) -> Self { - if self.cursor_is_visible() { - match cursor_style { - CursorStyle::HollowBlock => { - self.populate_box_cursor(); - }, - CursorStyle::Block => { - self.populate_block_cursor(); - }, - CursorStyle::Beam => { - self.populate_beam_cursor(); - }, - CursorStyle::Underline => { - self.populate_underline_cursor(); - }, - } + let selection_background = config.colors().selection.background; + let bg_alpha = if let (true, Some(col)) = (selected, selection_background) { + // Override selection background with config colors + bg_rgb = col; + 1.0 + } else if selected ^ cell.inverse() { + // Invert cell fg and bg colors + mem::swap(&mut fg_rgb, &mut bg_rgb); + Self::compute_bg_alpha(cell.fg) } else { - self.populate_no_cursor(); + Self::compute_bg_alpha(cell.bg) + }; + + // Override selection text with config colors + if let (true, Some(col)) = (selected, config.colors().selection.text) { + fg_rgb = col; + } + + RenderableCell { + line: cell.line, + column: cell.column, + inner: RenderableCellContent::Chars(cell.chars()), + fg: fg_rgb, + bg: bg_rgb, + bg_alpha, + flags: cell.flags, } - self } - /// Check if the cursor should be rendered. - #[inline] - fn cursor_is_visible(&self) -> bool { - self.mode.contains(mode::TermMode::SHOW_CURSOR) && self.grid.contains(self.cursor) - } - - fn compute_fg_rgb(&self, fg: Color, cell: &Cell) -> Rgb { + fn compute_fg_rgb(config: &Config, colors: &color::List, fg: Color, flags: cell::Flags) -> Rgb { match fg { Color::Spec(rgb) => rgb, Color::Named(ansi) => { - match ( - self.config.draw_bold_text_with_bright_colors(), - cell.flags & Flags::DIM_BOLD, - ) { + match (config.draw_bold_text_with_bright_colors(), flags & Flags::DIM_BOLD) { // If no bright foreground is set, treat it like the BOLD flag doesn't exist - (_, self::cell::Flags::DIM_BOLD) + (_, cell::Flags::DIM_BOLD) if ansi == NamedColor::Foreground - && self.config.colors().primary.bright_foreground.is_none() => + && config.colors().primary.bright_foreground.is_none() => { - self.colors[NamedColor::DimForeground] + colors[NamedColor::DimForeground] }, // Draw bold text in bright colors *and* contains bold flag. - (true, self::cell::Flags::BOLD) => self.colors[ansi.to_bright()], + (true, cell::Flags::BOLD) => colors[ansi.to_bright()], // Cell is marked as dim and not bold - (_, self::cell::Flags::DIM) | (false, self::cell::Flags::DIM_BOLD) => { - self.colors[ansi.to_dim()] - }, + (_, cell::Flags::DIM) | (false, cell::Flags::DIM_BOLD) => colors[ansi.to_dim()], // None of the above, keep original color. - _ => self.colors[ansi], + _ => colors[ansi], } }, Color::Indexed(idx) => { let idx = match ( - self.config.draw_bold_text_with_bright_colors(), - cell.flags & Flags::DIM_BOLD, + config.draw_bold_text_with_bright_colors(), + flags & Flags::DIM_BOLD, idx, ) { - (true, self::cell::Flags::BOLD, 0..=7) => idx as usize + 8, - (false, self::cell::Flags::DIM, 8..=15) => idx as usize - 8, - (false, self::cell::Flags::DIM, 0..=7) => idx as usize + 260, + (true, cell::Flags::BOLD, 0..=7) => idx as usize + 8, + (false, cell::Flags::DIM, 8..=15) => idx as usize - 8, + (false, cell::Flags::DIM, 0..=7) => idx as usize + 260, _ => idx as usize, }; - self.colors[idx] + colors[idx] }, } } #[inline] - fn compute_bg_alpha(&self, bg: Color) -> f32 { + fn compute_bg_alpha(bg: Color) -> f32 { match bg { Color::Named(NamedColor::Background) => 0.0, _ => 1.0, } } - fn compute_bg_rgb(&self, bg: Color) -> Rgb { + #[inline] + fn compute_bg_rgb(colors: &color::List, bg: Color) -> Rgb { match bg { Color::Spec(rgb) => rgb, - Color::Named(ansi) => self.colors[ansi], - Color::Indexed(idx) => self.colors[idx], + Color::Named(ansi) => colors[ansi], + Color::Indexed(idx) => colors[idx], } } } -#[derive(Copy, Clone, Debug)] -pub struct RenderableCell { - /// A _Display_ line (not necessarily an _Active_ line) - pub line: Line, - pub column: Column, - pub chars: [char; cell::MAX_ZEROWIDTH_CHARS + 1], - pub fg: Rgb, - pub bg: Rgb, - pub bg_alpha: f32, - pub flags: cell::Flags, -} - impl<'a> Iterator for RenderableCellsIter<'a> { type Item = RenderableCell; @@ -431,23 +372,32 @@ impl<'a> Iterator for RenderableCellsIter<'a> { #[inline] fn next(&mut self) -> Option { loop { - // Handle cursor - let (cell, selected, highlighted) = if self.cursor_offset == self.inner.offset() - && self.inner.column() == self.cursor.col - { - // Cursor cell - let mut cell = self.cursor_cells.pop_front().unwrap(); - cell.line = self.inner.line(); + if self.cursor_offset == self.inner.offset() && self.inner.column() == self.cursor.col { + // Handle cursor + if let Some(cursor_cell) = self.cursor_cell.take() { + let cell = Indexed { + inner: self.grid[self.cursor], + column: self.cursor.col, + line: self.cursor.line, + }; + let mut renderable_cell = + RenderableCell::new(self.config, self.colors, cell, false); - // Since there may be multiple cursor cells (for a wide - // char), only update iteration position after all cursor - // cells have been drawn. - if self.cursor_cells.is_empty() { - self.inner.next(); + renderable_cell.inner = RenderableCellContent::Raw(cursor_cell); + + return Some(renderable_cell); + } else { + let mut cell = + RenderableCell::new(self.config, self.colors, self.inner.next()?, false); + + if self.cursor_style == CursorStyle::Block { + std::mem::swap(&mut cell.bg, &mut cell.fg); + } + + return Some(cell); } - (cell, false, false) } else { - let cell = self.inner.next()?; + let mut cell = self.inner.next()?; let index = Linear::new(self.grid.num_cols(), cell.column, cell.line); @@ -460,51 +410,13 @@ impl<'a> Iterator for RenderableCellsIter<'a> { } // Underline URL highlights - let highlighted = self - .url_highlight - .as_ref() - .map(|range| range.contains_(index)) - .unwrap_or(false); + if self.url_highlight.as_ref().map(|range| range.contains_(index)).unwrap_or(false) + { + cell.inner.flags.insert(Flags::UNDERLINE); + } - (cell, selected, highlighted) - }; - - // Lookup RGB values - let mut fg_rgb = self.compute_fg_rgb(cell.fg, &cell); - let mut bg_rgb = self.compute_bg_rgb(cell.bg); - - let selection_background = self.config.colors().selection.background; - let bg_alpha = if let (true, Some(col)) = (selected, selection_background) { - // Override selection background with config colors - bg_rgb = col; - 1.0 - } else if selected ^ cell.inverse() { - // Invert cell fg and bg colors - mem::swap(&mut fg_rgb, &mut bg_rgb); - self.compute_bg_alpha(cell.fg) - } else { - self.compute_bg_alpha(cell.bg) - }; - - // Override selection text with config colors - if let (true, Some(col)) = (selected, self.config.colors().selection.text) { - fg_rgb = col; + return Some(RenderableCell::new(self.config, self.colors, cell, selected)); } - - let mut flags = cell.flags; - if highlighted { - flags.insert(Flags::UNDERLINE); - } - - return Some(RenderableCell { - line: cell.line, - column: cell.column, - chars: cell.chars(), - fg: fg_rgb, - bg: bg_rgb, - bg_alpha, - flags, - }); } } } @@ -1174,6 +1086,7 @@ impl Term { &'b self, config: &'b Config, window_focused: bool, + metrics: font::Metrics, ) -> RenderableCellsIter<'_> { let alt_screen = self.mode.contains(TermMode::ALT_SCREEN); let selection = self @@ -1189,7 +1102,7 @@ impl Term { CursorStyle::HollowBlock }; - RenderableCellsIter::new(&self, config, selection, cursor) + RenderableCellsIter::new(&self, config, selection, cursor, metrics) } /// Resize terminal to new dimensions @@ -2497,8 +2410,18 @@ mod benches { let mut terminal = Term::new(&config, size, MessageBuffer::new()); mem::swap(&mut terminal.grid, &mut grid); + let metrics = font::Metrics { + descent: 0., + line_height: 0., + average_advance: 0., + underline_position: 0., + underline_thickness: 0., + strikeout_position: 0., + strikeout_thickness: 0., + }; + b.iter(|| { - let iter = terminal.renderable_cells(&config, false); + let iter = terminal.renderable_cells(&config, false, metrics); for cell in iter { test::black_box(cell); }