Merge remote-tracking branch 'origin/staging-render' into develop

This commit is contained in:
Alejandro Alonso
2026-02-06 11:23:50 +01:00
26 changed files with 586 additions and 167 deletions

View File

@@ -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, {

View File

@@ -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);
}
}

View File

@@ -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,

View File

@@ -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();

View File

@@ -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

View File

@@ -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(

View File

@@ -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));
}
}

View File

@@ -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)
}
}

View File

@@ -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()

View File

@@ -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);
});
}

View File

@@ -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);
});
}