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_arrow: EventHandler<()>,
14}
15
16impl Render for NodeElement {
17    fn render_key(&self) -> DiffKey
18    where
19        Self: RenderKey,
20    {
21        DiffKey::from(&(self.node_id, self.window_id))
22    }
23    fn render(&self) -> impl IntoElement {
24        let Some(node) = use_node_info(self.node_id, self.window_id) else {
25            return rect().into_element();
26        };
27
28        let margin_left = ((node.height + 1) * 10) as f32 - 18.;
29        let id = self.node_id.0;
30
31        let role = node.state.accessibility.builder.role();
32
33        let on_select = {
34            let on_selected = self.on_selected.clone();
35            move |_| on_selected.call(())
36        };
37
38        let on_open = {
39            let handler = self.on_arrow.clone();
40            let is_open = self.is_open;
41            move |e: Event<PressEventData>| {
42                if is_open.is_some() {
43                    handler.call(());
44                    e.stop_propagation();
45                }
46            }
47        };
48
49        let arrow_button = self.is_open.map(|is_open| {
50            let arrow_degrees = if is_open { 0. } else { 270. };
51            Button::new()
52                .corner_radius(99.)
53                .border_fill(Color::TRANSPARENT)
54                .padding(Gaps::new_all(6.))
55                .background(Color::TRANSPARENT)
56                .on_press(on_open)
57                .child(ArrowIcon::new().rotate(arrow_degrees).fill(Color::WHITE))
58        });
59
60        Button::new()
61            .corner_radius(99.)
62            .width(Size::fill())
63            .height(Size::px(27.))
64            .border_fill(Color::TRANSPARENT)
65            .background(if self.is_selected {
66                (40, 40, 40).into()
67            } else {
68                Color::TRANSPARENT
69            })
70            .hover_background(if self.is_selected {
71                (40, 40, 40).into()
72            } else {
73                Color::from((45, 45, 45))
74            })
75            .on_press(on_select)
76            .child(
77                rect()
78                    .offset_x(margin_left)
79                    .direction(Direction::Horizontal)
80                    .width(Size::fill())
81                    .cross_align(Alignment::center())
82                    .child(rect().width(Size::px(25.)).maybe_child(arrow_button))
83                    .child(
84                        paragraph()
85                            .max_lines(1)
86                            .font_size(14.)
87                            // TODO: Add text overflow
88                            .text_overflow(TextOverflow::Ellipsis)
89                            .span(
90                                Span::new(if node.is_window {
91                                    "Window".to_string()
92                                } else if role == AccessibilityRole::GenericContainer {
93                                    "rect".to_string()
94                                } else {
95                                    format!("{role:?}")
96                                })
97                                .color(Color::WHITE),
98                            )
99                            .span(
100                                Span::new(if node.is_window {
101                                    format!(", id: {}", self.window_id)
102                                } else {
103                                    format!(", id: {}", id)
104                                })
105                                .color(Color::from_rgb(200, 200, 200)),
106                            ),
107                    ),
108            )
109            .into()
110    }
111}