From 399ecaabc6b09bd1a43b9e1069e35b33ba586a9f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 25 Aug 2021 14:43:22 +0200 Subject: [PATCH] Wrap lines in `Text` element --- gpui/src/elements/text.rs | 59 +++++++++++++++++++++++++-------------- gpui/src/presenter.rs | 4 ++- gpui/src/text_layout.rs | 48 +++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 22 deletions(-) diff --git a/gpui/src/elements/text.rs b/gpui/src/elements/text.rs index 4c6d0559e680e97e93e23fa414f72344573f3ce2..5d30fe03fceaa7035fc5235eb1fa18ec7f50d636 100644 --- a/gpui/src/elements/text.rs +++ b/gpui/src/elements/text.rs @@ -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, + 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::>(); + 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( diff --git a/gpui/src/presenter.rs b/gpui/src/presenter.rs index ac4652482cc72abc76640af671259a376eebdd97..030b8262ccb5d2d4cea271d46a9eb96ab90a387c 100644 --- a/gpui/src/presenter.rs +++ b/gpui/src/presenter.rs @@ -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, view_stack: Vec, pub font_cache: &'a FontCache, + pub font_system: Arc, pub text_layout_cache: &'a TextLayoutCache, pub asset_cache: &'a AssetCache, pub app: &'a mut MutableAppContext, diff --git a/gpui/src/text_layout.rs b/gpui/src/text_layout.rs index e19f82ed55c76ad470756bb1db9019f5d7022e8b..9d171e180cdfb12ca4294e4df991977b416ba259 100644 --- a/gpui/src/text_layout.rs +++ b/gpui/src/text_layout.rs @@ -251,6 +251,54 @@ impl Line { } } } + + pub fn paint_wrapped( + &self, + origin: Vector2F, + line_height: f32, + boundaries: impl IntoIterator, + 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 {