Checkpoint

This commit is contained in:
Nathan Sobo 2023-10-16 20:16:35 +02:00
parent 847376cd8f
commit 938dd8b9ca
7 changed files with 130 additions and 116 deletions

View file

@ -155,8 +155,9 @@ impl Refineable for TextStyleRefinement {
}
}
fn refined(self, refinement: Self::Refinement) -> Self {
todo!()
fn refined(mut self, refinement: Self::Refinement) -> Self {
self.refine(&refinement);
self
}
}

View file

@ -1,7 +1,8 @@
use crate::{
AnyElement, Bounds, Element, IntoAnyElement, LayoutId, Line, Pixels, Size, ViewContext,
size, AnyElement, Bounds, Element, IntoAnyElement, LayoutId, Line, Pixels, Size, ViewContext,
};
use parking_lot::Mutex;
use smallvec::SmallVec;
use std::{marker::PhantomData, sync::Arc};
use util::{arc_cow::ArcCow, ResultExt};
@ -75,7 +76,7 @@ impl<S: 'static + Send + Sync> Element for Text<S> {
let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
let element_state = element_state.clone();
move |known_dimensions, _| {
let Some(line_layout) = text_system
let Some(lines) = text_system
.layout_text(
text.as_ref(),
font_size,
@ -88,14 +89,13 @@ impl<S: 'static + Send + Sync> Element for Text<S> {
};
let size = Size {
width: line_layout.width(),
height: line_height,
width: lines.iter().map(|line| line.width()).max().unwrap(),
height: line_height * lines.len(),
};
element_state.lock().replace(TextElementState {
line: Arc::new(line_layout),
line_height,
});
element_state
.lock()
.replace(TextElementState { lines, line_height });
size
}
@ -111,22 +111,25 @@ impl<S: 'static + Send + Sync> Element for Text<S> {
element_state: &mut Self::ElementState,
cx: &mut ViewContext<S>,
) {
let line;
let line_height;
{
let element_state = element_state.lock();
let element_state = element_state
.as_ref()
.expect("measurement has not been performed");
line = element_state.line.clone();
line_height = element_state.line_height;
let element_state = element_state.lock();
let element_state = element_state
.as_ref()
.expect("measurement has not been performed");
let line_height = element_state.line_height;
let mut line_origin = bounds.origin;
for line in &element_state.lines {
let line_bounds = Bounds {
origin: line_origin,
size: size(line.width(), line_height),
};
line.paint(line_bounds, line_bounds, line_height, cx)
.log_err();
line_origin.y += line_height;
}
line.paint(bounds, bounds, line_height, cx).log_err();
}
}
pub struct TextElementState {
line: Arc<Line>,
lines: SmallVec<[Arc<Line>; 1]>,
line_height: Pixels,
}

View file

