mirror of
https://github.com/penpot/penpot.git
synced 2025-12-11 22:14:05 +01:00
🐛 Fix paragraph layout width on autowidth
This commit is contained in:
@@ -511,7 +511,7 @@ impl RenderState {
|
||||
});
|
||||
|
||||
let text_content = text_content.new_bounds(shape.selrect());
|
||||
let mut paragraphs = text_content.get_skia_paragraphs(
|
||||
let mut paragraphs = text_content.to_paragraphs(
|
||||
shape.image_filter(1.).as_ref(),
|
||||
shape.mask_filter(1.).as_ref(),
|
||||
);
|
||||
@@ -524,7 +524,7 @@ impl RenderState {
|
||||
text::render(self, &shape, &mut paragraphs, None, None);
|
||||
|
||||
for stroke in shape.visible_strokes().rev() {
|
||||
let mut stroke_paragraphs = text_content.get_skia_stroke_paragraphs(
|
||||
let mut stroke_paragraphs = text_content.to_stroke_paragraphs(
|
||||
stroke,
|
||||
&shape.selrect(),
|
||||
shape.image_filter(1.).as_ref(),
|
||||
|
||||
@@ -14,10 +14,17 @@ pub fn render(
|
||||
let canvas = render_state
|
||||
.surfaces
|
||||
.canvas(surface_id.unwrap_or(SurfaceId::Fills));
|
||||
let container_height = shape.selrect().height();
|
||||
|
||||
// Calculate total height for vertical alignment
|
||||
let total_content_height = calculate_all_paragraphs_height(paragraphs, shape.bounds().width());
|
||||
// Width
|
||||
let paragraph_width = if let crate::shapes::Type::Text(text_content) = &shape.shape_type {
|
||||
text_content.get_width()
|
||||
} else {
|
||||
shape.width()
|
||||
};
|
||||
|
||||
// Height
|
||||
let container_height = shape.selrect().height();
|
||||
let total_content_height = calculate_all_paragraphs_height(paragraphs, paragraph_width);
|
||||
let mut global_offset_y = match shape.vertical_align() {
|
||||
VerticalAlign::Center => (container_height - total_content_height) / 2.0,
|
||||
VerticalAlign::Bottom => container_height - total_content_height,
|
||||
@@ -48,8 +55,7 @@ pub fn render(
|
||||
continue;
|
||||
}
|
||||
|
||||
skia_paragraph.layout(shape.bounds().width());
|
||||
|
||||
skia_paragraph.layout(paragraph_width);
|
||||
let paragraph_height = skia_paragraph.height();
|
||||
let xy = (shape.selrect().x(), shape.selrect().y() + group_offset_y);
|
||||
skia_paragraph.paint(canvas, xy);
|
||||
@@ -126,7 +132,7 @@ pub fn render(
|
||||
// For stroke groups (multiple elements), increment global_offset_y once per group
|
||||
if group_len > 1 {
|
||||
let mut first_paragraph = group[0].build();
|
||||
first_paragraph.layout(shape.bounds().width());
|
||||
first_paragraph.layout(paragraph_width);
|
||||
global_offset_y += first_paragraph.height();
|
||||
} else {
|
||||
// For regular paragraphs, global_offset_y was already incremented inside the loop
|
||||
|
||||
@@ -10,8 +10,8 @@ use crate::math::bools;
|
||||
use crate::math::{self as math, identitish, Bounds, Matrix, Point};
|
||||
|
||||
use crate::shapes::{
|
||||
auto_height, set_paragraphs_width, ConstraintH, ConstraintV, Frame, Group, GrowType, Layout,
|
||||
Modifier, Shape, StructureEntry, TransformEntry, Type,
|
||||
auto_height, ConstraintH, ConstraintV, Frame, Group, GrowType, Layout, Modifier, Shape,
|
||||
StructureEntry, TransformEntry, Type,
|
||||
};
|
||||
use crate::state::ShapesPool;
|
||||
use crate::state::State;
|
||||
@@ -197,18 +197,34 @@ fn propagate_transform(
|
||||
let mut transform = entry.transform;
|
||||
|
||||
if let Type::Text(content) = &shape.shape_type {
|
||||
if content.grow_type() == GrowType::AutoHeight {
|
||||
let mut paragraphs = content.get_skia_paragraphs(None, None);
|
||||
set_paragraphs_width(shape_bounds_after.width(), &mut paragraphs);
|
||||
let height = auto_height(&mut paragraphs, shape_bounds_after.width());
|
||||
let resize_transform = math::resize_matrix(
|
||||
&shape_bounds_after,
|
||||
&shape_bounds_after,
|
||||
shape_bounds_after.width(),
|
||||
height,
|
||||
);
|
||||
shape_bounds_after = shape_bounds_after.transform(&resize_transform);
|
||||
transform.post_concat(&resize_transform);
|
||||
match content.grow_type() {
|
||||
GrowType::AutoHeight => {
|
||||
let paragraph_width = shape_bounds_after.width();
|
||||
let mut paragraphs = content.to_paragraphs(None, None);
|
||||
let height = auto_height(&mut paragraphs, paragraph_width);
|
||||
let resize_transform = math::resize_matrix(
|
||||
&shape_bounds_after,
|
||||
&shape_bounds_after,
|
||||
shape_bounds_after.width(),
|
||||
height,
|
||||
);
|
||||
shape_bounds_after = shape_bounds_after.transform(&resize_transform);
|
||||
transform.post_concat(&resize_transform);
|
||||
}
|
||||
GrowType::AutoWidth => {
|
||||
let paragraph_width = content.get_width();
|
||||
let mut paragraphs = content.to_paragraphs(None, None);
|
||||
let height = auto_height(&mut paragraphs, paragraph_width);
|
||||
let resize_transform = math::resize_matrix(
|
||||
&shape_bounds_after,
|
||||
&shape_bounds_after,
|
||||
paragraph_width,
|
||||
height,
|
||||
);
|
||||
shape_bounds_after = shape_bounds_after.transform(&resize_transform);
|
||||
transform.post_concat(&resize_transform);
|
||||
}
|
||||
GrowType::Fixed => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,14 +41,28 @@ pub struct TextContent {
|
||||
pub grow_type: GrowType,
|
||||
}
|
||||
|
||||
pub fn set_paragraphs_width(width: f32, paragraphs: &mut Vec<Vec<ParagraphBuilder>>) {
|
||||
for group in paragraphs {
|
||||
for p in group {
|
||||
let mut paragraph = p.build();
|
||||
paragraph.layout(f32::MAX);
|
||||
paragraph.layout(f32::max(width, paragraph.min_intrinsic_width().ceil()));
|
||||
}
|
||||
}
|
||||
pub fn build_paragraphs_with_width(
|
||||
paragraphs: &mut [Vec<ParagraphBuilder>],
|
||||
width: f32,
|
||||
) -> Vec<Vec<skia_safe::textlayout::Paragraph>> {
|
||||
paragraphs
|
||||
.iter_mut()
|
||||
.map(|builders| {
|
||||
builders
|
||||
.iter_mut()
|
||||
.map(|builder| {
|
||||
let mut paragraph = builder.build();
|
||||
// For auto-width, always layout with infinite width first to get intrinsic width
|
||||
paragraph.layout(f32::MAX);
|
||||
let intrinsic_width = paragraph.max_intrinsic_width().ceil();
|
||||
// Use the larger of the requested width or intrinsic width to prevent line breaks
|
||||
let final_width = f32::max(width, intrinsic_width);
|
||||
paragraph.layout(final_width);
|
||||
paragraph
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl TextContent {
|
||||
@@ -177,43 +191,14 @@ impl TextContent {
|
||||
paragraph_group
|
||||
}
|
||||
|
||||
pub fn collect_paragraphs(
|
||||
&self,
|
||||
mut paragraphs: Vec<Vec<ParagraphBuilder>>,
|
||||
) -> Vec<Vec<ParagraphBuilder>> {
|
||||
pub fn get_width(&self) -> f32 {
|
||||
if self.grow_type() == GrowType::AutoWidth {
|
||||
set_paragraphs_width(f32::MAX, &mut paragraphs);
|
||||
let max_width = auto_width(&mut paragraphs, self.width()).ceil();
|
||||
set_paragraphs_width(max_width, &mut paragraphs);
|
||||
let temp_paragraphs = self.to_paragraphs(None, None);
|
||||
let mut temp_paragraphs = temp_paragraphs;
|
||||
auto_width(&mut temp_paragraphs, f32::MAX).ceil()
|
||||
} else {
|
||||
set_paragraphs_width(self.width(), &mut paragraphs);
|
||||
self.width()
|
||||
}
|
||||
paragraphs
|
||||
}
|
||||
|
||||
pub fn get_skia_paragraphs(
|
||||
&self,
|
||||
blur: Option<&ImageFilter>,
|
||||
blur_mask: Option<&MaskFilter>,
|
||||
) -> Vec<Vec<ParagraphBuilder>> {
|
||||
self.collect_paragraphs(self.to_paragraphs(blur, blur_mask))
|
||||
}
|
||||
|
||||
pub fn get_skia_stroke_paragraphs(
|
||||
&self,
|
||||
stroke: &Stroke,
|
||||
bounds: &Rect,
|
||||
blur: Option<&ImageFilter>,
|
||||
blur_mask: Option<&MaskFilter>,
|
||||
count_inner_strokes: usize,
|
||||
) -> Vec<Vec<ParagraphBuilder>> {
|
||||
self.collect_paragraphs(self.to_stroke_paragraphs(
|
||||
stroke,
|
||||
bounds,
|
||||
blur,
|
||||
blur_mask,
|
||||
count_inner_strokes,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn grow_type(&self) -> GrowType {
|
||||
@@ -225,9 +210,10 @@ impl TextContent {
|
||||
}
|
||||
|
||||
pub fn visual_bounds(&self) -> (f32, f32) {
|
||||
let paragraph_width = self.get_width();
|
||||
let mut paragraphs = self.to_paragraphs(None, None);
|
||||
let height = auto_height(&mut paragraphs, self.width());
|
||||
(self.width(), height)
|
||||
let paragraph_height = auto_height(&mut paragraphs, paragraph_width);
|
||||
(paragraph_width, paragraph_height)
|
||||
}
|
||||
|
||||
pub fn transform(&mut self, transform: &Matrix) {
|
||||
@@ -728,19 +714,7 @@ pub fn get_built_paragraphs(
|
||||
paragraphs: &mut [Vec<ParagraphBuilder>],
|
||||
width: f32,
|
||||
) -> Vec<Vec<skia_safe::textlayout::Paragraph>> {
|
||||
paragraphs
|
||||
.iter_mut()
|
||||
.map(|builders| {
|
||||
builders
|
||||
.iter_mut()
|
||||
.map(|builder_handle| {
|
||||
let mut paragraph = builder_handle.build();
|
||||
paragraph.layout(width);
|
||||
paragraph
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect()
|
||||
build_paragraphs_with_width(paragraphs, width)
|
||||
}
|
||||
|
||||
pub fn auto_width(paragraphs: &mut [Vec<ParagraphBuilder>], width: f32) -> f32 {
|
||||
@@ -754,15 +728,6 @@ pub fn auto_width(paragraphs: &mut [Vec<ParagraphBuilder>], width: f32) -> f32 {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn max_width(paragraphs: &mut [Vec<ParagraphBuilder>], width: f32) -> f32 {
|
||||
let built_paragraphs = get_built_paragraphs(paragraphs, width);
|
||||
|
||||
built_paragraphs
|
||||
.iter()
|
||||
.flatten()
|
||||
.fold(0.0, |max_width, p| f32::max(p.max_width(), max_width))
|
||||
}
|
||||
|
||||
pub fn auto_height(paragraphs: &mut [Vec<ParagraphBuilder>], width: f32) -> f32 {
|
||||
paragraphs.iter_mut().fold(0.0, |auto_height, p| {
|
||||
p.iter_mut().fold(auto_height, |auto_height, paragraph| {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::shapes::text::TextContent;
|
||||
use skia_safe::{
|
||||
self as skia, textlayout::Paragraph as SkiaParagraph, textlayout::ParagraphBuilder,
|
||||
FontMetrics, Point, Rect, TextBlob,
|
||||
self as skia, textlayout::Paragraph as SkiaParagraph, FontMetrics, Point, Rect, TextBlob,
|
||||
};
|
||||
use std::ops::Deref;
|
||||
|
||||
@@ -17,16 +16,11 @@ impl TextPaths {
|
||||
Self(content)
|
||||
}
|
||||
|
||||
pub fn get_skia_paragraphs(&self) -> Vec<Vec<ParagraphBuilder>> {
|
||||
let paragraphs = self.to_paragraphs(None, None);
|
||||
self.collect_paragraphs(paragraphs)
|
||||
}
|
||||
|
||||
pub fn get_paths(&self, antialias: bool) -> Vec<(skia::Path, skia::Paint)> {
|
||||
let mut paths = Vec::new();
|
||||
|
||||
let mut offset_y = self.bounds.y();
|
||||
let mut paragraphs = self.get_skia_paragraphs();
|
||||
let mut paragraphs = self.to_paragraphs(None, None);
|
||||
|
||||
for paragraphs in paragraphs.iter_mut() {
|
||||
for paragraph_builder in paragraphs.iter_mut() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::mem;
|
||||
use crate::shapes::{auto_height, auto_width, max_width, GrowType, RawTextData, Type};
|
||||
use crate::shapes::{auto_height, build_paragraphs_with_width, GrowType, RawTextData, Type};
|
||||
|
||||
use crate::STATE;
|
||||
use crate::{with_current_shape, with_current_shape_mut};
|
||||
@@ -43,19 +43,27 @@ pub extern "C" fn get_text_dimensions() -> *mut u8 {
|
||||
height = shape.selrect.height();
|
||||
|
||||
if let Type::Text(content) = &shape.shape_type {
|
||||
let mut paragraphs = content.get_skia_paragraphs(
|
||||
shape.image_filter(1.).as_ref(),
|
||||
shape.mask_filter(1.).as_ref(),
|
||||
);
|
||||
m_width = max_width(&mut paragraphs, width);
|
||||
// 1. Reset Paragraphs
|
||||
let paragraph_width = content.get_width();
|
||||
let mut paragraphs = content.to_paragraphs(None, None);
|
||||
let built_paragraphs = build_paragraphs_with_width(&mut paragraphs, paragraph_width);
|
||||
|
||||
// 2. Max Width Calculation
|
||||
m_width = built_paragraphs
|
||||
.iter()
|
||||
.flatten()
|
||||
.fold(0.0, |max_width, p| f32::max(p.max_width(), max_width));
|
||||
|
||||
// 3. Width and Height Calculation
|
||||
match content.grow_type() {
|
||||
GrowType::AutoHeight => {
|
||||
height = auto_height(&mut paragraphs, width).ceil();
|
||||
let mut paragraph_height = content.to_paragraphs(None, None);
|
||||
height = auto_height(&mut paragraph_height, paragraph_width).ceil();
|
||||
}
|
||||
GrowType::AutoWidth => {
|
||||
width = auto_width(&mut paragraphs, width).ceil();
|
||||
height = auto_height(&mut paragraphs, width).ceil();
|
||||
width = paragraph_width;
|
||||
let mut paragraph_height = content.to_paragraphs(None, None);
|
||||
height = auto_height(&mut paragraph_height, paragraph_width).ceil();
|
||||
}
|
||||
GrowType::Fixed => {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user