blob: 5cd856ab8f34289f2a3ba907d0bb055ae775052f [file] [log] [blame]
use std::collections::HashMap;
use crossfont::Metrics;
use alacritty_terminal::index::{Column, Point};
use alacritty_terminal::term::cell::Flags;
use alacritty_terminal::term::color::Rgb;
use alacritty_terminal::term::{RenderableCell, SizeInfo};
#[derive(Debug, Copy, Clone)]
pub struct RenderRect {
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
pub color: Rgb,
pub alpha: f32,
}
impl RenderRect {
pub fn new(x: f32, y: f32, width: f32, height: f32, color: Rgb, alpha: f32) -> Self {
RenderRect { x, y, width, height, color, alpha }
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct RenderLine {
pub start: Point,
pub end: Point,
pub color: Rgb,
}
impl RenderLine {
pub fn rects(&self, flag: Flags, metrics: &Metrics, size: &SizeInfo) -> Vec<RenderRect> {
let mut rects = Vec::new();
let mut start = self.start;
while start.line < self.end.line {
let mut end = start;
end.col = size.cols() - 1;
rects.push(Self::create_rect(metrics, size, flag, start, end, self.color));
start.col = Column(0);
start.line += 1;
}
rects.push(Self::create_rect(metrics, size, flag, start, self.end, self.color));
rects
}
fn create_rect(
metrics: &Metrics,
size: &SizeInfo,
flag: Flags,
start: Point,
end: Point,
color: Rgb,
) -> RenderRect {
let start_x = start.col.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 line_bottom = (start.line.0 as f32 + 1.) * size.cell_height;
let baseline = line_bottom + metrics.descent;
let mut y = (baseline - position - height / 2.).ceil();
let max_y = line_bottom - height;
if y > max_y {
y = max_y;
}
RenderRect::new(start_x + size.padding_x, y + size.padding_y, width, height, color, 1.)
}
}
/// Lines for underline and strikeout.
#[derive(Default)]
pub struct RenderLines {
inner: HashMap<Flags, Vec<RenderLine>>,
}
impl RenderLines {
pub fn new() -> Self {
Self::default()
}
pub fn rects(&self, metrics: &Metrics, size: &SizeInfo) -> Vec<RenderRect> {
self.inner
.iter()
.map(|(flag, lines)| -> Vec<RenderRect> {
lines.iter().map(|line| line.rects(*flag, metrics, size)).flatten().collect()
})
.flatten()
.collect()
}
/// Update the stored lines with the next cell info.
pub fn update(&mut self, cell: RenderableCell) {
for flag in &[Flags::UNDERLINE, Flags::STRIKEOUT] {
if !cell.flags.contains(*flag) {
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.fg == line.color
&& cell.column == line.end.col + 1
&& cell.line == line.end.line
{
// Update the length of the line.
line.end = cell.into();
continue;
}
}
// Start new line if there currently is none.
let line = RenderLine { start: cell.into(), end: cell.into(), color: cell.fg };
match self.inner.get_mut(flag) {
Some(lines) => lines.push(line),
None => {
self.inner.insert(*flag, vec![line]);
},
}
}
}
}