freya_devtools_app/tabs/
tree.rs

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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use std::collections::HashSet;

use dioxus_radio::prelude::use_radio;
use freya::prelude::*;
use freya_native_core::NodeId;
use freya_router::prelude::{
    router,
    use_navigator,
};

use crate::{
    Route,
    node::NodeElement,
    state::DevtoolsChannel,
};

#[derive(Clone, PartialEq)]
struct NodeTreeItem {
    is_open: Option<bool>,
    window_id: u64,
    node_id: NodeId,
}

#[component]
pub fn NodesTree(
    selected_node_id: Option<NodeId>,
    selected_window_id: Option<u64>,
    onselected: EventHandler<(u64, NodeId)>,
) -> Element {
    let navigator = use_navigator();
    let mut radio = use_radio(DevtoolsChannel::UpdatedDOM);

    let items = {
        let radio = radio.read();
        radio
            .nodes
            .iter()
            .flat_map(|(window_id, nodes)| {
                let mut allowed_nodes = HashSet::new();
                nodes
                    .iter()
                    .filter_map(|node| {
                        let parent_is_open = node
                            .parent_id
                            .map(|node_id| {
                                allowed_nodes.contains(&node_id)
                                    && radio.expanded_nodes.contains(&(*window_id, node_id))
                            })
                            .unwrap_or(false);
                        let is_top_height = node.height == 1;
                        if parent_is_open || is_top_height {
                            allowed_nodes.insert(node.node_id);
                            let is_open = (node.children_len != 0).then_some(
                                radio.expanded_nodes.contains(&(*window_id, node.node_id)),
                            );
                            Some(NodeTreeItem {
                                is_open,
                                node_id: node.node_id,
                                window_id: *window_id,
                            })
                        } else {
                            None
                        }
                    })
                    .collect::<Vec<_>>()
            })
            .collect::<Vec<_>>()
    };

    if items.is_empty() {
        return rsx!(
            rect {
                width: "fill",
                height: "fill",
                main_align: "center",
                cross_align: "center",
                label {
                    "Waiting for an app to connect..."
                }
            }
        );
    }

    rsx!(VirtualScrollView {
        show_scrollbar: true,
        length: items.len(),
        item_size: 27.0,
        builder_args: (selected_node_id, selected_window_id, items),
        builder: move |i, options: &Option<(Option<NodeId>, Option<u64>, Vec<NodeTreeItem>)>| {
            let (selected_node_id, selected_window_id, items) = options.as_ref().unwrap();
            let NodeTreeItem {
                window_id,
                node_id,
                is_open,
            } = items[i];
            to_owned![onselected];
            rsx! {
                NodeElement {
                    key: "{node_id:?}-{window_id}",
                    is_selected: Some(node_id) == *selected_node_id && Some(window_id) == *selected_window_id,
                    is_open: is_open,
                    onarrow: move |_| {
                        let mut radio = radio.write();
                        if radio.expanded_nodes.contains(&(window_id, node_id)) {
                            radio.expanded_nodes.remove(&(window_id, node_id));
                        } else {
                            radio.expanded_nodes.insert((window_id, node_id));
                        }
                    },
                    onselected: move |_| {
                        onselected.call((window_id, node_id));

                        match router().current() {
                            Route::NodeInspectorComputedLayout { .. } => {
                                navigator.replace(Route::NodeInspectorComputedLayout { node_id, window_id });
                            }
                            _ => {
                                navigator.replace(Route::NodeInspectorLayout { node_id, window_id });
                            }
                        }
                    },
                    node_id,
                    window_id
                }
            }
        }
    })
}