mirror of
https://github.com/penpot/penpot.git
synced 2026-02-12 14:42:56 +00:00
Merge remote-tracking branch 'origin/staging-render' into develop
This commit is contained in:
@@ -346,6 +346,14 @@ pub extern "C" fn use_shape(a: u32, b: u32, c: u32, d: u32) {
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn touch_shape(a: u32, b: u32, c: u32, d: u32) {
|
||||
with_state_mut!(state, {
|
||||
let shape_id = uuid_from_u32_quartet(a, b, c, d);
|
||||
state.touch_shape(shape_id);
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn set_parent(a: u32, b: u32, c: u32, d: u32) {
|
||||
with_state_mut!(state, {
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
use skia_safe::{self as skia};
|
||||
|
||||
use crate::math::Rect;
|
||||
use crate::shapes::modifiers::grid_layout::grid_cell_data;
|
||||
use crate::shapes::Shape;
|
||||
use crate::state::ShapesPoolRef;
|
||||
|
||||
pub fn render_overlay(zoom: f32, canvas: &skia::Canvas, shape: &Shape, shapes: ShapesPoolRef) {
|
||||
let cells = grid_cell_data(shape, shapes, true);
|
||||
let cells: Vec<crate::shapes::grid_layout::CellData<'_>> = grid_cell_data(shape, shapes, true);
|
||||
let bounds = shape.bounds();
|
||||
|
||||
let mut paint = skia::Paint::default();
|
||||
paint.set_style(skia::PaintStyle::Stroke);
|
||||
paint.set_color(skia::Color::from_rgb(255, 111, 224));
|
||||
paint.set_anti_alias(shape.should_use_antialias(zoom));
|
||||
|
||||
paint.set_stroke_width(1.0 / zoom);
|
||||
|
||||
for cell in cells.iter() {
|
||||
let rect = Rect::from_xywh(cell.anchor.x, cell.anchor.y, cell.width, cell.height);
|
||||
canvas.draw_rect(rect, &paint);
|
||||
let hv = bounds.hv(cell.width);
|
||||
let vv = bounds.vv(cell.height);
|
||||
let points = [
|
||||
cell.anchor,
|
||||
cell.anchor + hv,
|
||||
cell.anchor + hv + vv,
|
||||
cell.anchor + vv,
|
||||
];
|
||||
let polygon = skia::Path::polygon(&points, true, None, None);
|
||||
canvas.draw_path(&polygon, &paint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use skia_safe::{self as skia, Color4f};
|
||||
|
||||
use super::{RenderState, ShapesPoolRef, SurfaceId};
|
||||
use crate::render::grid_layout;
|
||||
use crate::shapes::{Layout, Type};
|
||||
|
||||
pub fn render(render_state: &mut RenderState, shapes: ShapesPoolRef) {
|
||||
let canvas = render_state.surfaces.canvas(SurfaceId::UI);
|
||||
@@ -18,12 +19,37 @@ pub fn render(render_state: &mut RenderState, shapes: ShapesPoolRef) {
|
||||
|
||||
let canvas = render_state.surfaces.canvas(SurfaceId::UI);
|
||||
|
||||
if let Some(id) = render_state.show_grid {
|
||||
let show_grid_id = render_state.show_grid;
|
||||
|
||||
if let Some(id) = show_grid_id {
|
||||
if let Some(shape) = shapes.get(&id) {
|
||||
grid_layout::render_overlay(zoom, canvas, shape, shapes);
|
||||
}
|
||||
}
|
||||
|
||||
// Render overlays for empty grid frames
|
||||
for shape in shapes.iter() {
|
||||
if shape.id.is_nil() || !shape.children.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if show_grid_id == Some(shape.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Type::Frame(frame) = &shape.shape_type else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if !matches!(frame.layout, Some(Layout::GridLayout(_, _))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(shape) = shapes.get(&shape.id) {
|
||||
grid_layout::render_overlay(zoom, canvas, shape, shapes);
|
||||
}
|
||||
}
|
||||
|
||||
canvas.restore();
|
||||
render_state.surfaces.draw_into(
|
||||
SurfaceId::UI,
|
||||
|
||||
@@ -1074,6 +1074,10 @@ impl Shape {
|
||||
self.children.first()
|
||||
}
|
||||
|
||||
pub fn children_count(&self) -> usize {
|
||||
self.children_ids_iter(false).count()
|
||||
}
|
||||
|
||||
pub fn children_ids(&self, include_hidden: bool) -> Vec<Uuid> {
|
||||
if include_hidden {
|
||||
return self.children.iter().rev().copied().collect();
|
||||
|
||||
@@ -264,7 +264,7 @@ fn propagate_transform(
|
||||
|
||||
// If this is a layout and we're only moving don't need to reflow
|
||||
if shape.has_layout() && is_resize {
|
||||
entries.push_back(Modifier::reflow(shape.id));
|
||||
entries.push_back(Modifier::reflow(shape.id, false));
|
||||
}
|
||||
|
||||
if let Some(parent) = shape.parent_id.and_then(|id| shapes.get(&id)) {
|
||||
@@ -272,7 +272,7 @@ fn propagate_transform(
|
||||
// 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) {
|
||||
entries.push_back(Modifier::reflow(parent.id));
|
||||
entries.push_back(Modifier::reflow(parent.id, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -282,7 +282,7 @@ fn propagate_reflow(
|
||||
state: &State,
|
||||
entries: &mut VecDeque<Modifier>,
|
||||
bounds: &mut HashMap<Uuid, Bounds>,
|
||||
layout_reflows: &mut Vec<Uuid>,
|
||||
layout_reflows: &mut HashSet<Uuid>,
|
||||
reflown: &mut HashSet<Uuid>,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
) {
|
||||
@@ -300,20 +300,7 @@ fn propagate_reflow(
|
||||
Type::Frame(Frame {
|
||||
layout: Some(_), ..
|
||||
}) => {
|
||||
let mut skip_reflow = false;
|
||||
if shape.is_layout_horizontal_fill() || shape.is_layout_vertical_fill() {
|
||||
if let Some(parent_id) = shape.parent_id {
|
||||
if parent_id != Uuid::nil() && !reflown.contains(&parent_id) {
|
||||
// If this is a fill layout but the parent has not been reflown yet
|
||||
// we wait for the next iteration for reflow
|
||||
skip_reflow = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !skip_reflow {
|
||||
layout_reflows.push(*id);
|
||||
}
|
||||
layout_reflows.insert(*id);
|
||||
}
|
||||
Type::Group(Group { masked: true }) => {
|
||||
let children_ids = shape.children_ids(true);
|
||||
@@ -340,7 +327,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() {
|
||||
entries.push_back(Modifier::reflow(parent.id));
|
||||
entries.push_back(Modifier::reflow(parent.id, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -382,19 +369,20 @@ pub fn propagate_modifiers(
|
||||
let mut entries: VecDeque<_> = modifiers
|
||||
.iter()
|
||||
.map(|entry| {
|
||||
// If we receibe a identity matrix we force a reflow
|
||||
// If we receive a identity matrix we force a reflow
|
||||
if math::identitish(&entry.transform) {
|
||||
Modifier::Reflow(entry.id)
|
||||
Modifier::Reflow(entry.id, false)
|
||||
} else {
|
||||
Modifier::Transform(*entry)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let shapes = &state.shapes;
|
||||
let mut modifiers = HashMap::<Uuid, Matrix>::new();
|
||||
let mut bounds = HashMap::<Uuid, Bounds>::new();
|
||||
let mut reflown = HashSet::<Uuid>::new();
|
||||
let mut layout_reflows = Vec::<Uuid>::new();
|
||||
let mut layout_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
|
||||
@@ -412,25 +400,43 @@ pub fn propagate_modifiers(
|
||||
&mut bounds,
|
||||
&mut modifiers,
|
||||
),
|
||||
Modifier::Reflow(id) => propagate_reflow(
|
||||
&id,
|
||||
state,
|
||||
&mut entries,
|
||||
&mut bounds,
|
||||
&mut layout_reflows,
|
||||
&mut reflown,
|
||||
&modifiers,
|
||||
),
|
||||
Modifier::Reflow(id, force_reflow) => {
|
||||
if force_reflow {
|
||||
reflown.remove(&id);
|
||||
}
|
||||
|
||||
propagate_reflow(
|
||||
&id,
|
||||
state,
|
||||
&mut entries,
|
||||
&mut bounds,
|
||||
&mut layout_reflows,
|
||||
&mut reflown,
|
||||
&modifiers,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for id in layout_reflows.iter() {
|
||||
let mut layout_reflows_vec: Vec<Uuid> = layout_reflows.into_iter().collect();
|
||||
|
||||
// We sort the reflows so they are process first the ones that are more
|
||||
// deep in the tree structure. This way we can be sure that the children layouts
|
||||
// are already reflowed.
|
||||
layout_reflows_vec.sort_unstable_by(|id_a, id_b| {
|
||||
let da = shapes.get_depth(id_a);
|
||||
let db = shapes.get_depth(id_b);
|
||||
db.cmp(&da)
|
||||
});
|
||||
|
||||
let mut bounds_temp = bounds.clone();
|
||||
for id in layout_reflows_vec.iter() {
|
||||
if reflown.contains(id) {
|
||||
continue;
|
||||
}
|
||||
reflow_shape(id, state, &mut reflown, &mut entries, &mut bounds);
|
||||
reflow_shape(id, state, &mut reflown, &mut entries, &mut bounds_temp);
|
||||
}
|
||||
layout_reflows = Vec::new();
|
||||
layout_reflows = HashSet::new();
|
||||
}
|
||||
|
||||
modifiers
|
||||
|
||||
@@ -61,6 +61,7 @@ impl LayoutAxis {
|
||||
layout_data: &LayoutData,
|
||||
flex_data: &FlexData,
|
||||
) -> Self {
|
||||
let num_child = shape.children_count();
|
||||
if flex_data.is_row() {
|
||||
Self {
|
||||
main_size: layout_bounds.width(),
|
||||
@@ -73,8 +74,8 @@ impl LayoutAxis {
|
||||
padding_across_end: layout_data.padding_bottom,
|
||||
gap_main: layout_data.column_gap,
|
||||
gap_across: layout_data.row_gap,
|
||||
is_auto_main: shape.is_layout_horizontal_auto(),
|
||||
is_auto_across: shape.is_layout_vertical_auto(),
|
||||
is_auto_main: num_child > 0 && shape.is_layout_horizontal_auto(),
|
||||
is_auto_across: num_child > 0 && shape.is_layout_vertical_auto(),
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
@@ -88,8 +89,8 @@ impl LayoutAxis {
|
||||
padding_across_end: layout_data.padding_right,
|
||||
gap_main: layout_data.row_gap,
|
||||
gap_across: layout_data.column_gap,
|
||||
is_auto_main: shape.is_layout_vertical_auto(),
|
||||
is_auto_across: shape.is_layout_horizontal_auto(),
|
||||
is_auto_main: num_child > 0 && shape.is_layout_vertical_auto(),
|
||||
is_auto_across: num_child > 0 && shape.is_layout_horizontal_auto(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,7 +346,10 @@ fn distribute_fill_across_space(layout_axis: &LayoutAxis, tracks: &mut [TrackDat
|
||||
let mut size =
|
||||
track.across_size - child.margin_across_start - child.margin_across_end;
|
||||
size = size.clamp(child.min_across_size, child.max_across_size);
|
||||
size = f32::min(size, layout_axis.across_space());
|
||||
|
||||
if !layout_axis.is_auto_across {
|
||||
size = f32::min(size, layout_axis.across_space());
|
||||
}
|
||||
child.across_size = size;
|
||||
}
|
||||
}
|
||||
@@ -620,9 +624,12 @@ pub fn reflow_flex_layout(
|
||||
|
||||
let mut transform = Matrix::default();
|
||||
|
||||
let mut force_reflow = false;
|
||||
if (new_width - child_bounds.width()).abs() > MIN_SIZE
|
||||
|| (new_height - child_bounds.height()).abs() > MIN_SIZE
|
||||
{
|
||||
// When the child is fill we need to force a reflow
|
||||
force_reflow = true;
|
||||
transform.post_concat(&math::resize_matrix(
|
||||
layout_bounds,
|
||||
child_bounds,
|
||||
@@ -637,7 +644,7 @@ pub fn reflow_flex_layout(
|
||||
|
||||
result.push_back(Modifier::transform_propagate(child.id, transform));
|
||||
if child.has_layout() {
|
||||
result.push_back(Modifier::reflow(child.id));
|
||||
result.push_back(Modifier::reflow(child.id, force_reflow));
|
||||
}
|
||||
|
||||
shape_anchor = next_anchor(
|
||||
|
||||
@@ -765,9 +765,12 @@ pub fn reflow_grid_layout(
|
||||
|
||||
let mut transform = Matrix::default();
|
||||
|
||||
let mut force_reflow = false;
|
||||
if (new_width - child_bounds.width()).abs() > MIN_SIZE
|
||||
|| (new_height - child_bounds.height()).abs() > MIN_SIZE
|
||||
{
|
||||
// When the child is a fill it needs to be reflown
|
||||
force_reflow = true;
|
||||
transform.post_concat(&math::resize_matrix(
|
||||
&layout_bounds,
|
||||
&child_bounds,
|
||||
@@ -793,7 +796,7 @@ pub fn reflow_grid_layout(
|
||||
|
||||
result.push_back(Modifier::transform_propagate(child.id, transform));
|
||||
if child.has_layout() {
|
||||
result.push_back(Modifier::reflow(child.id));
|
||||
result.push_back(Modifier::reflow(child.id, force_reflow));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use skia::Matrix;
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Modifier {
|
||||
Transform(TransformEntry),
|
||||
Reflow(Uuid),
|
||||
Reflow(Uuid, bool),
|
||||
}
|
||||
|
||||
impl Modifier {
|
||||
@@ -18,8 +18,8 @@ impl Modifier {
|
||||
pub fn parent(id: Uuid, transform: Matrix) -> Self {
|
||||
Modifier::Transform(TransformEntry::parent(id, transform))
|
||||
}
|
||||
pub fn reflow(id: Uuid) -> Self {
|
||||
Modifier::Reflow(id)
|
||||
pub fn reflow(id: Uuid, force_reflow: bool) -> Self {
|
||||
Modifier::Reflow(id, force_reflow)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -177,6 +177,26 @@ impl ShapesPoolImpl {
|
||||
}
|
||||
}
|
||||
|
||||
// Given an id, returns the depth in the tree-shaped structure
|
||||
// of shapes.
|
||||
pub fn get_depth(&self, id: &Uuid) -> usize {
|
||||
if id == &Uuid::nil() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let Some(idx) = self.uuid_to_idx.get(id) else {
|
||||
return 0;
|
||||
};
|
||||
|
||||
let shape = &self.shapes[*idx];
|
||||
|
||||
let Some(parent_id) = shape.parent_id else {
|
||||
return 0;
|
||||
};
|
||||
|
||||
self.get_depth(&parent_id) + 1
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn iter(&self) -> std::slice::Iter<'_, Shape> {
|
||||
self.shapes.iter()
|
||||
|
||||
@@ -31,21 +31,17 @@ impl From<RawFontStyle> for FontStyle {
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn store_font(
|
||||
a1: u32,
|
||||
b1: u32,
|
||||
c1: u32,
|
||||
d1: u32,
|
||||
a2: u32,
|
||||
b2: u32,
|
||||
c2: u32,
|
||||
d2: u32,
|
||||
a: u32,
|
||||
b: u32,
|
||||
c: u32,
|
||||
d: u32,
|
||||
weight: u32,
|
||||
style: u8,
|
||||
is_emoji: bool,
|
||||
is_fallback: bool,
|
||||
) {
|
||||
with_state_mut!(state, {
|
||||
let id = uuid_from_u32_quartet(a2, b2, c2, d2);
|
||||
let id = uuid_from_u32_quartet(a, b, c, d);
|
||||
let font_bytes = mem::bytes();
|
||||
let font_style = RawFontStyle::from(style);
|
||||
|
||||
@@ -57,9 +53,6 @@ pub extern "C" fn store_font(
|
||||
.add(family, &font_bytes, is_emoji, is_fallback);
|
||||
|
||||
mem::free_bytes();
|
||||
|
||||
let shape_id = uuid_from_u32_quartet(a1, b1, c1, d1);
|
||||
state.touch_shape(shape_id);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -384,6 +384,7 @@ pub extern "C" fn update_shape_text_layout_for(a: u32, b: u32, c: u32, d: u32) {
|
||||
if let Some(shape) = state.shapes.get_mut(&shape_id) {
|
||||
update_text_layout(shape);
|
||||
}
|
||||
state.touch_shape(shape_id);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user