@ -656,6 +656,14 @@ impl Mul<f32> for Pixels {
}
}
impl Mul<usize> for Pixels {
type Output = Pixels;
fn mul(self, other: usize) -> Pixels {
Pixels(self.0 * other as f32)
}
}
impl Mul<Pixels> for f32 {
type Output = Pixels;

View file

@ -18,6 +18,7 @@ use collections::HashMap;
use core::fmt;
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
use std::{
cmp,
fmt::{Debug, Display, Formatter},
hash::{Hash, Hasher},
ops::{Deref, DerefMut},
@ -150,63 +151,72 @@ impl TextSystem {
font_size: Pixels,
runs: &[(usize, RunStyle)],
wrap_width: Option<Pixels>,
) -> Result<SmallVec<[Line; 1]>> {
) -> Result<SmallVec<[Arc<Line>; 1]>> {
let mut runs = runs
.iter()
.map(|(run_len, style)| (*run_len, style))
.peekable();
let mut font_runs: Vec<(usize, FontId)> =
self.font_runs_pool.lock().pop().unwrap_or_default();
let mut last_font: Option<&Font> = None;
for (len, style) in runs {
if let Some(last_font) = last_font.as_ref() {
if **last_font == style.font {
font_runs.last_mut().unwrap().0 += len;
continue;
}
}
last_font = Some(&style.font);
font_runs.push((*len, self.font_id(&style.font)?));
}
let mut lines = SmallVec::new();
let mut line_start = 0;
for line in text.split('\n') {
let line_end = line_start + line.len();
let mut layouts = SmallVec::new();
let mut start = 0;
let mut run_start = 0;
for line in text.lines() {
let end = start + line.len();
let mut run_end = run_start;
let mut line_length = 0;
for (len, _) in font_runs[run_start..].iter() {
line_length += len;
if *len >= line_length {
let mut last_font: Option<&Font> = None;
let mut decoration_runs = SmallVec::<[DecorationRun; 32]>::new();
let mut run_start = line_start;
while run_start < line_end {
let Some((run_len, run_style)) = runs.peek_mut() else {
break;
};
let run_len_within_line = cmp::min(line_end, run_start + *run_len) - run_start;
if last_font == Some(&run_style.font) {
font_runs.last_mut().unwrap().0 += run_len_within_line;
} else {
last_font = Some(&run_style.font);
font_runs.push((
run_len_within_line,
self.platform_text_system.font_id(&run_style.font)?,
));
}
run_end += 1;
if decoration_runs.last().map_or(false, |last_run| {
last_run.color == run_style.color && last_run.underline == run_style.underline
}) {
decoration_runs.last_mut().unwrap().len += run_len_within_line as u32;
} else {
decoration_runs.push(DecorationRun {
len: run_len_within_line as u32,
color: run_style.color,
underline: run_style.underline.clone(),
});
}
if run_len_within_line == *run_len {
runs.next();
} else {
// Preserve the remainder of the run for the next line
*run_len -= run_len_within_line;
}
run_start += run_len_within_line;
}
// If a run lands in the middle of a line, create an additional run for the remaining characters.
if line_length < end - start {
// Create a run for the part that fits this line.
let partial_run = font_runs[run_end];
partial_run.0 = line_length;
layouts.push(self.text_layout_cache.layout_line(
&text[start..start + line_length],
font_size,
&font_runs[run_start..=run_end],
));
// Update the original run to only include the part that does not fit this line.
font_runs[run_end].0 -= line_length;
} else {
layouts.push(self.text_layout_cache.layout_line(
&text[start..end],
font_size,
&font_runs[run_start..run_end],
));
run_start = run_end;
}
start = end + 1;
let layout = self
.text_layout_cache
.layout_line(line, font_size, &font_runs);
lines.push(Arc::new(Line::new(layout, decoration_runs)));
line_start = line_end + 1; // Skip `\n` character.
font_runs.clear();
}
font_runs.clear();
self.font_runs_pool.lock().push(font_runs);
Ok(layouts)
Ok(lines)
}
pub fn end_frame(&self) {

View file

@ -1,6 +1,6 @@
use crate::{
black, point, px, Bounds, FontId, Hsla, LineLayout, Pixels, Point, RunStyle, ShapedBoundary,
ShapedRun, UnderlineStyle, WindowContext,
black, point, px, Bounds, FontId, Hsla, LineLayout, Pixels, Point, ShapedBoundary, ShapedRun,
UnderlineStyle, WindowContext,
};
use anyhow::Result;
use smallvec::SmallVec;
@ -9,27 +9,22 @@ use std::sync::Arc;
#[derive(Default, Debug, Clone)]
pub struct Line {
layout: Arc<LineLayout>,
style_runs: SmallVec<[StyleRun; 32]>,
decoration_runs: SmallVec<[DecorationRun; 32]>,
}
#[derive(Debug, Clone)]
struct StyleRun {
len: u32,
color: Hsla,
underline: UnderlineStyle,
pub struct DecorationRun {
pub len: u32,
pub color: Hsla,
pub underline: Option<UnderlineStyle>,
}
impl Line {
pub fn new(layout: Arc<LineLayout>, runs: &[(usize, RunStyle)]) -> Self {
let mut style_runs = SmallVec::new();
for (len, style) in runs {
style_runs.push(StyleRun {
len: *len as u32,
color: style.color,
underline: style.underline.clone().unwrap_or_default(),
});
pub fn new(layout: Arc<LineLayout>, decoration_runs: SmallVec<[DecorationRun; 32]>) -> Self {
Self {
layout,
decoration_runs,
}
Self { layout, style_runs }
}
pub fn runs(&self) -> &[ShapedRun] {
@ -101,10 +96,10 @@ impl Line {
let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
let baseline_offset = point(px(0.), padding_top + self.layout.ascent);
let mut style_runs = self.style_runs.iter();
let mut style_runs = self.decoration_runs.iter();
let mut run_end = 0;
let mut color = black();
let mut underline = None;
let mut current_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
let text_system = cx.text_system().clone();
for run in &self.layout.runs {
@ -122,23 +117,21 @@ impl Line {
let mut finished_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
if glyph.index >= run_end {
if let Some(style_run) = style_runs.next() {
if let Some((_, underline_style)) = &mut underline {
if style_run.underline != *underline_style {
finished_underline = underline.take();
if let Some((_, underline_style)) = &mut current_underline {
if style_run.underline.as_ref() != Some(underline_style) {
finished_underline = current_underline.take();
}
}
if style_run.underline.thickness > px(0.) {
underline.get_or_insert((
if let Some(run_underline) = style_run.underline.as_ref() {
current_underline.get_or_insert((
point(
glyph_origin.x,
origin.y + baseline_offset.y + (self.layout.descent * 0.618),
),
UnderlineStyle {
color: Some(
style_run.underline.color.unwrap_or(style_run.color),
),
thickness: style_run.underline.thickness,
wavy: style_run.underline.wavy,
color: Some(run_underline.color.unwrap_or(style_run.color)),
thickness: run_underline.thickness,
wavy: run_underline.wavy,
},
));
}
@ -147,7 +140,7 @@ impl Line {
color = style_run.color;
} else {
run_end = self.layout.len;
finished_underline = underline.take();
finished_underline = current_underline.take();
}
}
@ -177,7 +170,7 @@ impl Line {
}
}
if let Some((underline_start, underline_style)) = underline.take() {
if let Some((underline_start, underline_style)) = current_underline.take() {
let line_end_x = origin.x + self.layout.width;
cx.paint_underline(
underline_start,
@ -201,10 +194,10 @@ impl Line {
let baseline_offset = point(px(0.), padding_top + self.layout.ascent);
let mut boundaries = boundaries.into_iter().peekable();
let mut color_runs = self.style_runs.iter();
let mut color_runs = self.decoration_runs.iter();
let mut style_run_end = 0;
let mut _color = black(); // todo!
let mut underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
let mut current_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
let mut glyph_origin = origin;
let mut prev_position = px(0.);
@ -217,7 +210,7 @@ impl Line {
.map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix)
{
boundaries.next();
if let Some((underline_origin, underline_style)) = underline.take() {
if let Some((underline_origin, underline_style)) = current_underline.take() {
cx.paint_underline(
underline_origin,
glyph_origin.x - underline_origin.x,
@ -234,31 +227,29 @@ impl Line {
if let Some(style_run) = color_runs.next() {
style_run_end += style_run.len as usize;
_color = style_run.color;
if let Some((_, underline_style)) = &mut underline {
if style_run.underline != *underline_style {
finished_underline = underline.take();
if let Some((_, underline_style)) = &mut current_underline {
if style_run.underline.as_ref() != Some(underline_style) {
finished_underline = current_underline.take();
}
}
if style_run.underline.thickness > px(0.) {
underline.get_or_insert((
if let Some(underline_style) = style_run.underline.as_ref() {
current_underline.get_or_insert((
glyph_origin
+ point(
px(0.),
baseline_offset.y + (self.layout.descent * 0.618),
),
UnderlineStyle {
color: Some(
style_run.underline.color.unwrap_or(style_run.color),
),
thickness: style_run.underline.thickness,
wavy: style_run.underline.wavy,
color: Some(underline_style.color.unwrap_or(style_run.color)),
thickness: underline_style.thickness,
wavy: underline_style.wavy,
},
));
}
} else {
style_run_end = self.layout.len;
_color = black();
finished_underline = underline.take();
finished_underline = current_underline.take();
}
}
@ -298,7 +289,7 @@ impl Line {
}
}
if let Some((underline_origin, underline_style)) = underline.take() {
if let Some((underline_origin, underline_style)) = current_underline.take() {
let line_end_x = glyph_origin.x + self.layout.width - prev_position;
cx.paint_underline(
underline_origin,

View file

@ -286,7 +286,7 @@ mod tests {
};
let text = "aa bbb cccc ddddd eeee";
let line = text_system
let lines = text_system
.layout_text(
text,
px(16.),
@ -300,6 +300,7 @@ mod tests {
None,
)
.unwrap();
let line = &lines[0];
let mut wrapper = LineWrapper::new(
text_system.font_id(&normal.font).unwrap(),

View file

@ -15,11 +15,11 @@ pub(crate) struct TextLayoutCache {
}
impl TextLayoutCache {
pub fn new(fonts: Arc<dyn PlatformTextSystem>) -> Self {
pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
Self {
prev_frame: Mutex::new(HashMap::new()),
curr_frame: RwLock::new(HashMap::new()),
platform_text_system: fonts,
platform_text_system,
}
}