freya_devtools_app/
node.rs

1use freya::prelude::*;
2use freya_core::integration::NodeId;
3
4use crate::hooks::use_node_info;
5
6#[derive(PartialEq)]
7pub struct NodeElement {
8    pub node_id: NodeId,
9    pub window_id: u64,
10    pub is_selected: bool,
11    pub is_open: Option<bool>,
12    pub on_selected: EventHandler<()>,
13    pub on_toggle: EventHandler<()>,
14    pub on_expand_all: EventHandler<()>,
15    pub on_collapse_all: EventHandler<()>,
16}
17
18impl Render for NodeElement {
19    fn render_key(&self) -> DiffKey
20    where
21        Self: RenderKey,
22    {
23        DiffKey::from(&(self.node_id, self.window_id))
24    }
25
26    fn render(&self) -> impl IntoElement {
27        let Some(node) = use_node_info(self.node_id, self.window_id) else {
28            return rect().into_element();
29        };
30
31        let margin_left = ((node.height + 1) * 10) as f32 - 18.;
32        let id = self.node_id.0;
33
34        let role = node.state.accessibility.builder.role();
35
36        let on_select = {
37            let on_selected = self.on_selected.clone();
38            move |_| on_selected.call(())
39        };
40
41        let on_open = {
42            let handler = self.on_toggle.clone();
43            let is_open = self.is_open;
44            move |e: Event<PressEventData>| {
45                if is_open.is_some() {
46                    handler.call(());
47                    e.stop_propagation();
48                }
49            }
50        };
51
52        let on_secondary_press = {
53            let on_expand = self.on_toggle.clone();
54            let on_expand_all = self.on_expand_all.clone();
55            let on_collapse_all = self.on_collapse_all.clone();
56            let is_open = self.is_open;
57            move |_| {
58                let on_expand = on_expand.clone();
59                let on_expand_all = on_expand_all.clone();
60                let on_collapse_all = on_collapse_all.clone();
61                ContextMenu::open(
62                    Menu::new()
63                        .child(
64                            MenuItem::new()
65                                .on_press({
66                                    let on_expand = on_expand.clone();
67                                    move |_| {
68                                        on_expand.call(());
69                                    }
70                                })
71                                .child(if Some(true) == is_open {
72                                    "Collapse"
73                                } else {
74                                    "Expand"
75                                }),
76                        )
77                        .child(
78                            MenuItem::new()
79                                .on_press({
80                                    let on_expand_all = on_expand_all.clone();
81                                    move |_| {
82                                        on_expand_all.call(());
83                                    }
84                                })
85                                .child("Expand All"),
86                        )
87                        .child(
88                            MenuItem::new()
89                                .on_press({
90                                    let on_collapse_all = on_collapse_all.clone();
91                                    move |_| {
92                                        on_collapse_all.call(());
93                                    }
94                                })
95                                .child("Collapse All"),
96                        ),
97                );
98            }
99        };
100
101        let arrow_button = self.is_open.map(|is_open| {
102            let arrow_degrees = if is_open { 0. } else { 270. };
103            Button::new()
104                .corner_radius(99.)
105                .border_fill(Color::TRANSPARENT)
106                .padding(Gaps::new_all(6.))
107                .background(Color::TRANSPARENT)
108                .on_press(on_open)
109                .child(ArrowIcon::new().rotate(arrow_degrees).fill(Color::WHITE))
110        });
111
112        Button::new()
113            .corner_radius(99.)
114            .width(Size::fill())
115            .height(Size::px(27.))
116            .border_fill(Color::TRANSPARENT)
117            .background(if self.is_selected {
118                (40, 40, 40).into()
119            } else {
120                Color::TRANSPARENT
121            })
122            .hover_background(if self.is_selected {
123                (40, 40, 40).into()
124            } else {
125                Color::from((45, 45, 45))
126            })
127            .on_press(on_select)
128            .on_secondary_press(on_secondary_press)
129            .child(
130                rect()
131                    .offset_x(margin_left)
132                    .direction(Direction::Horizontal)
133                    .width(Size::fill())
134                    .cross_align(Alignment::center())
135                    .child(rect().width(Size::px(25.)).maybe_child(arrow_button))
136                    .child(
137                        paragraph()
138                            .max_lines(1)
139                            .font_size(14.)
140                            .text_overflow(TextOverflow::Ellipsis)
141                            .span(
142                                Span::new(if node.is_window {
143                                    "Window".to_string()
144                                } else if role == AccessibilityRole::GenericContainer {
145                                    "rect".to_string()
146                                } else {
147                                    format!("{role:?}")
148                                })
149                                .color(Color::WHITE),
150                            )
151                            .span(
152                                Span::new(if node.is_window {
153                                    format!(", id: {}", self.window_id)
154                                } else {
155                                    format!(", id: {}", id)
156                                })
157                                .color(Color::from_rgb(200, 200, 200)),
158                            ),
159                    ),
160            )
161            .into()
162    }
163}