Overhaul handling of font families

* Specify font families in the theme.
* Load fonts eagerly when loading themes, instead of loading
  them lazily when rendering.

Co-Authored-By: Antonio Scandurra <me@as-cii.com>
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Max Brunsfeld
2021-08-26 15:06:00 -07:00
co-authored by Antonio Scandurra Nathan Sobo
parent ee9ee294ad
commit 3bb5610ad1
23 changed files with 438 additions and 397 deletions
+69 -82
View File
@@ -1,6 +1,5 @@
use crate::{
color::Color,
font_cache::FamilyId,
fonts::{FontId, TextStyle},
geometry::{
rect::RectF,
@@ -8,8 +7,7 @@ use crate::{
},
json::{ToJson, Value},
text_layout::Line,
DebugContext, Element, Event, EventContext, FontCache, LayoutContext, PaintContext,
SizeConstraint,
DebugContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
};
use serde::Deserialize;
use serde_json::json;
@@ -17,37 +15,32 @@ use smallvec::{smallvec, SmallVec};
pub struct Label {
text: String,
family_id: FamilyId,
font_size: f32,
style: LabelStyle,
highlight_indices: Vec<usize>,
}
#[derive(Clone, Debug, Default, Deserialize)]
#[derive(Clone, Debug, Deserialize)]
pub struct LabelStyle {
pub text: TextStyle,
pub highlight_text: Option<TextStyle>,
}
impl Label {
pub fn new(text: String, family_id: FamilyId, font_size: f32) -> Self {
Self {
impl From<TextStyle> for LabelStyle {
fn from(text: TextStyle) -> Self {
LabelStyle {
text,
family_id,
font_size,
highlight_indices: Default::default(),
style: Default::default(),
highlight_text: None,
}
}
}
pub fn with_style(mut self, style: &LabelStyle) -> Self {
self.style = style.clone();
self
}
pub fn with_default_color(mut self, color: Color) -> Self {
self.style.text.color = color;
self
impl Label {
pub fn new(text: String, style: impl Into<LabelStyle>) -> Self {
Self {
text,
highlight_indices: Default::default(),
style: style.into(),
}
}
pub fn with_highlights(mut self, indices: Vec<usize>) -> Self {
@@ -55,11 +48,8 @@ impl Label {
self
}
fn compute_runs(
&self,
font_cache: &FontCache,
font_id: FontId,
) -> SmallVec<[(usize, FontId, Color); 8]> {
fn compute_runs(&self) -> SmallVec<[(usize, FontId, Color); 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)];
}
@@ -68,12 +58,7 @@ impl Label {
.style
.highlight_text
.as_ref()
.and_then(|style| {
font_cache
.select_font(self.family_id, &style.font_properties)
.ok()
})
.unwrap_or(font_id);
.map_or(font_id, |style| style.font_id);
let mut highlight_indices = self.highlight_indices.iter().copied().peekable();
let mut runs = SmallVec::new();
@@ -123,18 +108,18 @@ impl Element for Label {
constraint: SizeConstraint,
cx: &mut LayoutContext,
) -> (Vector2F, Self::LayoutState) {
let font_id = cx
.font_cache
.select_font(self.family_id, &self.style.text.font_properties)
.unwrap();
let runs = self.compute_runs(&cx.font_cache, font_id);
let line =
cx.text_layout_cache
.layout_str(self.text.as_str(), self.font_size, runs.as_slice());
let runs = self.compute_runs();
let line = cx.text_layout_cache.layout_str(
self.text.as_str(),
self.style.text.font_size,
runs.as_slice(),
);
let size = vec2f(
line.width().max(constraint.min.x()).min(constraint.max.x()),
cx.font_cache.line_height(font_id, self.font_size).ceil(),
cx.font_cache
.line_height(self.style.text.font_id, self.style.text.font_size)
.ceil(),
);
(size, line)
@@ -169,15 +154,13 @@ impl Element for Label {
bounds: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
cx: &DebugContext,
_: &DebugContext,
) -> Value {
json!({
"type": "Label",
"bounds": bounds.to_json(),
"text": &self.text,
"highlight_indices": self.highlight_indices,
"font_family": cx.font_cache.family_name(self.family_id).unwrap(),
"font_size": self.font_size,
"style": self.style.to_json(),
})
}
@@ -201,48 +184,52 @@ mod tests {
#[crate::test(self)]
fn test_layout_label_with_highlights(cx: &mut crate::MutableAppContext) {
let menlo = cx.font_cache().load_family(&["Menlo"]).unwrap();
let menlo_regular = cx
.font_cache()
.select_font(menlo, &FontProperties::new())
.unwrap();
let menlo_bold = cx
.font_cache()
.select_font(menlo, FontProperties::new().weight(Weight::BOLD))
.unwrap();
let black = Color::black();
let red = Color::new(255, 0, 0, 255);
let default_style = TextStyle::new(
"Menlo",
12.,
Default::default(),
Color::black(),
cx.font_cache(),
)
.unwrap();
let highlight_style = TextStyle::new(
"Menlo",
12.,
*FontProperties::new().weight(Weight::BOLD),
Color::new(255, 0, 0, 255),
cx.font_cache(),
)
.unwrap();
let label = Label::new(
".αβγδε.ⓐⓑⓒⓓⓔ.abcde.".to_string(),
LabelStyle {
text: default_style.clone(),
highlight_text: Some(highlight_style.clone()),
},
)
.with_highlights(vec![
".α".len(),
".αβ".len(),
".αβγδ".len(),
".αβγδε.ⓐ".len(),
".αβγδε.ⓐⓑ".len(),
]);
let label = Label::new(".αβγδε.ⓐⓑⓒⓓⓔ.abcde.".to_string(), menlo, 12.0)
.with_style(&LabelStyle {
text: TextStyle {
color: black,
font_properties: Default::default(),
},
highlight_text: Some(TextStyle {
color: red,
font_properties: *FontProperties::new().weight(Weight::BOLD),
}),
})
.with_highlights(vec![
".α".len(),
".αβ".len(),
".αβγδ".len(),
".αβγδε.ⓐ".len(),
".αβγδε.ⓐⓑ".len(),
]);
let runs = label.compute_runs(cx.font_cache().as_ref(), menlo_regular);
let runs = label.compute_runs();
assert_eq!(
runs.as_slice(),
&[
(".α".len(), menlo_regular, black),
("βγ".len(), menlo_bold, red),
("δ".len(), menlo_regular, black),
("ε".len(), menlo_bold, red),
(".ⓐ".len(), menlo_regular, black),
("ⓑⓒ".len(), menlo_bold, red),
("ⓓⓔ.abcde.".len(), menlo_regular, black),
(".α".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
),
]
);
}
+8 -26
View File
@@ -1,6 +1,5 @@
use crate::{
color::Color,
font_cache::FamilyId,
fonts::TextStyle,
geometry::{
rect::RectF,
@@ -14,8 +13,6 @@ use serde_json::json;
pub struct Text {
text: String,
family_id: FamilyId,
font_size: f32,
style: TextStyle,
}
@@ -25,18 +22,8 @@ pub struct LayoutState {
}
impl Text {
pub fn new(text: String, family_id: FamilyId, font_size: f32) -> Self {
Self {
text,
family_id,
font_size,
style: Default::default(),
}
}
pub fn with_style(mut self, style: &TextStyle) -> Self {
self.style = style.clone();
self
pub fn new(text: String, style: TextStyle) -> Self {
Self { text, style }
}
pub fn with_default_color(mut self, color: Color) -> Self {
@@ -54,20 +41,17 @@ impl Element for Text {
constraint: SizeConstraint,
cx: &mut LayoutContext,
) -> (Vector2F, Self::LayoutState) {
let font_id = cx
.font_cache
.select_font(self.family_id, &self.style.font_properties)
.unwrap();
let line_height = cx.font_cache.line_height(font_id, self.font_size);
let font_id = self.style.font_id;
let line_height = cx.font_cache.line_height(font_id, self.style.font_size);
let mut wrapper = cx.font_cache.line_wrapper(font_id, self.font_size);
let mut wrapper = cx.font_cache.line_wrapper(font_id, self.style.font_size);
let mut lines = Vec::new();
let mut line_count = 0;
let mut max_line_width = 0_f32;
for line in self.text.lines() {
let shaped_line = cx.text_layout_cache.layout_str(
line,
self.font_size,
self.style.font_size,
&[(line.len(), font_id, self.style.color)],
);
let wrap_boundaries = wrapper
@@ -123,14 +107,12 @@ impl Element for Text {
bounds: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
cx: &DebugContext,
_: &DebugContext,
) -> Value {
json!({
"type": "Label",
"type": "Text",
"bounds": bounds.to_json(),
"text": &self.text,
"font_family": cx.font_cache.family_name(self.family_id).unwrap(),
"font_size": self.font_size,
"style": self.style.to_json(),
})
}