Initial public release of Warp.
Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
//! This module defines CellGlyphCache, a struct which manages the caching of glyph values for cells
|
||||
//! when rendering Grids within Warp.
|
||||
use warpui::elements::DEFAULT_LINE_HEIGHT_RATIO;
|
||||
|
||||
use warpui::fonts::{Cache as FontCache, FamilyId, FontId, GlyphId, Properties};
|
||||
use warpui::platform::LineStyle;
|
||||
use warpui::text_layout::{StyleAndFont, DEFAULT_TOP_BOTTOM_RATIO};
|
||||
use warpui::PaintContext;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Stores cached glyph values for characters/strings. Note that we normally only need to look up
|
||||
/// characters - we only look up strings in the case of zerowidth characters (which act as modifiers
|
||||
/// to the first character e.g. emoji variant selectors). We have 2 separate caches internally for
|
||||
/// performance reasons (avoid allocating strings when we don't need to!).
|
||||
#[derive(Default)]
|
||||
pub struct CellGlyphCache {
|
||||
glyph_cache: HashMap<(char, FontId), Option<(GlyphId, FontId)>>,
|
||||
string_cache: HashMap<(String, FontId), Option<(GlyphId, FontId)>>,
|
||||
}
|
||||
|
||||
impl CellGlyphCache {
|
||||
pub(super) fn glyph_for_char(
|
||||
&mut self,
|
||||
char: char,
|
||||
font_id: FontId,
|
||||
font_cache: &FontCache,
|
||||
) -> Option<(GlyphId, FontId)> {
|
||||
*self
|
||||
.glyph_cache
|
||||
.entry((char, font_id))
|
||||
.or_insert_with(|| font_cache.glyph_for_char(font_id, char, true))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) fn glyph_for_string(
|
||||
&mut self,
|
||||
string: &str,
|
||||
font_id: FontId,
|
||||
font_cache: &FontCache,
|
||||
font_family: FamilyId,
|
||||
font_size: f32,
|
||||
properties: Properties,
|
||||
ctx: &mut PaintContext,
|
||||
) -> Option<(GlyphId, FontId)> {
|
||||
let glyph = *self
|
||||
.string_cache
|
||||
.entry((string.to_owned(), font_id))
|
||||
.or_insert_with(|| {
|
||||
// Calculate the length of total characters in the string.
|
||||
let run_length_chars = string.chars().count();
|
||||
let line = ctx.text_layout_cache.layout_line(
|
||||
string,
|
||||
LineStyle {
|
||||
font_size,
|
||||
// Note that we DO NOT paint the `Line` in this particular instance. As such,
|
||||
// the line height ratio and baseline ratio are both NOT used. Hence, we arbitrarily
|
||||
// set them to the default values.
|
||||
line_height_ratio: DEFAULT_LINE_HEIGHT_RATIO,
|
||||
baseline_ratio: DEFAULT_TOP_BOTTOM_RATIO,
|
||||
fixed_width_tab_size: None,
|
||||
},
|
||||
&[(
|
||||
(0..run_length_chars),
|
||||
StyleAndFont {
|
||||
font_family,
|
||||
properties,
|
||||
style: Default::default(),
|
||||
},
|
||||
)],
|
||||
f32::MAX,
|
||||
Default::default(),
|
||||
&font_cache.text_layout_system(),
|
||||
);
|
||||
let run = line.runs.first()?;
|
||||
if run.glyphs.len() > 1 {
|
||||
// If we have more than one glyph, something has gone wrong.
|
||||
return None;
|
||||
}
|
||||
run.glyphs.first().map(|glyph| (glyph.id, run.font_id))
|
||||
});
|
||||
|
||||
glyph.or_else(|| {
|
||||
#[cfg(debug_assertions)]
|
||||
log::warn!("Falling back to glyph for first character of string, could not get glyph for entire string: {string:?}");
|
||||
let first_char = string.chars().next()?;
|
||||
let glyph = self.glyph_for_char(first_char, font_id, font_cache);
|
||||
// Make sure we update the cache with the fallback, so we don't
|
||||
// recompute it again.
|
||||
self.string_cache.insert((string.to_owned(), font_id), glyph);
|
||||
glyph
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
use pathfinder_color::ColorU;
|
||||
|
||||
use crate::{
|
||||
terminal::{
|
||||
color,
|
||||
model::{
|
||||
ansi::{color_index, Color, NamedColor},
|
||||
cell::{Cell, Flags},
|
||||
ObfuscateSecrets,
|
||||
},
|
||||
},
|
||||
util::color::OPAQUE,
|
||||
};
|
||||
|
||||
use super::{BLOCK_FILTER_MATCH_COLOR, FOCUSED_MATCH_COLOR, MATCH_COLOR, URL_COLOR};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub(super) struct Secret {
|
||||
pub(super) hovered: bool,
|
||||
pub(super) is_obfuscated: bool,
|
||||
}
|
||||
|
||||
/// Determines whether a match is focused.
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
pub(super) enum IsFocused {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq)]
|
||||
pub(super) struct CellType {
|
||||
pub(super) is_find_match: Option<IsFocused>,
|
||||
pub(super) is_url: bool,
|
||||
pub(super) secret: Option<Secret>,
|
||||
pub(super) is_filter_match: bool,
|
||||
pub(super) is_marked_text_char: bool,
|
||||
}
|
||||
|
||||
impl CellType {
|
||||
pub(super) fn marked_text_char() -> Self {
|
||||
Self {
|
||||
is_marked_text_char: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn is_find_match(&self) -> bool {
|
||||
self.is_find_match.is_some()
|
||||
}
|
||||
|
||||
pub(super) fn is_focused_find_match(&self) -> bool {
|
||||
self.is_find_match
|
||||
.map_or_else(|| false, |is_focused| matches!(is_focused, IsFocused::Yes))
|
||||
}
|
||||
|
||||
pub(super) fn is_unfocused_find_match(&self) -> bool {
|
||||
self.is_find_match
|
||||
.map_or_else(|| false, |is_focused| matches!(is_focused, IsFocused::No))
|
||||
}
|
||||
|
||||
pub(super) fn is_filter_match(&self) -> bool {
|
||||
self.is_filter_match
|
||||
}
|
||||
|
||||
pub(super) fn is_url(&self) -> bool {
|
||||
self.is_url
|
||||
}
|
||||
|
||||
pub(super) fn is_secret(&self) -> bool {
|
||||
self.secret.is_some()
|
||||
}
|
||||
|
||||
pub(super) fn is_hovered_secret(&self) -> bool {
|
||||
self.secret
|
||||
.as_ref()
|
||||
.map_or_else(|| false, |secret| secret.hovered)
|
||||
}
|
||||
|
||||
pub(super) fn is_unhovered_secret(&self) -> bool {
|
||||
self.secret
|
||||
.as_ref()
|
||||
.map_or_else(|| false, |secret| !secret.hovered)
|
||||
}
|
||||
|
||||
/// Used to check if a CellType is equivalent to the default (i.e. no matches, urls, secrets, etc).
|
||||
pub(super) fn is_default(&self) -> bool {
|
||||
&Self::default() == self
|
||||
}
|
||||
|
||||
pub(super) fn is_marked_text_char(&self) -> bool {
|
||||
self.is_marked_text_char
|
||||
}
|
||||
|
||||
/// Calculate the foreground color for a cell. Does not set alpha value.
|
||||
pub(super) fn foreground_color(
|
||||
&self,
|
||||
cell: &Cell,
|
||||
colors: &color::List,
|
||||
override_colors: &color::OverrideList,
|
||||
obfuscate_mode: ObfuscateSecrets,
|
||||
) -> ColorU {
|
||||
let is_unhovered_secret = self.is_unhovered_secret();
|
||||
|
||||
if self.is_filter_match() {
|
||||
*BLOCK_FILTER_MATCH_COLOR
|
||||
} else if self.is_url() || self.is_hovered_secret() {
|
||||
*URL_COLOR
|
||||
} else if matches!(obfuscate_mode, ObfuscateSecrets::Strikethrough) && is_unhovered_secret {
|
||||
warpui::color::ColorU::new(128, 128, 128, 255)
|
||||
} else if self.is_default()
|
||||
|| self.is_marked_text_char()
|
||||
|| is_unhovered_secret
|
||||
|| matches!(obfuscate_mode, ObfuscateSecrets::AlwaysShow)
|
||||
{
|
||||
compute_fg_rgb(colors, override_colors, cell.fg, cell.flags)
|
||||
} else {
|
||||
ColorU::black()
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the background color (including the alpha) for a cell.
|
||||
pub(super) fn background_color(
|
||||
&self,
|
||||
cell: &Cell,
|
||||
colors: &color::List,
|
||||
override_colors: &color::OverrideList,
|
||||
) -> ColorU {
|
||||
let mut bg_color =
|
||||
if self.is_unfocused_find_match() && !self.is_hovered_secret() && !self.is_url() {
|
||||
*MATCH_COLOR
|
||||
} else if self.is_focused_find_match() && !self.is_hovered_secret() && !self.is_url() {
|
||||
*FOCUSED_MATCH_COLOR
|
||||
} else {
|
||||
compute_bg_rgb(colors, override_colors, cell.bg)
|
||||
};
|
||||
let bg_alpha = if cell.flags.contains(Flags::INVERSE) {
|
||||
OPAQUE
|
||||
} else if self.is_default()
|
||||
|| self.is_url()
|
||||
|| (self.is_filter_match() && !self.is_find_match())
|
||||
|| (self.is_secret() && !self.is_find_match())
|
||||
|| self.is_hovered_secret()
|
||||
{
|
||||
if cell.bg == Color::Named(NamedColor::Background) {
|
||||
// If the background of the cell is the same as the terminal background, treat it as
|
||||
// an alpha value of 0.
|
||||
0
|
||||
} else {
|
||||
OPAQUE
|
||||
}
|
||||
} else {
|
||||
OPAQUE
|
||||
};
|
||||
bg_color.a = bg_alpha;
|
||||
bg_color
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the RGB color from a cell's foreground color.
|
||||
fn compute_fg_rgb(
|
||||
colors: &color::List,
|
||||
override_colors: &color::OverrideList,
|
||||
fg: Color,
|
||||
flags: Flags,
|
||||
) -> ColorU {
|
||||
match fg {
|
||||
Color::Spec(rgb) => match flags & Flags::DIM {
|
||||
Flags::DIM => crate::terminal::color::dim(rgb),
|
||||
_ => rgb,
|
||||
},
|
||||
Color::Named(ansi) => {
|
||||
match flags & Flags::DIM_BOLD {
|
||||
// If no bright foreground is set, treat it like the BOLD flag doesn't exist.
|
||||
Flags::DIM_BOLD if ansi == NamedColor::Foreground => {
|
||||
get_override_color(colors, override_colors, color_index::DIM_FOREGROUND)
|
||||
}
|
||||
// Cell is marked as dim and not bold.
|
||||
Flags::DIM => {
|
||||
get_override_color(colors, override_colors, ansi.to_dim().into_color_index())
|
||||
}
|
||||
// None of the above, keep original color..
|
||||
_ => get_override_color(colors, override_colors, ansi.into_color_index()),
|
||||
}
|
||||
}
|
||||
Color::Indexed(idx) => {
|
||||
let idx = match (flags & Flags::DIM_BOLD, idx) {
|
||||
(Flags::DIM, 8..=15) => idx as usize - 8,
|
||||
(Flags::DIM, 0..=7) => color_index::DIM_BLACK + idx as usize,
|
||||
_ => idx as usize,
|
||||
};
|
||||
|
||||
get_override_color(colors, override_colors, idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the RGB color from a cell's background color.
|
||||
fn compute_bg_rgb(
|
||||
colors: &color::List,
|
||||
override_colors: &color::OverrideList,
|
||||
bg: Color,
|
||||
) -> ColorU {
|
||||
match bg {
|
||||
Color::Spec(rgb) => rgb,
|
||||
Color::Named(ansi) => get_override_color(colors, override_colors, ansi.into_color_index()),
|
||||
Color::Indexed(idx) => get_override_color(colors, override_colors, idx as usize),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_override_color(
|
||||
colors: &color::List,
|
||||
override_colors: &color::OverrideList,
|
||||
index: usize,
|
||||
) -> ColorU {
|
||||
override_colors[index].unwrap_or(colors[index])
|
||||
}
|
||||
Reference in New Issue
Block a user