Start work on underlined text

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Max Brunsfeld 2021-09-06 15:36:09 -07:00
parent 8e5c709057
commit 9346aa300d
11 changed files with 317 additions and 152 deletions

View File

@ -1,6 +1,7 @@
use gpui::{
color::Color,
fonts::{Properties, Weight},
text_layout::RunStyle,
DebugContext, Element as _, Quad,
};
use log::LevelFilter;
@ -55,31 +56,39 @@ impl gpui::Element for TextElement {
) -> Self::PaintState {
let font_size = 12.;
let family = cx.font_cache.load_family(&["SF Pro Display"]).unwrap();
let normal = cx
.font_cache
.select_font(family, &Default::default())
.unwrap();
let bold = cx
.font_cache
.select_font(
family,
&Properties {
weight: Weight::BOLD,
..Default::default()
},
)
.unwrap();
let normal = RunStyle {
font_id: cx
.font_cache
.select_font(family, &Default::default())
.unwrap(),
color: Color::default(),
underline: false,
};
let bold = RunStyle {
font_id: cx
.font_cache
.select_font(
family,
&Properties {
weight: Weight::BOLD,
..Default::default()
},
)
.unwrap(),
color: Color::default(),
underline: false,
};
let text = "Hello world!";
let line = cx.text_layout_cache.layout_str(
text,
font_size,
&[
(1, normal, Color::default()),
(1, bold, Color::default()),
(1, normal, Color::default()),
(1, bold, Color::default()),
(text.len() - 4, normal, Color::default()),
(1, normal.clone()),
(1, bold.clone()),
(1, normal.clone()),
(1, bold.clone()),
(text.len() - 4, normal.clone()),
],
);

View File

@ -1,12 +1,11 @@
use crate::{
color::Color,
fonts::{FontId, TextStyle},
fonts::TextStyle,
geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
},
json::{ToJson, Value},
text_layout::Line,
text_layout::{Line, RunStyle},
DebugContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
};
use serde::Deserialize;
@ -48,10 +47,17 @@ impl Label {
self
}
fn compute_runs(&self) -> SmallVec<[(usize, FontId, Color); 8]> {
fn compute_runs(&self) -> SmallVec<[(usize, RunStyle); 8]> {
let font_id = self.style.text.font_id;
if self.highlight_indices.is_empty() {
return smallvec![(self.text.len(), font_id, self.style.text.color)];
return smallvec![(
self.text.len(),
RunStyle {
font_id,
color: self.style.text.color,
underline: false,
}
)];
}
let highlight_font_id = self
@ -62,25 +68,31 @@ impl Label {
let mut highlight_indices = self.highlight_indices.iter().copied().peekable();
let mut runs = SmallVec::new();
let highlight_style = self
.style
.highlight_text
.as_ref()
.unwrap_or(&self.style.text);
for (char_ix, c) in self.text.char_indices() {
let mut font_id = font_id;
let mut color = self.style.text.color;
let mut underline = self.style.text.underline;
if let Some(highlight_ix) = highlight_indices.peek() {
if char_ix == *highlight_ix {
font_id = highlight_font_id;
color = self
.style
.highlight_text
.as_ref()
.unwrap_or(&self.style.text)
.color;
color = highlight_style.color;
underline = highlight_style.underline;
highlight_indices.next();
}
}
let push_new_run = if let Some((last_len, last_font_id, last_color)) = runs.last_mut() {
if font_id == *last_font_id && color == *last_color {
let last_run: Option<&mut (usize, RunStyle)> = runs.last_mut();
let push_new_run = if let Some((last_len, last_style)) = last_run {
if font_id == last_style.font_id
&& color == last_style.color
&& underline == last_style.underline
{
*last_len += c.len_utf8();
false
} else {
@ -91,7 +103,14 @@ impl Label {
};
if push_new_run {
runs.push((c.len_utf8(), font_id, color));
runs.push((
c.len_utf8(),
RunStyle {
font_id,
color,
underline,
},
));
}
}
@ -177,6 +196,7 @@ impl ToJson for LabelStyle {
#[cfg(test)]
mod tests {
use super::*;
use crate::color::Color;
use crate::fonts::{Properties as FontProperties, Weight};
#[crate::test(self)]
@ -185,6 +205,7 @@ mod tests {
"Menlo",
12.,
Default::default(),
false,
Color::black(),
cx.font_cache(),
)
@ -193,6 +214,7 @@ mod tests {
"Menlo",
12.,
*FontProperties::new().weight(Weight::BOLD),
false,
Color::new(255, 0, 0, 255),
cx.font_cache(),
)
@ -212,21 +234,27 @@ mod tests {
".αβγδε.ⓐⓑ".len(),
]);
let default_run_style = RunStyle {
font_id: default_style.font_id,
color: default_style.color,
underline: default_style.underline,
};
let highlight_run_style = RunStyle {
font_id: highlight_style.font_id,
color: highlight_style.color,
underline: highlight_style.underline,
};
let runs = label.compute_runs();
assert_eq!(
runs.as_slice(),
&[
(".α".len(), default_style.font_id, default_style.color),
("βγ".len(), highlight_style.font_id, highlight_style.color),
("δ".len(), default_style.font_id, default_style.color),
("ε".len(), highlight_style.font_id, highlight_style.color),
(".ⓐ".len(), default_style.font_id, default_style.color),
("ⓑⓒ".len(), highlight_style.font_id, highlight_style.color),
(
"ⓓⓔ.abcde.".len(),
default_style.font_id,
default_style.color
),
(".α".len(), default_run_style),
("βγ".len(), highlight_run_style),
("δ".len(), default_run_style),
("ε".len(), highlight_run_style),
(".ⓐ".len(), default_run_style),
("ⓑⓒ".len(), highlight_run_style),
("ⓓⓔ.abcde.".len(), default_run_style),
]
);
}

View File

@ -52,7 +52,7 @@ impl Element for Text {
let shaped_line = cx.text_layout_cache.layout_str(
line,
self.style.font_size,
&[(line.len(), font_id, self.style.color)],
&[(line.len(), self.style.to_run())],
);
let wrap_boundaries = wrapper
.wrap_shaped_line(line, &shaped_line, constraint.max.x())

View File

@ -1,6 +1,7 @@
use crate::{
color::Color,
json::{json, ToJson},
text_layout::RunStyle,
FontCache,
};
use anyhow::anyhow;
@ -24,12 +25,14 @@ pub struct TextStyle {
pub font_id: FontId,
pub font_size: f32,
pub font_properties: Properties,
pub underline: bool,
}
#[derive(Clone, Debug, Default)]
pub struct HighlightStyle {
pub color: Color,
pub font_properties: Properties,
pub underline: bool,
}
#[allow(non_camel_case_types)]
@ -55,9 +58,11 @@ struct TextStyleJson {
color: Color,
family: String,
weight: Option<WeightJson>,
size: f32,
#[serde(default)]
italic: bool,
size: f32,
#[serde(default)]
underline: bool,
}
#[derive(Deserialize)]
@ -66,6 +71,8 @@ struct HighlightStyleJson {
weight: Option<WeightJson>,
#[serde(default)]
italic: bool,
#[serde(default)]
underline: bool,
}
impl TextStyle {
@ -73,6 +80,7 @@ impl TextStyle {
font_family_name: impl Into<Arc<str>>,
font_size: f32,
font_properties: Properties,
underline: bool,
color: Color,
font_cache: &FontCache,
) -> anyhow::Result<Self> {
@ -85,9 +93,18 @@ impl TextStyle {
font_id,
font_size,
font_properties,
underline,
})
}
pub fn to_run(&self) -> RunStyle {
RunStyle {
font_id: self.font_id,
color: self.color,
underline: self.underline,
}
}
fn from_json(json: TextStyleJson) -> anyhow::Result<Self> {
FONT_CACHE.with(|font_cache| {
if let Some(font_cache) = font_cache.borrow().as_ref() {
@ -96,6 +113,7 @@ impl TextStyle {
json.family,
json.size,
font_properties,
json.underline,
json.color,
font_cache,
)
@ -114,6 +132,7 @@ impl HighlightStyle {
Self {
color: json.color,
font_properties,
underline: json.underline,
}
}
}
@ -123,6 +142,7 @@ impl From<Color> for HighlightStyle {
Self {
color,
font_properties: Default::default(),
underline: false,
}
}
}
@ -161,6 +181,7 @@ impl<'de> Deserialize<'de> for HighlightStyle {
Ok(Self {
color: serde_json::from_value(json).map_err(de::Error::custom)?,
font_properties: Properties::new(),
underline: false,
})
}
}

View File

@ -8,14 +8,13 @@ pub mod current {
}
use crate::{
color::Color,
executor,
fonts::{FontId, GlyphId, Metrics as FontMetrics, Properties as FontProperties},
geometry::{
rect::{RectF, RectI},
vector::{vec2f, Vector2F},
},
text_layout::LineLayout,
text_layout::{LineLayout, RunStyle},
AnyAction, ClipboardItem, Menu, Scene,
};
use anyhow::Result;
@ -146,12 +145,7 @@ pub trait FontSystem: Send + Sync {
subpixel_shift: Vector2F,
scale_factor: f32,
) -> Option<(RectI, Vec<u8>)>;
fn layout_line(
&self,
text: &str,
font_size: f32,
runs: &[(usize, FontId, Color)],
) -> LineLayout;
fn layout_line(&self, text: &str, font_size: f32, runs: &[(usize, RunStyle)]) -> LineLayout;
fn wrap_line(&self, text: &str, font_id: FontId, font_size: f32, width: f32) -> Vec<usize>;
}

View File

@ -1,5 +1,4 @@
use crate::{
color::Color,
fonts::{FontId, GlyphId, Metrics, Properties},
geometry::{
rect::{RectF, RectI},
@ -7,7 +6,7 @@ use crate::{
vector::{vec2f, vec2i, Vector2F},
},
platform,
text_layout::{Glyph, LineLayout, Run},
text_layout::{Glyph, LineLayout, Run, RunStyle},
};
use cocoa::appkit::{CGFloat, CGPoint};
use core_foundation::{
@ -87,12 +86,7 @@ impl platform::FontSystem for FontSystem {
.rasterize_glyph(font_id, font_size, glyph_id, subpixel_shift, scale_factor)
}
fn layout_line(
&self,
text: &str,
font_size: f32,
runs: &[(usize, FontId, Color)],
) -> LineLayout {
fn layout_line(&self, text: &str, font_size: f32, runs: &[(usize, RunStyle)]) -> LineLayout {
self.0.read().layout_line(text, font_size, runs)
}
@ -210,12 +204,7 @@ impl FontSystemState {
}
}
fn layout_line(
&self,
text: &str,
font_size: f32,
runs: &[(usize, FontId, Color)],
) -> LineLayout {
fn layout_line(&self, text: &str, font_size: f32, runs: &[(usize, RunStyle)]) -> LineLayout {
let font_id_attr_name = CFString::from_static_string("zed_font_id");
// Construct the attributed string, converting UTF8 ranges to UTF16 ranges.
@ -227,20 +216,20 @@ impl FontSystemState {
let last_run: RefCell<Option<(usize, FontId)>> = Default::default();
let font_runs = runs
.iter()
.filter_map(|(len, font_id, _)| {
.filter_map(|(len, style)| {
let mut last_run = last_run.borrow_mut();
if let Some((last_len, last_font_id)) = last_run.as_mut() {
if font_id == last_font_id {
if style.font_id == *last_font_id {
*last_len += *len;
None
} else {
let result = (*last_len, *last_font_id);
*last_len = *len;
*last_font_id = *font_id;
*last_font_id = style.font_id;
Some(result)
}
} else {
*last_run = Some((*len, *font_id));
*last_run = Some((*len, style.font_id));
None
}
})
@ -415,9 +404,8 @@ extern "C" {
#[cfg(test)]
mod tests {
use crate::MutableAppContext;
use super::*;
use crate::MutableAppContext;
use font_kit::properties::{Style, Weight};
use platform::FontSystem as _;
@ -426,13 +414,25 @@ mod tests {
// This is failing intermittently on CI and we don't have time to figure it out
let fonts = FontSystem::new();
let menlo = fonts.load_family("Menlo").unwrap();
let menlo_regular = fonts.select_font(&menlo, &Properties::new()).unwrap();
let menlo_italic = fonts
.select_font(&menlo, &Properties::new().style(Style::Italic))
.unwrap();
let menlo_bold = fonts
.select_font(&menlo, &Properties::new().weight(Weight::BOLD))
.unwrap();
let menlo_regular = RunStyle {
font_id: fonts.select_font(&menlo, &Properties::new()).unwrap(),
color: Default::default(),
underline: false,
};
let menlo_italic = RunStyle {
font_id: fonts
.select_font(&menlo, &Properties::new().style(Style::Italic))
.unwrap(),
color: Default::default(),
underline: false,
};
let menlo_bold = RunStyle {
font_id: fonts
.select_font(&menlo, &Properties::new().weight(Weight::BOLD))
.unwrap(),
color: Default::default(),
underline: false,
};
assert_ne!(menlo_regular, menlo_italic);
assert_ne!(menlo_regular, menlo_bold);
assert_ne!(menlo_italic, menlo_bold);
@ -440,18 +440,14 @@ mod tests {
let line = fonts.layout_line(
"hello world",
16.0,
&[
(2, menlo_bold, Default::default()),
(4, menlo_italic, Default::default()),
(5, menlo_regular, Default::default()),
],
&[(2, menlo_bold), (4, menlo_italic), (5, menlo_regular)],
);
assert_eq!(line.runs.len(), 3);
assert_eq!(line.runs[0].font_id, menlo_bold);
assert_eq!(line.runs[0].font_id, menlo_bold.font_id);
assert_eq!(line.runs[0].glyphs.len(), 2);
assert_eq!(line.runs[1].font_id, menlo_italic);
assert_eq!(line.runs[1].font_id, menlo_italic.font_id);
assert_eq!(line.runs[1].glyphs.len(), 4);
assert_eq!(line.runs[2].font_id, menlo_regular);
assert_eq!(line.runs[2].font_id, menlo_regular.font_id);
assert_eq!(line.runs[2].glyphs.len(), 5);
}
@ -459,18 +455,26 @@ mod tests {
fn test_glyph_offsets() -> anyhow::Result<()> {
let fonts = FontSystem::new();
let zapfino = fonts.load_family("Zapfino")?;
let zapfino_regular = fonts.select_font(&zapfino, &Properties::new())?;
let zapfino_regular = RunStyle {
font_id: fonts.select_font(&zapfino, &Properties::new())?,
color: Default::default(),
underline: false,
};
let menlo = fonts.load_family("Menlo")?;
let menlo_regular = fonts.select_font(&menlo, &Properties::new())?;
let menlo_regular = RunStyle {
font_id: fonts.select_font(&menlo, &Properties::new())?,
color: Default::default(),
underline: false,
};
let text = "This is, m𐍈re 𐍈r less, Zapfino!𐍈";
let line = fonts.layout_line(
text,
16.0,
&[
(9, zapfino_regular, Color::default()),
(13, menlo_regular, Color::default()),
(text.len() - 22, zapfino_regular, Color::default()),
(9, zapfino_regular),
(13, menlo_regular),
(text.len() - 22, zapfino_regular),
],
);
assert_eq!(
@ -536,15 +540,19 @@ mod tests {
fn test_layout_line_bom_char() {
let fonts = FontSystem::new();
let font_ids = fonts.load_family("Helvetica").unwrap();
let font_id = fonts.select_font(&font_ids, &Default::default()).unwrap();
let style = RunStyle {
font_id: fonts.select_font(&font_ids, &Default::default()).unwrap(),
color: Default::default(),
underline: false,
};
let line = "\u{feff}";
let layout = fonts.layout_line(line, 16., &[(line.len(), font_id, Default::default())]);
let layout = fonts.layout_line(line, 16., &[(line.len(), style)]);
assert_eq!(layout.len, line.len());
assert!(layout.runs.is_empty());
let line = "a\u{feff}b";
let layout = fonts.layout_line(line, 16., &[(line.len(), font_id, Default::default())]);
let layout = fonts.layout_line(line, 16., &[(line.len(), style)]);
assert_eq!(layout.len, line.len());
assert_eq!(layout.runs.len(), 1);
assert_eq!(layout.runs[0].glyphs.len(), 2);

View File

@ -24,6 +24,13 @@ pub struct TextLayoutCache {
fonts: Arc<dyn platform::FontSystem>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct RunStyle {
pub color: Color,
pub font_id: FontId,
pub underline: bool,
}
impl TextLayoutCache {
pub fn new(fonts: Arc<dyn platform::FontSystem>) -> Self {
Self {
@ -44,7 +51,7 @@ impl TextLayoutCache {
&'a self,
text: &'a str,
font_size: f32,
runs: &'a [(usize, FontId, Color)],
runs: &'a [(usize, RunStyle)],
) -> Line {
let key = &CacheKeyRef {
text,
@ -95,7 +102,7 @@ impl<'a> Hash for (dyn CacheKey + 'a) {
struct CacheKeyValue {
text: String,
font_size: OrderedFloat<f32>,
runs: SmallVec<[(usize, FontId, Color); 1]>,
runs: SmallVec<[(usize, RunStyle); 1]>,
}
impl CacheKey for CacheKeyValue {
@ -120,11 +127,11 @@ impl<'a> Borrow<dyn CacheKey + 'a> for CacheKeyValue {
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[derive(Copy, Clone)]
struct CacheKeyRef<'a> {
text: &'a str,
font_size: OrderedFloat<f32>,
runs: &'a [(usize, FontId, Color)],
runs: &'a [(usize, RunStyle)],
}
impl<'a> CacheKey for CacheKeyRef<'a> {
@ -133,10 +140,34 @@ impl<'a> CacheKey for CacheKeyRef<'a> {
}
}
impl<'a> PartialEq for CacheKeyRef<'a> {
fn eq(&self, other: &Self) -> bool {
self.text == other.text
&& self.font_size == other.font_size
&& self.runs.len() == other.runs.len()
&& self.runs.iter().zip(other.runs.iter()).all(
|((len_a, style_a), (len_b, style_b))| {
len_a == len_b && style_a.font_id == style_b.font_id
},
)
}
}
impl<'a> Hash for CacheKeyRef<'a> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.text.hash(state);
self.font_size.hash(state);
for (len, style_id) in self.runs {
len.hash(state);
style_id.font_id.hash(state);
}
}
}
#[derive(Default, Debug)]
pub struct Line {
layout: Arc<LineLayout>,
color_runs: SmallVec<[(u32, Color); 32]>,
style_runs: SmallVec<[(u32, Color, bool); 32]>,
}
#[derive(Default, Debug)]
@ -163,12 +194,12 @@ pub struct Glyph {
}
impl Line {
fn new(layout: Arc<LineLayout>, runs: &[(usize, FontId, Color)]) -> Self {
let mut color_runs = SmallVec::new();
for (len, _, color) in runs {
color_runs.push((*len as u32, *color));
fn new(layout: Arc<LineLayout>, runs: &[(usize, RunStyle)]) -> Self {
let mut style_runs = SmallVec::new();
for (len, style) in runs {
style_runs.push((*len as u32, style.color, style.underline));
}
Self { layout, color_runs }
Self { layout, style_runs }
}
pub fn runs(&self) -> &[Run] {
@ -213,11 +244,12 @@ impl Line {
cx: &mut PaintContext,
) {
let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
let baseline_origin = vec2f(0., padding_top + self.layout.ascent);
let baseline_offset = vec2f(0., padding_top + self.layout.ascent);
let mut color_runs = self.color_runs.iter();
let mut color_end = 0;
let mut style_runs = self.style_runs.iter();
let mut run_end = 0;
let mut color = Color::black();
let mut underline_start = None;
for run in &self.layout.runs {
let max_glyph_width = cx
@ -226,7 +258,7 @@ impl Line {
.x();
for glyph in &run.glyphs {
let glyph_origin = origin + baseline_origin + glyph.position;
let glyph_origin = origin + baseline_offset + glyph.position;
if glyph_origin.x() + max_glyph_width < visible_bounds.origin().x() {
continue;
@ -235,12 +267,31 @@ impl Line {
break;
}
if glyph.index >= color_end {
if let Some(next_run) = color_runs.next() {
color_end += next_run.0 as usize;
color = next_run.1;
if glyph.index >= run_end {
if let Some((run_len, run_color, run_underlined)) = style_runs.next() {
if let Some(underline_origin) = underline_start {
if !*run_underlined || *run_color != color {
cx.scene.push_quad(scene::Quad {
bounds: RectF::from_points(
underline_origin,
glyph_origin + vec2f(0., 1.),
),
background: Some(color),
border: Default::default(),
corner_radius: 0.,
});
underline_start = None;
}
}
if *run_underlined {
underline_start.get_or_insert(glyph_origin);
}
run_end += *run_len as usize;
color = *run_color;
} else {
color_end = self.layout.len;
run_end = self.layout.len;
color = Color::black();
}
}
@ -253,6 +304,16 @@ impl Line {
color,
});
}
if let Some(underline_start) = underline_start.take() {
let line_end = origin + baseline_offset + vec2f(self.layout.width, 0.);
cx.scene.push_quad(scene::Quad {
bounds: RectF::from_points(underline_start, line_end + vec2f(0., 1.)),
background: Some(color),
border: Default::default(),
corner_radius: 0.,
});
}
}
}
@ -268,7 +329,7 @@ impl Line {
let baseline_origin = vec2f(0., padding_top + self.layout.ascent);
let mut boundaries = boundaries.into_iter().peekable();
let mut color_runs = self.color_runs.iter();
let mut color_runs = self.style_runs.iter();
let mut color_end = 0;
let mut color = Color::black();
@ -519,7 +580,14 @@ impl LineWrapper {
.layout_line(
&c.to_string(),
self.font_size,
&[(1, self.font_id, Default::default())],
&[(
1,
RunStyle {
font_id: self.font_id,
color: Default::default(),
underline: false,
},
)],
)
.width
}
@ -528,10 +596,7 @@ impl LineWrapper {
#[cfg(test)]
mod tests {
use super::*;
use crate::{
color::Color,
fonts::{Properties, Weight},
};
use crate::fonts::{Properties, Weight};
#[crate::test(self)]
fn test_wrap_line(cx: &mut crate::MutableAppContext) {
@ -600,28 +665,30 @@ mod tests {
let family = font_cache.load_family(&["Helvetica"]).unwrap();
let font_id = font_cache.select_font(family, &Default::default()).unwrap();
let normal = font_cache.select_font(family, &Default::default()).unwrap();
let bold = font_cache
.select_font(
family,
&Properties {
weight: Weight::BOLD,
..Default::default()
},
)
.unwrap();
let normal = RunStyle {
font_id,
color: Default::default(),
underline: false,
};
let bold = RunStyle {
font_id: font_cache
.select_font(
family,
&Properties {
weight: Weight::BOLD,
..Default::default()
},
)
.unwrap(),
color: Default::default(),
underline: false,
};
let text = "aa bbb cccc ddddd eeee";
let line = text_layout_cache.layout_str(
text,
16.0,
&[
(4, normal, Color::default()),
(5, bold, Color::default()),
(6, normal, Color::default()),
(1, bold, Color::default()),
(7, normal, Color::default()),
],
&[(4, normal), (5, bold), (6, normal), (1, bold), (7, normal)],
);
let mut wrapper = LineWrapper::new(font_id, 16., font_system);

View File

@ -2,7 +2,7 @@
background = "$surface.0"
[workspace.tab]
text = "$text.2"
text = { extends = "$text.2", underline = true }
padding = { left = 10, right = 10 }
icon_close = "$text.0.color"
icon_dirty = "$status.info"

View File

@ -23,7 +23,7 @@ bad = "#b7372e"
[syntax]
keyword = { color = "#0086c0", weight = "bold" }
function = "#dcdcaa"
function = { color = "#dcdcaa", underline = true }
string = "#cb8f77"
type = "#4ec9b0"
number = "#b5cea8"

View File

@ -17,10 +17,15 @@ pub use display_map::DisplayPoint;
use display_map::*;
pub use element::*;
use gpui::{
action, color::Color, font_cache::FamilyId, fonts::Properties as FontProperties,
geometry::vector::Vector2F, keymap::Binding, text_layout, AppContext, ClipboardItem, Element,
ElementBox, Entity, FontCache, ModelHandle, MutableAppContext, RenderContext, Task,
TextLayoutCache, View, ViewContext, WeakViewHandle,
action,
color::Color,
font_cache::FamilyId,
fonts::Properties as FontProperties,
geometry::vector::Vector2F,
keymap::Binding,
text_layout::{self, RunStyle},
AppContext, ClipboardItem, Element, ElementBox, Entity, FontCache, ModelHandle,
MutableAppContext, RenderContext, Task, TextLayoutCache, View, ViewContext, WeakViewHandle,
};
use postage::watch;
use serde::{Deserialize, Serialize};
@ -2330,7 +2335,7 @@ impl Snapshot {
pub fn line_height(&self, font_cache: &FontCache) -> f32 {
let font_id = font_cache.default_font(self.font_family);
font_cache.line_height(font_id, self.font_size)
font_cache.line_height(font_id, self.font_size).ceil()
}
pub fn em_width(&self, font_cache: &FontCache) -> f32 {
@ -2355,7 +2360,14 @@ impl Snapshot {
.layout_str(
"1".repeat(digit_count).as_str(),
font_size,
&[(digit_count, font_id, Color::black())],
&[(
digit_count,
RunStyle {
font_id,
color: Color::black(),
underline: false,
},
)],
)
.width())
}
@ -2392,7 +2404,14 @@ impl Snapshot {
layouts.push(Some(layout_cache.layout_str(
&line_number,
self.font_size,
&[(line_number.len(), font_id, color)],
&[(
line_number.len(),
RunStyle {
font_id,
color,
underline: false,
},
)],
)));
}
}
@ -2429,7 +2448,14 @@ impl Snapshot {
layout_cache.layout_str(
line,
self.font_size,
&[(line.len(), font_id, style.placeholder_text.color)],
&[(
line.len(),
RunStyle {
font_id,
color: style.placeholder_text.color,
underline: false,
},
)],
)
})
.collect());
@ -2485,7 +2511,14 @@ impl Snapshot {
}
line.push_str(line_chunk);
styles.push((line_chunk.len(), font_id, style.color));
styles.push((
line_chunk.len(),
RunStyle {
font_id,
color: style.color,
underline: style.underline,
},
));
prev_font_id = font_id;
prev_font_properties = style.font_properties;
}
@ -2518,8 +2551,11 @@ impl Snapshot {
self.font_size,
&[(
self.display_snapshot.line_len(row) as usize,
font_id,
Color::black(),
RunStyle {
font_id,
color: Color::black(),
underline: false,
},
)],
))
}

View File

@ -180,10 +180,12 @@ impl Default for EditorStyle {
text: HighlightStyle {
color: Color::from_u32(0xff0000ff),
font_properties: Default::default(),
underline: false,
},
placeholder_text: HighlightStyle {
color: Color::from_u32(0x00ff00ff),
font_properties: Default::default(),
underline: false,
},
background: Default::default(),
gutter_background: Default::default(),