@@ -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 size = vec2f(
- // line.width().max(constraint.min.x()).min(constraint.max.x()),
- // cx.font_cache.line_height(font_id, self.font_size).ceil(),
- // );
+ 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,
+ };
- // (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 {