mirror of
https://github.com/penpot/penpot.git
synced 2026-06-09 08:52:05 +00:00
Merge pull request #9941 from penpot/alotor-layout-fixes
🐛 Fix layout render-wasm issues
This commit is contained in:
commit
2410bcb0df
@ -29,6 +29,8 @@ fn propagate_children(
|
||||
) -> Result<VecDeque<Modifier>> {
|
||||
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) {
|
||||
for child_id in shape.children_ids_iter(true) {
|
||||
result.push_back(Modifier::transform_propagate(*child_id, transform));
|
||||
@ -180,6 +182,7 @@ fn propagate_transform(
|
||||
modifiers: &mut HashMap<Uuid, Matrix>,
|
||||
reflown: &mut HashSet<Uuid>,
|
||||
reflowed_shapes: &mut HashSet<Uuid>,
|
||||
pending_reflows: &mut HashSet<Uuid>,
|
||||
) -> Result<()> {
|
||||
let Some(shape) = state.shapes.get(&entry.id) else {
|
||||
return Ok(());
|
||||
@ -237,13 +240,29 @@ fn propagate_transform(
|
||||
transform.post_concat(&resize_transform);
|
||||
}
|
||||
GrowType::AutoWidth => {
|
||||
let width_before = text_content.width();
|
||||
let height_before = 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)
|
||||
(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(
|
||||
&shape_bounds_after,
|
||||
&shape_bounds_after,
|
||||
@ -284,7 +303,7 @@ fn propagate_transform(
|
||||
let is_propagate = entry.source == TransformEntrySource::Propagate;
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
@ -292,13 +311,17 @@ fn propagate_transform(
|
||||
// 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 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));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn propagate_reflow(
|
||||
id: &Uuid,
|
||||
state: &State,
|
||||
@ -307,6 +330,7 @@ fn propagate_reflow(
|
||||
layout_reflows: &mut HashSet<Uuid>,
|
||||
reflown: &mut HashSet<Uuid>,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
pending_reflows: &mut HashSet<Uuid>,
|
||||
) {
|
||||
let Some(shape) = state.shapes.get(id) else {
|
||||
return;
|
||||
@ -326,7 +350,7 @@ fn propagate_reflow(
|
||||
}
|
||||
Type::Group(Group { masked: 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);
|
||||
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 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));
|
||||
}
|
||||
}
|
||||
@ -406,6 +430,14 @@ pub fn propagate_modifiers(
|
||||
let mut bounds = HashMap::<Uuid, Bounds>::new();
|
||||
let mut reflown = 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
|
||||
// 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
|
||||
// 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(
|
||||
@ -425,8 +456,10 @@ pub fn propagate_modifiers(
|
||||
&mut modifiers,
|
||||
&mut reflown,
|
||||
&mut reflowed_shapes,
|
||||
&mut pending_reflows,
|
||||
)?,
|
||||
Modifier::Reflow(id, force_reflow) => {
|
||||
pending_reflows.remove(&id);
|
||||
if force_reflow {
|
||||
reflown.remove(&id);
|
||||
}
|
||||
@ -439,6 +472,7 @@ pub fn propagate_modifiers(
|
||||
&mut layout_reflows,
|
||||
&mut reflown,
|
||||
&modifiers,
|
||||
&mut pending_reflows,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -465,7 +499,6 @@ pub fn propagate_modifiers(
|
||||
}
|
||||
reflow_shape(id, state, &mut reflown, &mut entries, &mut bounds_temp)?;
|
||||
}
|
||||
layout_reflows = HashSet::new();
|
||||
}
|
||||
|
||||
// #[allow(dead_code)]
|
||||
|
||||
@ -54,6 +54,7 @@ struct LayoutAxis {
|
||||
gap_across: f32,
|
||||
is_auto_main: bool,
|
||||
is_auto_across: bool,
|
||||
is_wrap: bool,
|
||||
}
|
||||
|
||||
impl LayoutAxis {
|
||||
@ -78,6 +79,7 @@ impl LayoutAxis {
|
||||
gap_across: layout_data.row_gap,
|
||||
is_auto_main: num_child > 0 && shape.is_layout_horizontal_auto(),
|
||||
is_auto_across: num_child > 0 && shape.is_layout_vertical_auto(),
|
||||
is_wrap: flex_data.is_wrap(),
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
@ -93,6 +95,7 @@ impl LayoutAxis {
|
||||
gap_across: layout_data.column_gap,
|
||||
is_auto_main: num_child > 0 && shape.is_layout_vertical_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;
|
||||
|
||||
if layout_axis.is_auto_across {
|
||||
if layout_axis.is_auto_across || !layout_axis.is_wrap {
|
||||
align_content = &AlignContent::Start;
|
||||
}
|
||||
|
||||
@ -427,7 +430,10 @@ fn calculate_track_positions(
|
||||
|
||||
AlignContent::SpaceAround => {
|
||||
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 => {
|
||||
@ -473,7 +479,9 @@ fn calculate_track_data(
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -506,12 +514,12 @@ fn first_anchor(
|
||||
}
|
||||
JustifyContent::SpaceAround => {
|
||||
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 => {
|
||||
let effective_gap =
|
||||
(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,
|
||||
};
|
||||
@ -538,8 +546,11 @@ fn next_anchor(
|
||||
+ child_axis.margin_main_end
|
||||
+ match layout_data.justify_content {
|
||||
JustifyContent::SpaceBetween => {
|
||||
let effective_gap = (layout_axis.main_space() - total_shapes_size)
|
||||
/ (track.shapes.len() - 1) as f32;
|
||||
let effective_gap = if track.shapes.len() > 1 {
|
||||
(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)
|
||||
}
|
||||
JustifyContent::SpaceAround => {
|
||||
|
||||
@ -193,7 +193,7 @@ fn set_auto_multi_span(
|
||||
// Sort descendant order of prop-span
|
||||
selected_cells.sort_by(|a, b| {
|
||||
if column {
|
||||
b.column_span.cmp(&a.row_span)
|
||||
b.column_span.cmp(&a.column_span)
|
||||
} else {
|
||||
b.row_span.cmp(&a.row_span)
|
||||
}
|
||||
@ -268,7 +268,7 @@ fn set_flex_multi_span(
|
||||
// Sort descendant order of prop-span
|
||||
selected_cells.sort_by(|a, b| {
|
||||
if column {
|
||||
b.column_span.cmp(&a.row_span)
|
||||
b.column_span.cmp(&a.column_span)
|
||||
} else {
|
||||
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>()
|
||||
+ layout_data.padding_left
|
||||
+ 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;
|
||||
}
|
||||
|
||||
@ -909,7 +909,7 @@ pub fn reflow_grid_layout(
|
||||
let auto_height = row_tracks.iter().map(|t| t.size).sum::<f32>()
|
||||
+ layout_data.padding_top
|
||||
+ 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;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user