mirror of
https://github.com/penpot/penpot.git
synced 2026-09-08 21:19:49 +00:00
🐛 Fix layout render-wasm issues
This commit is contained in:
parent
d3148e1a10
commit
dddb4cf0b6
@ -29,6 +29,8 @@ fn propagate_children(
|
|||||||
) -> Result<VecDeque<Modifier>> {
|
) -> Result<VecDeque<Modifier>> {
|
||||||
let mut result = VecDeque::new();
|
let mut result = VecDeque::new();
|
||||||
|
|
||||||
|
// We use the identity transform as a mark that a reflow is needed.
|
||||||
|
// It's needed to be propagated to its children.
|
||||||
if identitish(&transform) {
|
if identitish(&transform) {
|
||||||
for child_id in shape.children_ids_iter(true) {
|
for child_id in shape.children_ids_iter(true) {
|
||||||
result.push_back(Modifier::transform_propagate(*child_id, transform));
|
result.push_back(Modifier::transform_propagate(*child_id, transform));
|
||||||
@ -180,6 +182,7 @@ fn propagate_transform(
|
|||||||
modifiers: &mut HashMap<Uuid, Matrix>,
|
modifiers: &mut HashMap<Uuid, Matrix>,
|
||||||
reflown: &mut HashSet<Uuid>,
|
reflown: &mut HashSet<Uuid>,
|
||||||
reflowed_shapes: &mut HashSet<Uuid>,
|
reflowed_shapes: &mut HashSet<Uuid>,
|
||||||
|
pending_reflows: &mut HashSet<Uuid>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let Some(shape) = state.shapes.get(&entry.id) else {
|
let Some(shape) = state.shapes.get(&entry.id) else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -237,13 +240,29 @@ fn propagate_transform(
|
|||||||
transform.post_concat(&resize_transform);
|
transform.post_concat(&resize_transform);
|
||||||
}
|
}
|
||||||
GrowType::AutoWidth => {
|
GrowType::AutoWidth => {
|
||||||
|
let width_before = text_content.width();
|
||||||
|
let height_before = text_content.size.height;
|
||||||
let (new_width, new_height) = if height_changed {
|
let (new_width, new_height) = if height_changed {
|
||||||
let mut clone = text_content.clone();
|
let mut clone = text_content.clone();
|
||||||
clone.update_layout(resized_selrect);
|
clone.update_layout(resized_selrect);
|
||||||
(clone.width(), clone.size.height)
|
(clone.width(), clone.size.height)
|
||||||
} else {
|
} else {
|
||||||
(text_content.width(), text_content.size.height)
|
(width_before, height_before)
|
||||||
};
|
};
|
||||||
|
if (!is_close_to(width_before, new_width)
|
||||||
|
|| !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 {
|
||||||
|
for pid in
|
||||||
|
shapes::all_with_ancestors(&[parent_id], shapes, false).iter()
|
||||||
|
{
|
||||||
|
reflown.remove(pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
let resize_transform = math::resize_matrix(
|
let resize_transform = math::resize_matrix(
|
||||||
&shape_bounds_after,
|
&shape_bounds_after,
|
||||||
&shape_bounds_after,
|
&shape_bounds_after,
|
||||||
@ -284,7 +303,7 @@ fn propagate_transform(
|
|||||||
let is_propagate = entry.source == TransformEntrySource::Propagate;
|
let is_propagate = entry.source == TransformEntrySource::Propagate;
|
||||||
|
|
||||||
// If this is a layout and we're only moving don't need to reflow
|
// If this is a layout and we're only moving don't need to reflow
|
||||||
if shape.has_layout() && is_resize {
|
if shape.has_layout() && is_resize && pending_reflows.insert(shape.id) {
|
||||||
entries.push_back(Modifier::reflow(shape.id, false));
|
entries.push_back(Modifier::reflow(shape.id, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,13 +311,17 @@ fn propagate_transform(
|
|||||||
// When the parent is either a group or a layout we only mark for reflow
|
// When the parent is either a group or a layout we only mark for reflow
|
||||||
// if the current transformation is not a move propagation.
|
// if the current transformation is not a move propagation.
|
||||||
// If it's a move propagation we don't need to reflow, the parent is already changed.
|
// If it's a move propagation we don't need to reflow, the parent is already changed.
|
||||||
if (parent.has_layout() || parent.is_group_like()) && (is_resize || !is_propagate) {
|
if (parent.has_layout() || parent.is_group_like())
|
||||||
|
&& (is_resize || !is_propagate)
|
||||||
|
&& pending_reflows.insert(parent.id)
|
||||||
|
{
|
||||||
entries.push_back(Modifier::reflow(parent.id, false));
|
entries.push_back(Modifier::reflow(parent.id, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn propagate_reflow(
|
fn propagate_reflow(
|
||||||
id: &Uuid,
|
id: &Uuid,
|
||||||
state: &State,
|
state: &State,
|
||||||
@ -307,6 +330,7 @@ fn propagate_reflow(
|
|||||||
layout_reflows: &mut HashSet<Uuid>,
|
layout_reflows: &mut HashSet<Uuid>,
|
||||||
reflown: &mut HashSet<Uuid>,
|
reflown: &mut HashSet<Uuid>,
|
||||||
modifiers: &HashMap<Uuid, Matrix>,
|
modifiers: &HashMap<Uuid, Matrix>,
|
||||||
|
pending_reflows: &mut HashSet<Uuid>,
|
||||||
) {
|
) {
|
||||||
let Some(shape) = state.shapes.get(id) else {
|
let Some(shape) = state.shapes.get(id) else {
|
||||||
return;
|
return;
|
||||||
@ -326,7 +350,7 @@ fn propagate_reflow(
|
|||||||
}
|
}
|
||||||
Type::Group(Group { masked: true }) => {
|
Type::Group(Group { masked: true }) => {
|
||||||
let children_ids = shape.children_ids(true);
|
let children_ids = shape.children_ids(true);
|
||||||
if let Some(child) = shapes.get(&children_ids[0]) {
|
if let Some(child) = children_ids.first().and_then(|id| shapes.get(id)) {
|
||||||
let child_bounds = bounds.find(child);
|
let child_bounds = bounds.find(child);
|
||||||
bounds.insert(shape.id, child_bounds);
|
bounds.insert(shape.id, child_bounds);
|
||||||
}
|
}
|
||||||
@ -348,7 +372,7 @@ fn propagate_reflow(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(parent) = shape.parent_id.and_then(|id| shapes.get(&id)) {
|
if let Some(parent) = shape.parent_id.and_then(|id| shapes.get(&id)) {
|
||||||
if parent.has_layout() || parent.is_group_like() {
|
if (parent.has_layout() || parent.is_group_like()) && pending_reflows.insert(parent.id) {
|
||||||
entries.push_back(Modifier::reflow(parent.id, false));
|
entries.push_back(Modifier::reflow(parent.id, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -406,6 +430,14 @@ pub fn propagate_modifiers(
|
|||||||
let mut bounds = HashMap::<Uuid, Bounds>::new();
|
let mut bounds = HashMap::<Uuid, Bounds>::new();
|
||||||
let mut reflown = HashSet::<Uuid>::new();
|
let mut reflown = HashSet::<Uuid>::new();
|
||||||
let mut layout_reflows = HashSet::<Uuid>::new();
|
let mut layout_reflows = HashSet::<Uuid>::new();
|
||||||
|
// Tracks text shapes that have already triggered a reflow across all outer
|
||||||
|
// iterations, preventing oscillation when a parent layout re-emits a
|
||||||
|
// transform for the same text shape in a later pass.
|
||||||
|
let mut reflowed_shapes = HashSet::<Uuid>::new();
|
||||||
|
// Tracks reflow ids already queued to avoid flooding entries with
|
||||||
|
// duplicate Reflow entries when many children of the same parent
|
||||||
|
// are transformed in the same pass.
|
||||||
|
let mut pending_reflows = HashSet::<Uuid>::new();
|
||||||
|
|
||||||
// We first propagate the transforms to the children and then after
|
// We first propagate the transforms to the children and then after
|
||||||
// recalculate the layouts. The layout can create further transforms that
|
// recalculate the layouts. The layout can create further transforms that
|
||||||
@ -413,7 +445,6 @@ pub fn propagate_modifiers(
|
|||||||
// In order for loop to eventualy finish, we limit the flex reflow to just
|
// In order for loop to eventualy finish, we limit the flex reflow to just
|
||||||
// one (the reflown set).
|
// one (the reflown set).
|
||||||
while !entries.is_empty() {
|
while !entries.is_empty() {
|
||||||
let mut reflowed_shapes = HashSet::<Uuid>::new();
|
|
||||||
while let Some(modifier) = entries.pop_front() {
|
while let Some(modifier) = entries.pop_front() {
|
||||||
match modifier {
|
match modifier {
|
||||||
Modifier::Transform(entry, pixel) => propagate_transform(
|
Modifier::Transform(entry, pixel) => propagate_transform(
|
||||||
@ -425,8 +456,10 @@ pub fn propagate_modifiers(
|
|||||||
&mut modifiers,
|
&mut modifiers,
|
||||||
&mut reflown,
|
&mut reflown,
|
||||||
&mut reflowed_shapes,
|
&mut reflowed_shapes,
|
||||||
|
&mut pending_reflows,
|
||||||
)?,
|
)?,
|
||||||
Modifier::Reflow(id, force_reflow) => {
|
Modifier::Reflow(id, force_reflow) => {
|
||||||
|
pending_reflows.remove(&id);
|
||||||
if force_reflow {
|
if force_reflow {
|
||||||
reflown.remove(&id);
|
reflown.remove(&id);
|
||||||
}
|
}
|
||||||
@ -439,6 +472,7 @@ pub fn propagate_modifiers(
|
|||||||
&mut layout_reflows,
|
&mut layout_reflows,
|
||||||
&mut reflown,
|
&mut reflown,
|
||||||
&modifiers,
|
&modifiers,
|
||||||
|
&mut pending_reflows,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -465,7 +499,6 @@ pub fn propagate_modifiers(
|
|||||||
}
|
}
|
||||||
reflow_shape(id, state, &mut reflown, &mut entries, &mut bounds_temp)?;
|
reflow_shape(id, state, &mut reflown, &mut entries, &mut bounds_temp)?;
|
||||||
}
|
}
|
||||||
layout_reflows = HashSet::new();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[allow(dead_code)]
|
// #[allow(dead_code)]
|
||||||
|
|||||||
@ -54,6 +54,7 @@ struct LayoutAxis {
|
|||||||
gap_across: f32,
|
gap_across: f32,
|
||||||
is_auto_main: bool,
|
is_auto_main: bool,
|
||||||
is_auto_across: bool,
|
is_auto_across: bool,
|
||||||
|
is_wrap: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutAxis {
|
impl LayoutAxis {
|
||||||
@ -78,6 +79,7 @@ impl LayoutAxis {
|
|||||||
gap_across: layout_data.row_gap,
|
gap_across: layout_data.row_gap,
|
||||||
is_auto_main: num_child > 0 && shape.is_layout_horizontal_auto(),
|
is_auto_main: num_child > 0 && shape.is_layout_horizontal_auto(),
|
||||||
is_auto_across: num_child > 0 && shape.is_layout_vertical_auto(),
|
is_auto_across: num_child > 0 && shape.is_layout_vertical_auto(),
|
||||||
|
is_wrap: flex_data.is_wrap(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Self {
|
Self {
|
||||||
@ -93,6 +95,7 @@ impl LayoutAxis {
|
|||||||
gap_across: layout_data.column_gap,
|
gap_across: layout_data.column_gap,
|
||||||
is_auto_main: num_child > 0 && shape.is_layout_vertical_auto(),
|
is_auto_main: num_child > 0 && shape.is_layout_vertical_auto(),
|
||||||
is_auto_across: num_child > 0 && shape.is_layout_horizontal_auto(),
|
is_auto_across: num_child > 0 && shape.is_layout_horizontal_auto(),
|
||||||
|
is_wrap: flex_data.is_wrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -399,7 +402,7 @@ fn calculate_track_positions(
|
|||||||
) {
|
) {
|
||||||
let mut align_content = &layout_data.align_content;
|
let mut align_content = &layout_data.align_content;
|
||||||
|
|
||||||
if layout_axis.is_auto_across {
|
if layout_axis.is_auto_across || !layout_axis.is_wrap {
|
||||||
align_content = &AlignContent::Start;
|
align_content = &AlignContent::Start;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -427,7 +430,10 @@ fn calculate_track_positions(
|
|||||||
|
|
||||||
AlignContent::SpaceAround => {
|
AlignContent::SpaceAround => {
|
||||||
let effective_gap = (layout_axis.across_space() - total_across_size) / tlen as f32;
|
let effective_gap = (layout_axis.across_space() - total_across_size) / tlen as f32;
|
||||||
(effective_gap / 2.0, effective_gap)
|
(
|
||||||
|
layout_axis.padding_across_start + effective_gap / 2.0,
|
||||||
|
effective_gap,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
AlignContent::SpaceEvenly => {
|
AlignContent::SpaceEvenly => {
|
||||||
@ -473,7 +479,9 @@ fn calculate_track_data(
|
|||||||
|
|
||||||
let total_across_size = tracks.iter().map(|t| t.across_size).sum::<f32>();
|
let total_across_size = tracks.iter().map(|t| t.across_size).sum::<f32>();
|
||||||
|
|
||||||
if !layout_axis.is_auto_across && layout_data.align_content == AlignContent::Stretch {
|
let stretch_tracks = !layout_axis.is_wrap || layout_data.align_content == AlignContent::Stretch;
|
||||||
|
|
||||||
|
if !layout_axis.is_auto_across && stretch_tracks {
|
||||||
stretch_tracks_sizes(&layout_axis, &mut tracks, total_across_size);
|
stretch_tracks_sizes(&layout_axis, &mut tracks, total_across_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,12 +514,12 @@ fn first_anchor(
|
|||||||
}
|
}
|
||||||
JustifyContent::SpaceAround => {
|
JustifyContent::SpaceAround => {
|
||||||
let effective_gap = (layout_axis.main_space() - total_shapes_size) / slen as f32;
|
let effective_gap = (layout_axis.main_space() - total_shapes_size) / slen as f32;
|
||||||
layout_axis.padding_main_end + f32::max(layout_axis.gap_main, effective_gap / 2.0)
|
layout_axis.padding_main_start + f32::max(layout_axis.gap_main, effective_gap / 2.0)
|
||||||
}
|
}
|
||||||
JustifyContent::SpaceEvenly => {
|
JustifyContent::SpaceEvenly => {
|
||||||
let effective_gap =
|
let effective_gap =
|
||||||
(layout_axis.main_space() - total_shapes_size) / (track.shapes.len() + 1) as f32;
|
(layout_axis.main_space() - total_shapes_size) / (track.shapes.len() + 1) as f32;
|
||||||
layout_axis.padding_main_end + f32::max(layout_axis.gap_main, effective_gap)
|
layout_axis.padding_main_start + f32::max(layout_axis.gap_main, effective_gap)
|
||||||
}
|
}
|
||||||
_ => layout_axis.padding_main_start,
|
_ => layout_axis.padding_main_start,
|
||||||
};
|
};
|
||||||
@ -538,8 +546,11 @@ fn next_anchor(
|
|||||||
+ child_axis.margin_main_end
|
+ child_axis.margin_main_end
|
||||||
+ match layout_data.justify_content {
|
+ match layout_data.justify_content {
|
||||||
JustifyContent::SpaceBetween => {
|
JustifyContent::SpaceBetween => {
|
||||||
let effective_gap = (layout_axis.main_space() - total_shapes_size)
|
let effective_gap = if track.shapes.len() > 1 {
|
||||||
/ (track.shapes.len() - 1) as f32;
|
(layout_axis.main_space() - total_shapes_size) / (track.shapes.len() - 1) as f32
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
child_axis.main_size + f32::max(layout_axis.gap_main, effective_gap)
|
child_axis.main_size + f32::max(layout_axis.gap_main, effective_gap)
|
||||||
}
|
}
|
||||||
JustifyContent::SpaceAround => {
|
JustifyContent::SpaceAround => {
|
||||||
|
|||||||
@ -193,7 +193,7 @@ fn set_auto_multi_span(
|
|||||||
// Sort descendant order of prop-span
|
// Sort descendant order of prop-span
|
||||||
selected_cells.sort_by(|a, b| {
|
selected_cells.sort_by(|a, b| {
|
||||||
if column {
|
if column {
|
||||||
b.column_span.cmp(&a.row_span)
|
b.column_span.cmp(&a.column_span)
|
||||||
} else {
|
} else {
|
||||||
b.row_span.cmp(&a.row_span)
|
b.row_span.cmp(&a.row_span)
|
||||||
}
|
}
|
||||||
@ -268,7 +268,7 @@ fn set_flex_multi_span(
|
|||||||
// Sort descendant order of prop-span
|
// Sort descendant order of prop-span
|
||||||
selected_cells.sort_by(|a, b| {
|
selected_cells.sort_by(|a, b| {
|
||||||
if column {
|
if column {
|
||||||
b.column_span.cmp(&a.row_span)
|
b.column_span.cmp(&a.column_span)
|
||||||
} else {
|
} else {
|
||||||
b.row_span.cmp(&a.row_span)
|
b.row_span.cmp(&a.row_span)
|
||||||
}
|
}
|
||||||
@ -901,7 +901,7 @@ pub fn reflow_grid_layout(
|
|||||||
let auto_width = column_tracks.iter().map(|t| t.size).sum::<f32>()
|
let auto_width = column_tracks.iter().map(|t| t.size).sum::<f32>()
|
||||||
+ layout_data.padding_left
|
+ layout_data.padding_left
|
||||||
+ layout_data.padding_right
|
+ layout_data.padding_right
|
||||||
+ (column_tracks.len() - 1) as f32 * layout_data.column_gap;
|
+ column_tracks.len().saturating_sub(1) as f32 * layout_data.column_gap;
|
||||||
scale_width = auto_width / width;
|
scale_width = auto_width / width;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -909,7 +909,7 @@ pub fn reflow_grid_layout(
|
|||||||
let auto_height = row_tracks.iter().map(|t| t.size).sum::<f32>()
|
let auto_height = row_tracks.iter().map(|t| t.size).sum::<f32>()
|
||||||
+ layout_data.padding_top
|
+ layout_data.padding_top
|
||||||
+ layout_data.padding_bottom
|
+ layout_data.padding_bottom
|
||||||
+ (row_tracks.len() - 1) as f32 * layout_data.row_gap;
|
+ row_tracks.len().saturating_sub(1) as f32 * layout_data.row_gap;
|
||||||
scale_height = auto_height / height;
|
scale_height = auto_height / height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user