Wrap lines in Text element
This commit is contained in:
@@ -1,19 +1,16 @@
|
||||
use crate::{
|
||||
color::Color,
|
||||
font_cache::FamilyId,
|
||||
fonts::{FontId, TextStyle},
|
||||
fonts::TextStyle,
|
||||
geometry::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::{ToJson, Value},
|
||||
text_layout::Line,
|
||||
DebugContext, Element, Event, EventContext, FontCache, LayoutContext, PaintContext,
|
||||
SizeConstraint,
|
||||
text_layout::{Line, LineWrapper, ShapedBoundary},
|
||||
DebugContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
pub struct Text {
|
||||
text: String,
|
||||
@@ -22,6 +19,12 @@ pub struct Text {
|
||||
style: TextStyle,
|
||||
}
|
||||
|
||||
pub struct LayoutState {
|
||||
line: Line,
|
||||
wrap_boundaries: Vec<ShapedBoundary>,
|
||||
line_height: f32,
|
||||
}
|
||||
|
||||
impl Text {
|
||||
pub fn new(text: String, family_id: FamilyId, font_size: f32) -> Self {
|
||||
Self {
|
||||
@@ -44,7 +47,7 @@ impl Text {
|
||||
}
|
||||
|
||||
impl Element for Text {
|
||||
type LayoutState = Line;
|
||||
type LayoutState = LayoutState;
|
||||
type PaintState = ();
|
||||
|
||||
fn layout(
|
||||
@@ -56,30 +59,44 @@ impl Element for Text {
|
||||
.font_cache
|
||||
.select_font(self.family_id, &self.style.font_properties)
|
||||
.unwrap();
|
||||
todo!()
|
||||
// let line =
|
||||
// cx.text_layout_cache
|
||||
// .layout_str(self.text.as_str(), self.font_size, runs.as_slice());
|
||||
let line_height = cx.font_cache.line_height(font_id, self.font_size);
|
||||
let line = cx.text_layout_cache.layout_str(
|
||||
self.text.as_str(),
|
||||
self.font_size,
|
||||
&[(self.text.len(), font_id, self.style.color)],
|
||||
);
|
||||
let mut wrapper = LineWrapper::acquire(font_id, self.font_size, cx.font_system.clone());
|
||||
let wrap_boundaries = wrapper
|
||||
.wrap_shaped_line(&self.text, &line, constraint.max.x())
|
||||
.collect::<Vec<_>>();
|
||||
let size = vec2f(
|
||||
line.width()
|
||||
.ceil()
|
||||
.max(constraint.min.x())
|
||||
.min(constraint.max.x()),
|
||||
(line_height * (wrap_boundaries.len() + 1) as f32).ceil(),
|
||||
);
|
||||
let layout = LayoutState {
|
||||
line,
|
||||
wrap_boundaries,
|
||||
line_height,
|
||||
};
|
||||
|
||||
// let size = vec2f(
|
||||
// line.width().max(constraint.min.x()).min(constraint.max.x()),
|
||||
// cx.font_cache.line_height(font_id, self.font_size).ceil(),
|
||||
// );
|
||||
|
||||
// (size, line)
|
||||
(size, layout)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: RectF,
|
||||
line: &mut Self::LayoutState,
|
||||
layout: &mut Self::LayoutState,
|
||||
cx: &mut PaintContext,
|
||||
) -> Self::PaintState {
|
||||
line.paint(
|
||||
layout.line.paint_wrapped(
|
||||
bounds.origin(),
|
||||
RectF::new(vec2f(0., 0.), bounds.size()),
|
||||
layout.line_height,
|
||||
layout.wrap_boundaries.iter().copied(),
|
||||
cx,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
fn dispatch_event(
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::{
|
||||
json::{self, ToJson},
|
||||
platform::Event,
|
||||
text_layout::TextLayoutCache,
|
||||
Action, AnyAction, AssetCache, ElementBox, Scene,
|
||||
Action, AnyAction, AssetCache, ElementBox, FontSystem, Scene,
|
||||
};
|
||||
use pathfinder_geometry::vector::{vec2f, Vector2F};
|
||||
use serde_json::json;
|
||||
@@ -114,6 +114,7 @@ impl Presenter {
|
||||
rendered_views: &mut self.rendered_views,
|
||||
parents: &mut self.parents,
|
||||
font_cache: &self.font_cache,
|
||||
font_system: cx.platform().fonts(),
|
||||
text_layout_cache: &self.text_layout_cache,
|
||||
asset_cache: &self.asset_cache,
|
||||
view_stack: Vec::new(),
|
||||
@@ -173,6 +174,7 @@ pub struct LayoutContext<'a> {
|
||||
parents: &'a mut HashMap<usize, usize>,
|
||||
view_stack: Vec<usize>,
|
||||
pub font_cache: &'a FontCache,
|
||||
pub font_system: Arc<dyn FontSystem>,
|
||||
pub text_layout_cache: &'a TextLayoutCache,
|
||||
pub asset_cache: &'a AssetCache,
|
||||
pub app: &'a mut MutableAppContext,
|
||||
|
||||
@@ -251,6 +251,54 @@ impl Line {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn paint_wrapped(
|
||||
&self,
|
||||
origin: Vector2F,
|
||||
line_height: f32,
|
||||
boundaries: impl IntoIterator<Item = ShapedBoundary>,
|
||||
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 mut boundaries = boundaries.into_iter().peekable();
|
||||
let mut color_runs = self.color_runs.iter();
|
||||
let mut color_end = 0;
|
||||
let mut color = Color::black();
|
||||
|
||||
let mut glyph_origin = baseline_origin;
|
||||
let mut prev_position = 0.;
|
||||
for run in &self.layout.runs {
|
||||
for (glyph_ix, glyph) in run.glyphs.iter().enumerate() {
|
||||
if boundaries.peek().map_or(false, |b| b.glyph_ix == glyph_ix) {
|
||||
boundaries.next();
|
||||
glyph_origin = vec2f(0., glyph_origin.y() + line_height);
|
||||
} else {
|
||||
glyph_origin.set_x(glyph_origin.x() + glyph.position.x() - prev_position);
|
||||
}
|
||||
prev_position = glyph.position.x();
|
||||
|
||||
if glyph.index >= color_end {
|
||||
if let Some(next_run) = color_runs.next() {
|
||||
color_end += next_run.0 as usize;
|
||||
color = next_run.1;
|
||||
} else {
|
||||
color_end = self.layout.len;
|
||||
color = Color::black();
|
||||
}
|
||||
}
|
||||
|
||||
cx.scene.push_glyph(scene::Glyph {
|
||||
font_id: run.font_id,
|
||||
font_size: self.layout.font_size,
|
||||
id: glyph.id,
|
||||
origin: origin + glyph_origin,
|
||||
color,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Run {
|
||||
|
||||
Reference in New Issue
Block a user