Detailed changes
@@ -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()),
],
);
@@ -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),
]
);
}
@@ -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())
@@ -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,
})
}
}
@@ -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>;
}
@@ -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);
@@ -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);
@@ -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"
@@ -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"
@@ -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,
+ },
)],
))
}
@@ -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(),