1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
use freya_common::Compositor;
use freya_engine::prelude::{
    Canvas,
    ClipOp,
    Color,
    Rect,
    SamplingOptions,
    Surface,
};
use freya_native_core::{
    real_dom::NodeImmutable,
    NodeId,
};
use freya_node_state::ViewportState;
use itertools::sorted;
use torin::prelude::{
    LayoutNode,
    Torin,
};

use crate::dom::FreyaDOM;

pub fn process_render(
    fdom: &FreyaDOM,
    canvas: &Canvas,
    dirty_surface: &mut Surface,
    compositor: &Compositor,
    mut render_fn: impl FnMut(&FreyaDOM, &NodeId, &LayoutNode, &Torin<NodeId>, &Canvas),
) {
    let dirty_canvas = dirty_surface.canvas();
    let layout = fdom.layout();
    let rdom = fdom.rdom();
    let layers = fdom.layers();
    let compositor_dirty_nodes = fdom.compositor_dirty_nodes();

    let (dirty_layers, dirty_area) = compositor.run(
        compositor_dirty_nodes,
        |node, try_traverse_children| {
            let node = rdom.get(node);
            if let Some(node) = node {
                let traverse_children = node
                    .node_type()
                    .tag()
                    .map(|tag| !tag.contains_text())
                    .unwrap_or_default();
                let mut affected = if traverse_children && try_traverse_children {
                    node.child_ids()
                } else {
                    Vec::new()
                };

                if !node.node_type().is_visible_element() {
                    if let Some(parent_id) = node.parent_id() {
                        affected.push(parent_id);
                    }
                }
                affected
            } else {
                Vec::new()
            }
        },
        |node| layout.get(node).map(|node| node.area),
        layers,
    );

    let dirty_layers = dirty_layers.layers();

    dirty_canvas.save();
    if let Some(dirty_area) = dirty_area {
        dirty_canvas.clip_rect(
            Rect::new(
                dirty_area.min_x(),
                dirty_area.min_y(),
                dirty_area.max_x(),
                dirty_area.max_y(),
            ),
            Some(ClipOp::Intersect),
            false,
        );
        dirty_canvas.clear(Color::WHITE);
    }

    let mut painted = Vec::new();

    // Render the dirty nodes
    for (_, nodes) in sorted(dirty_layers.iter()) {
        'elements: for node_id in nodes {
            let node = rdom.get(*node_id).unwrap();
            let node_viewports = node.get::<ViewportState>().unwrap();

            let layout_node = layout.get(*node_id);

            if let Some(layout_node) = layout_node {
                // Skip elements that are completely out of any their parent's viewport
                for viewport_id in &node_viewports.viewports {
                    let viewport = layout.get(*viewport_id).unwrap().visible_area();
                    if !viewport.intersects(&layout_node.area) {
                        continue 'elements;
                    }
                }
                // Render the element
                render_fn(fdom, node_id, layout_node, &layout, dirty_canvas);
                painted.push(node_id);
            }
        }
    }

    dirty_canvas.restore();
    dirty_surface.draw(canvas, (0, 0), SamplingOptions::default(), None);
}