agg_gui/widgets/inspector/
widget_impl.rs1use std::sync::Arc;
7
8use crate::draw_ctx::DrawCtx;
9use crate::event::{Event, EventResult, MouseButton};
10use crate::geometry::{Rect, Size};
11use crate::layout_props::{HAnchor, Insets, VAnchor, WidgetBase};
12use crate::widget::{InspectorOverlay, Widget};
13use crate::widgets::tree_view::{NodeIcon, TreeNode};
14
15use super::{
16 c_border, c_dim_text, c_header_bg, c_panel_bg, c_props_bg, c_split_bg, c_text,
17 InspectorPanel, HEADER_H, MIN_PROPS_H, MIN_TREE_H,
18};
19
20impl Widget for InspectorPanel {
21 fn type_name(&self) -> &'static str {
22 "InspectorPanel"
23 }
24 fn bounds(&self) -> Rect {
25 self.bounds
26 }
27 fn set_bounds(&mut self, b: Rect) {
28 self.bounds = b;
29 }
30 fn children(&self) -> &[Box<dyn Widget>] {
31 &self._children
32 }
33 fn children_mut(&mut self) -> &mut Vec<Box<dyn Widget>> {
34 &mut self._children
35 }
36
37 fn margin(&self) -> Insets {
38 self.base.margin
39 }
40 fn widget_base(&self) -> Option<&WidgetBase> {
41 Some(&self.base)
42 }
43 fn widget_base_mut(&mut self) -> Option<&mut WidgetBase> {
44 Some(&mut self.base)
45 }
46 fn h_anchor(&self) -> HAnchor {
47 self.base.h_anchor
48 }
49 fn v_anchor(&self) -> VAnchor {
50 self.base.v_anchor
51 }
52 fn min_size(&self) -> Size {
53 self.base.min_size
54 }
55 fn max_size(&self) -> Size {
56 self.base.max_size
57 }
58
59 fn layout(&mut self, available: Size) -> Size {
60 self.bounds.width = available.width;
61 self.bounds.height = available.height;
62
63 let nodes = self.nodes.borrow();
64 let nodes_fingerprint = (nodes.as_ptr() as usize, nodes.len());
70 let pending_state =
71 self.pending_expanded.is_some() || self.pending_selected.is_some();
72 let nodes_unchanged = !pending_state
73 && self.last_inspector_nodes_fingerprint == Some(nodes_fingerprint)
74 && !self.tree_view.nodes.is_empty();
75
76 if !nodes_unchanged {
77 let mut old_expanded: Vec<bool> =
79 self.tree_view.nodes.iter().map(|n| n.is_expanded).collect();
80 let mut old_selected: Vec<bool> =
81 self.tree_view.nodes.iter().map(|n| n.is_selected).collect();
82 if let Some(pe) = self.pending_expanded.take() {
83 old_expanded = pe;
84 }
85 if let Some(ps) = self.pending_selected.take() {
86 old_selected =
87 vec![false; old_expanded.len().max(ps.map(|i| i + 1).unwrap_or(0))];
88 if let Some(i) = ps {
89 if i < old_selected.len() {
90 old_selected[i] = true;
91 }
92 }
93 }
94
95 self.tree_view.nodes.clear();
96
97 let mut depth_stack: Vec<usize> = Vec::new();
100 let mut per_parent_counts: std::collections::HashMap<Option<usize>, u32> =
101 std::collections::HashMap::new();
102
103 for (orig_idx, node) in nodes.iter().enumerate() {
104 let parent = if node.depth == 0 {
105 None
106 } else {
107 depth_stack.get(node.depth.saturating_sub(1)).copied()
108 };
109 let order = {
110 let cnt = per_parent_counts.entry(parent).or_insert(0);
111 let o = *cnt;
112 *cnt += 1;
113 o
114 };
115 let b = &node.screen_bounds;
116 let label =
117 format!("{} {:.0}×{:.0}", node.type_name, b.width, b.height);
118 let tv_idx = self.tree_view.nodes.len();
119 self.tree_view
120 .nodes
121 .push(TreeNode::new(label, NodeIcon::Package, parent, order));
122 self.tree_view.nodes[tv_idx].is_expanded =
123 old_expanded.get(orig_idx).copied().unwrap_or(true);
124 self.tree_view.nodes[tv_idx].is_selected =
125 old_selected.get(orig_idx).copied().unwrap_or(false);
126 if depth_stack.len() <= node.depth {
127 depth_stack.resize(node.depth + 1, 0);
128 }
129 depth_stack[node.depth] = tv_idx;
130 }
131 self.last_inspector_nodes_fingerprint = Some(nodes_fingerprint);
132 }
133
134 self.selected = self.tree_view.nodes.iter().position(|n| n.is_selected);
135
136 *self.hovered_bounds.borrow_mut() = self
137 .tree_view
138 .hovered_node_idx()
139 .and_then(|i| nodes.get(i))
140 .map(|n| InspectorOverlay {
141 bounds: n.screen_bounds,
142 margin: n.margin,
143 padding: n.padding,
144 });
145
146 let tree_w = available.width;
147 let tree_bot = self.tree_origin_y();
148 let tree_top = self.list_area_h();
149 let tree_h = (tree_top - tree_bot).max(0.0);
150 self.tree_view
151 .set_bounds(Rect::new(0.0, tree_bot, tree_w, tree_h));
152 self.tree_view.layout(Size::new(tree_w, tree_h));
153
154 self._children[0].set_bounds(self.tree_view.bounds());
157
158 if let Some(cell) = &self.snapshot_out {
159 *cell.borrow_mut() = Some(self.saved_state());
160 }
161
162 available
163 }
164
165 fn paint(&mut self, ctx: &mut dyn DrawCtx) {
166 let w = self.bounds.width;
167 let h = self.bounds.height;
168 let sy = self.split_y();
169 let hdr_y = h - HEADER_H;
170 let v = ctx.visuals().clone();
171
172 ctx.set_fill_color(c_panel_bg(&v));
174 ctx.begin_path();
175 ctx.rect(0.0, 0.0, w, h);
176 ctx.fill();
177
178 ctx.set_stroke_color(c_border(&v));
179 ctx.set_line_width(1.0);
180 ctx.begin_path();
181 ctx.move_to(0.0, 0.0);
182 ctx.line_to(0.0, h);
183 ctx.stroke();
184
185 ctx.set_fill_color(c_header_bg(&v));
187 ctx.begin_path();
188 ctx.rect(0.0, hdr_y, w, HEADER_H);
189 ctx.fill();
190
191 ctx.set_stroke_color(c_border(&v));
192 ctx.set_line_width(1.0);
193 ctx.begin_path();
194 ctx.move_to(0.0, hdr_y);
195 ctx.line_to(w, hdr_y);
196 ctx.stroke();
197
198 ctx.set_font(Arc::clone(&self.font));
199 ctx.set_font_size(13.0);
200 ctx.set_fill_color(c_text(&v));
201 let title = "Widget Inspector";
202 if let Some(m) = ctx.measure_text(title) {
203 ctx.fill_text(
204 title,
205 12.0,
206 hdr_y + (HEADER_H - m.ascent - m.descent) * 0.5 + m.descent,
207 );
208 }
209
210 let count_txt = format!("{} widgets", self.nodes.borrow().len());
211 ctx.set_font_size(11.0);
212 ctx.set_fill_color(c_dim_text(&v));
213 if let Some(m) = ctx.measure_text(&count_txt) {
214 ctx.fill_text(
215 &count_txt,
216 w - m.width - 10.0,
217 hdr_y + (HEADER_H - m.ascent - m.descent) * 0.5 + m.descent,
218 );
219 }
220
221 ctx.set_fill_color(c_props_bg(&v));
223 ctx.begin_path();
224 ctx.rect(0.0, 0.0, w, sy - 2.0);
225 ctx.fill();
226 self.paint_properties(ctx, sy - 2.0);
227
228 ctx.set_fill_color(c_split_bg(&v));
230 ctx.begin_path();
231 ctx.rect(0.0, sy - 2.0, w, 4.0);
232 ctx.fill();
233 ctx.set_stroke_color(c_border(&v));
234 ctx.set_line_width(1.0);
235 ctx.begin_path();
236 ctx.move_to(0.0, sy);
237 ctx.line_to(w, sy);
238 ctx.stroke();
239
240 let tree_bot = self.tree_origin_y();
242 let tree_top = self.list_area_h();
243 let tree_h = (tree_top - tree_bot).max(0.0);
244 if tree_h > 0.0 {
245 ctx.save();
246 ctx.translate(0.0, tree_bot);
247 ctx.clip_rect(0.0, 0.0, w, tree_h);
248 crate::widget::paint_subtree(&mut self.tree_view, ctx);
249 ctx.restore();
250 }
251 }
252
253 fn on_event(&mut self, event: &Event) -> EventResult {
254 match event {
255 Event::MouseDown {
256 pos,
257 button: MouseButton::Left,
258 ..
259 } => {
260 if pos.y < self.split_y() - 2.0 && self.try_emit_base_edit_from_click(*pos) {
261 return EventResult::Consumed;
262 }
263 #[cfg(feature = "reflect")]
264 if pos.y < self.split_y() - 2.0 && self.try_emit_edit_from_click(*pos) {
265 return EventResult::Consumed;
266 }
267 if self.on_split_handle(*pos) {
268 self.split_dragging = true;
269 return EventResult::Consumed;
270 }
271 if self.pos_in_tree_area(*pos) {
272 return self.forward_to_tree(event);
273 }
274 EventResult::Ignored
275 }
276 Event::MouseMove { pos } => {
277 if self.split_dragging {
278 self.props_h = pos.y.clamp(
279 MIN_PROPS_H,
280 (self.list_area_h() - MIN_TREE_H).max(MIN_PROPS_H),
281 );
282 crate::animation::request_draw();
283 return EventResult::Consumed;
284 }
285 if self.pos_in_tree_area(*pos) {
286 let _ = self.forward_to_tree(event);
287 self.update_hovered_bounds_from_tree();
288 } else if self.hovered_bounds.borrow().is_some() {
289 *self.hovered_bounds.borrow_mut() = None;
290 crate::animation::request_draw_without_invalidation();
291 }
292 EventResult::Ignored
293 }
294 Event::MouseUp {
295 button: MouseButton::Left,
296 pos,
297 ..
298 } => {
299 if self.split_dragging {
300 self.split_dragging = false;
301 crate::animation::request_draw();
302 return EventResult::Consumed;
303 }
304 if self.pos_in_tree_area(*pos) {
305 return self.forward_to_tree(event);
306 }
307 EventResult::Ignored
308 }
309 Event::MouseWheel { pos, .. } if self.pos_in_tree_area(*pos) => {
310 self.forward_to_tree(event)
311 }
312 _ => EventResult::Ignored,
313 }
314 }
315}