mirror of
https://github.com/penpot/penpot.git
synced 2026-05-14 20:43:55 +00:00
Merge pull request #9562 from penpot/elenatorro-fix-layout-freeze
🐛 Reflow only when needed
This commit is contained in:
commit
2685389aad
@ -167,6 +167,7 @@ fn set_pixel_precision(transform: &mut Matrix, bounds: &mut Bounds) {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn propagate_transform(
|
||||
entry: TransformEntry,
|
||||
pixel_precision: bool,
|
||||
@ -175,6 +176,7 @@ fn propagate_transform(
|
||||
bounds: &mut HashMap<Uuid, Bounds>,
|
||||
modifiers: &mut HashMap<Uuid, Matrix>,
|
||||
reflown: &mut HashSet<Uuid>,
|
||||
reflowed_shapes: &mut HashSet<Uuid>,
|
||||
) -> Result<()> {
|
||||
let Some(shape) = state.shapes.get(&entry.id) else {
|
||||
return Ok(());
|
||||
@ -190,7 +192,11 @@ fn propagate_transform(
|
||||
if !is_close_to(shape_bounds_before.width(), shape_bounds_after.width())
|
||||
|| !is_close_to(shape_bounds_before.height(), shape_bounds_after.height())
|
||||
{
|
||||
if let Type::Text(text_content) = &mut shape.shape_type.clone() {
|
||||
if let Type::Text(text_content) = &shape.shape_type {
|
||||
let width_changed =
|
||||
!is_close_to(shape_bounds_before.width(), shape_bounds_after.width());
|
||||
let height_changed =
|
||||
!is_close_to(shape_bounds_before.height(), shape_bounds_after.height());
|
||||
let resized_selrect = math::Rect::from_xywh(
|
||||
shape.selrect.left(),
|
||||
shape.selrect.top(),
|
||||
@ -199,12 +205,15 @@ fn propagate_transform(
|
||||
);
|
||||
match text_content.grow_type() {
|
||||
GrowType::AutoHeight => {
|
||||
// For auto-height, always update layout when width changes
|
||||
// because the new width affects how text wraps
|
||||
let width_changed =
|
||||
!is_close_to(shape_bounds_before.width(), shape_bounds_after.width());
|
||||
if width_changed || text_content.needs_update_layout() {
|
||||
text_content.update_layout(resized_selrect);
|
||||
let height_before = text_content.size.height;
|
||||
let new_height = if width_changed {
|
||||
let mut clone = text_content.clone();
|
||||
clone.update_layout(resized_selrect);
|
||||
clone.size.height
|
||||
} else {
|
||||
height_before
|
||||
};
|
||||
if !is_close_to(height_before, new_height) && reflowed_shapes.insert(shape.id) {
|
||||
entries.push_back(Modifier::reflow(shape.id, false));
|
||||
|
||||
if let Some(parent_id) = shape.parent_id {
|
||||
@ -215,31 +224,28 @@ fn propagate_transform(
|
||||
}
|
||||
}
|
||||
}
|
||||
let height = text_content.size.height;
|
||||
let resize_transform = math::resize_matrix(
|
||||
&shape_bounds_after,
|
||||
&shape_bounds_after,
|
||||
shape_bounds_after.width(),
|
||||
height,
|
||||
new_height,
|
||||
);
|
||||
shape_bounds_after = shape_bounds_after.transform(&resize_transform);
|
||||
transform.post_concat(&resize_transform);
|
||||
}
|
||||
GrowType::AutoWidth => {
|
||||
// For auto-width, always update layout when height changes
|
||||
// because the new height affects how text flows
|
||||
let height_changed =
|
||||
!is_close_to(shape_bounds_before.height(), shape_bounds_after.height());
|
||||
if height_changed || text_content.needs_update_layout() {
|
||||
text_content.update_layout(resized_selrect);
|
||||
}
|
||||
let width = text_content.width();
|
||||
let height = text_content.size.height;
|
||||
let (new_width, new_height) = if height_changed {
|
||||
let mut clone = text_content.clone();
|
||||
clone.update_layout(resized_selrect);
|
||||
(clone.width(), clone.size.height)
|
||||
} else {
|
||||
(text_content.width(), text_content.size.height)
|
||||
};
|
||||
let resize_transform = math::resize_matrix(
|
||||
&shape_bounds_after,
|
||||
&shape_bounds_after,
|
||||
width,
|
||||
height,
|
||||
new_width,
|
||||
new_height,
|
||||
);
|
||||
shape_bounds_after = shape_bounds_after.transform(&resize_transform);
|
||||
transform.post_concat(&resize_transform);
|
||||
@ -404,6 +410,7 @@ pub fn propagate_modifiers(
|
||||
// In order for loop to eventualy finish, we limit the flex reflow to just
|
||||
// one (the reflown set).
|
||||
while !entries.is_empty() {
|
||||
let mut reflowed_shapes = HashSet::<Uuid>::new();
|
||||
while let Some(modifier) = entries.pop_front() {
|
||||
match modifier {
|
||||
Modifier::Transform(entry, pixel) => propagate_transform(
|
||||
@ -414,6 +421,7 @@ pub fn propagate_modifiers(
|
||||
&mut bounds,
|
||||
&mut modifiers,
|
||||
&mut reflown,
|
||||
&mut reflowed_shapes,
|
||||
)?,
|
||||
Modifier::Reflow(id, force_reflow) => {
|
||||
if force_reflow {
|
||||
|
||||
@ -333,13 +333,26 @@ pub fn calculate_normalized_line_height(
|
||||
normalized_line_height
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TextContent {
|
||||
pub paragraphs: Vec<Paragraph>,
|
||||
pub bounds: Rect,
|
||||
pub grow_type: GrowType,
|
||||
pub size: TextContentSize,
|
||||
pub layout: TextContentLayout,
|
||||
content_version: u64,
|
||||
layout_version: u64,
|
||||
layout_width: Option<f32>,
|
||||
}
|
||||
|
||||
impl PartialEq for TextContent {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.paragraphs == other.paragraphs
|
||||
&& self.bounds == other.bounds
|
||||
&& self.grow_type == other.grow_type
|
||||
&& self.size == other.size
|
||||
&& self.layout == other.layout
|
||||
}
|
||||
}
|
||||
|
||||
impl TextContent {
|
||||
@ -350,6 +363,9 @@ impl TextContent {
|
||||
grow_type,
|
||||
size: TextContentSize::default(),
|
||||
layout: TextContentLayout::new(),
|
||||
content_version: 0,
|
||||
layout_version: 0,
|
||||
layout_width: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,6 +378,9 @@ impl TextContent {
|
||||
grow_type,
|
||||
size: TextContentSize::new_with_size(bounds.width(), bounds.height()),
|
||||
layout: TextContentLayout::new(),
|
||||
content_version: 0,
|
||||
layout_version: 0,
|
||||
layout_width: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -385,6 +404,7 @@ impl TextContent {
|
||||
|
||||
pub fn add_paragraph(&mut self, paragraph: Paragraph) {
|
||||
self.paragraphs.push(paragraph);
|
||||
self.content_version = self.content_version.wrapping_add(1);
|
||||
}
|
||||
|
||||
pub fn paragraphs(&self) -> &[Paragraph] {
|
||||
@ -392,6 +412,7 @@ impl TextContent {
|
||||
}
|
||||
|
||||
pub fn paragraphs_mut(&mut self) -> &mut Vec<Paragraph> {
|
||||
self.content_version = self.content_version.wrapping_add(1);
|
||||
&mut self.paragraphs
|
||||
}
|
||||
|
||||
@ -408,7 +429,10 @@ impl TextContent {
|
||||
}
|
||||
|
||||
pub fn set_grow_type(&mut self, grow_type: GrowType) {
|
||||
self.grow_type = grow_type;
|
||||
if self.grow_type != grow_type {
|
||||
self.grow_type = grow_type;
|
||||
self.content_version = self.content_version.wrapping_add(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute a tight text rect from laid-out Skia paragraphs using glyph
|
||||
@ -891,6 +915,15 @@ impl TextContent {
|
||||
}
|
||||
|
||||
pub fn update_layout(&mut self, selrect: Rect) -> TextContentSize {
|
||||
if !self.layout.needs_update()
|
||||
&& self.layout_version == self.content_version
|
||||
&& self
|
||||
.layout_width
|
||||
.is_some_and(|w| (w - selrect.width()).abs() < f32::EPSILON)
|
||||
{
|
||||
return self.size;
|
||||
}
|
||||
|
||||
self.size.set_size(selrect.width(), selrect.height());
|
||||
|
||||
match self.grow_type() {
|
||||
@ -915,6 +948,9 @@ impl TextContent {
|
||||
self.size.max_width = placeholder_width;
|
||||
}
|
||||
|
||||
self.layout_version = self.content_version;
|
||||
self.layout_width = Some(selrect.width());
|
||||
|
||||
self.size
|
||||
}
|
||||
|
||||
@ -1048,6 +1084,9 @@ impl Default for TextContent {
|
||||
grow_type: GrowType::Fixed,
|
||||
size: TextContentSize::default(),
|
||||
layout: TextContentLayout::new(),
|
||||
content_version: 0,
|
||||
layout_version: 0,
|
||||
layout_width: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user