Accept colors in styling runs passed to layout_str

Also move from the runs being expressed as ranges to the runs being expressed as a sequence of contiguous scalar lengths.
This commit is contained in:
Nathan Sobo
2021-05-21 14:10:38 -06:00
parent b42f5142f7
commit 92ff7f4e5e
7 changed files with 187 additions and 173 deletions
+58 -75
View File
@@ -1,4 +1,5 @@
use serde_json::json;
use smallvec::{smallvec, SmallVec};
use crate::{
color::ColorU,
@@ -13,7 +14,6 @@ use crate::{
AfterLayoutContext, DebugContext, Element, Event, EventContext, FontCache, LayoutContext,
PaintContext, SizeConstraint,
};
use std::{ops::Range, sync::Arc};
pub struct Label {
text: String,
@@ -23,11 +23,6 @@ pub struct Label {
highlights: Option<Highlights>,
}
pub struct LayoutState {
line: Arc<Line>,
colors: Vec<(Range<usize>, ColorU)>,
}
pub struct Highlights {
color: ColorU,
indices: Vec<usize>,
@@ -59,60 +54,56 @@ impl Label {
self
}
fn layout_text(
fn compute_runs(
&self,
font_cache: &FontCache,
font_id: FontId,
) -> (Vec<(Range<usize>, FontId)>, Vec<(Range<usize>, ColorU)>) {
let text_len = self.text.len();
let mut styles;
let mut colors;
) -> SmallVec<[(usize, FontId, ColorU); 8]> {
if let Some(highlights) = self.highlights.as_ref() {
styles = Vec::new();
colors = Vec::new();
let highlight_font_id = font_cache
.select_font(self.family_id, &highlights.font_properties)
.unwrap_or(font_id);
let mut pending_highlight: Option<Range<usize>> = None;
for ix in &highlights.indices {
if let Some(pending_highlight) = pending_highlight.as_mut() {
if *ix == pending_highlight.end {
pending_highlight.end += 1;
} else {
styles.push((pending_highlight.clone(), highlight_font_id));
colors.push((pending_highlight.clone(), highlights.color));
styles.push((pending_highlight.end..*ix, font_id));
colors.push((pending_highlight.end..*ix, ColorU::black()));
*pending_highlight = *ix..*ix + 1;
}
} else {
styles.push((0..*ix, font_id));
colors.push((0..*ix, ColorU::black()));
pending_highlight = Some(*ix..*ix + 1);
}
}
if let Some(pending_highlight) = pending_highlight.as_mut() {
styles.push((pending_highlight.clone(), highlight_font_id));
colors.push((pending_highlight.clone(), highlights.color));
if text_len > pending_highlight.end {
styles.push((pending_highlight.end..text_len, font_id));
colors.push((pending_highlight.end..text_len, ColorU::black()));
}
} else {
styles.push((0..text_len, font_id));
colors.push((0..text_len, ColorU::black()));
}
} else {
styles = vec![(0..text_len, font_id)];
colors = vec![(0..text_len, ColorU::black())];
}
(styles, colors)
let mut highlight_indices = highlights.indices.iter().copied().peekable();
let mut runs = SmallVec::new();
for (char_ix, c) in self.text.char_indices() {
let mut font_id = font_id;
let mut color = ColorU::black();
if let Some(highlight_ix) = highlight_indices.peek() {
if char_ix == *highlight_ix {
font_id = highlight_font_id;
color = highlights.color;
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 {
*last_len += c.len_utf8();
false
} else {
true
}
} else {
true
};
if push_new_run {
runs.push((c.len_utf8(), font_id, color));
}
}
runs
} else {
smallvec![(self.text.len(), font_id, ColorU::black())]
}
}
}
impl Element for Label {
type LayoutState = LayoutState;
type LayoutState = Line;
type PaintState = ();
fn layout(
@@ -124,17 +115,17 @@ impl Element for Label {
.font_cache
.select_font(self.family_id, &self.font_properties)
.unwrap();
let (styles, colors) = self.layout_text(&ctx.font_cache, font_id);
let runs = self.compute_runs(&ctx.font_cache, font_id);
let line =
ctx.text_layout_cache
.layout_str(self.text.as_str(), self.font_size, styles.as_slice());
.layout_str(self.text.as_str(), self.font_size, runs.as_slice());
let size = vec2f(
line.width.max(constraint.min.x()).min(constraint.max.x()),
line.width().max(constraint.min.x()).min(constraint.max.x()),
ctx.font_cache.line_height(font_id, self.font_size).ceil(),
);
(size, LayoutState { line, colors })
(size, line)
}
fn after_layout(&mut self, _: Vector2F, _: &mut Self::LayoutState, _: &mut AfterLayoutContext) {
@@ -143,13 +134,12 @@ impl Element for Label {
fn paint(
&mut self,
bounds: RectF,
layout: &mut Self::LayoutState,
line: &mut Self::LayoutState,
ctx: &mut PaintContext,
) -> Self::PaintState {
layout.line.paint(
line.paint(
bounds.origin(),
RectF::new(vec2f(0., 0.), bounds.size()),
layout.colors.as_slice(),
ctx,
)
}
@@ -226,28 +216,21 @@ mod tests {
],
);
let (styles, colors) = label.layout_text(app.font_cache().as_ref(), menlo_regular);
assert_eq!(styles.len(), colors.len());
let mut spans = Vec::new();
for ((style_range, font_id), (color_range, color)) in styles.into_iter().zip(colors) {
assert_eq!(style_range, color_range);
spans.push((style_range, font_id, color));
}
let runs = label.compute_runs(app.font_cache().as_ref(), menlo_regular);
assert_eq!(
spans,
runs.as_slice(),
&[
(0..3, menlo_regular, black),
(3..4, menlo_bold, red),
(4..5, menlo_regular, black),
(5..6, menlo_bold, red),
(6..9, menlo_regular, black),
(9..10, menlo_bold, red),
(10..15, menlo_regular, black),
(15..16, menlo_bold, red),
(16..18, menlo_regular, black),
(18..19, menlo_bold, red),
(19..34, menlo_regular, black)
(3, menlo_regular, black),
(1, menlo_bold, red),
(1, menlo_regular, black),
(1, menlo_bold, red),
(3, menlo_regular, black),
(1, menlo_bold, red),
(5, menlo_regular, black),
(1, menlo_bold, red),
(2, menlo_regular, black),
(1, menlo_bold, red),
(15, menlo_regular, black),
]
);
}