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