agg_gui/widgets/tree_view/
widget_impl.rs1use std::sync::Arc;
8
9use crate::draw_ctx::DrawCtx;
10use crate::event::{Event, EventResult, MouseButton};
11use crate::geometry::{Point, Rect, Size};
12use crate::layout_props::{HAnchor, Insets, VAnchor, WidgetBase};
13use crate::widget::Widget;
14
15use super::drag::{paint_drop_child_highlight, paint_drop_line, paint_ghost};
16use super::node::{flatten_visible, DropPosition, FlatRow};
17use super::row::{icon_color, TreeRow, EXPAND_W};
18use super::{RowMeta, TreeView, SCROLLBAR_W};
19
20impl Widget for TreeView {
21 fn type_name(&self) -> &'static str {
22 "TreeView"
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.row_widgets
32 }
33 fn children_mut(&mut self) -> &mut Vec<Box<dyn Widget>> {
34 &mut self.row_widgets
35 }
36 fn is_focusable(&self) -> bool {
37 true
38 }
39
40 fn margin(&self) -> Insets {
41 self.base.margin
42 }
43 fn widget_base(&self) -> Option<&WidgetBase> {
44 Some(&self.base)
45 }
46 fn widget_base_mut(&mut self) -> Option<&mut WidgetBase> {
47 Some(&mut self.base)
48 }
49 fn h_anchor(&self) -> HAnchor {
50 self.base.h_anchor
51 }
52 fn v_anchor(&self) -> VAnchor {
53 self.base.v_anchor
54 }
55 fn min_size(&self) -> Size {
56 self.base.min_size
57 }
58 fn max_size(&self) -> Size {
59 self.base.max_size
60 }
61
62 fn hit_test(&self, local_pos: Point) -> bool {
63 if self.drag.is_some() || self.dragging_scrollbar {
65 return true;
66 }
67 let b = self.bounds();
68 local_pos.x >= 0.0
69 && local_pos.x <= b.width
70 && local_pos.y >= 0.0
71 && local_pos.y <= b.height
72 }
73
74 fn layout(&mut self, available: Size) -> Size {
75 let rows = flatten_visible(&self.nodes);
76 self.content_height = rows.len() as f64 * self.row_height;
77 self.scroll_offset = self.scroll_offset.clamp(0.0, self.max_scroll());
78
79 let h = available.height;
80 let w = available.width - SCROLLBAR_W;
81 let rh = self.row_height;
82 let ind = self.indent_width;
83 let font_size = self.font_size;
84
85 let visible_rows: Vec<&FlatRow> = rows
92 .iter()
93 .filter(|flat| {
94 !self
95 .drag
96 .as_ref()
97 .map_or(false, |d| d.live && d.node_idx == flat.node_idx)
98 })
99 .collect();
100 let new_sig = self.row_content_signature();
101 let can_reuse = self.last_row_content_sig == Some(new_sig)
102 && self.row_widgets.len() == visible_rows.len()
103 && !self.row_widgets.is_empty();
104
105 if can_reuse {
106 for (i, flat) in visible_rows.iter().enumerate() {
109 let y_bot = h - (i as f64 + 1.0) * rh + self.scroll_offset;
110 let row = &mut self.row_widgets[i];
111 row.layout(Size::new(w, rh));
112 row.set_bounds(Rect::new(0.0, y_bot, w, rh));
113 if let Some(meta) = self.row_metas.get_mut(i) {
114 debug_assert_eq!(meta.node_idx, flat.node_idx);
115 if let Some(ref mut tr) = meta.toggle_rect {
116 tr.y = y_bot + (rh - tr.height) * 0.5;
117 }
118 }
119 }
120 return available;
121 }
122
123 self.row_widgets.clear();
125 self.row_metas.clear();
126
127 for (i, flat) in visible_rows.iter().enumerate() {
128 let node = &self.nodes[flat.node_idx];
129 let y_bot = h - (i as f64 + 1.0) * rh + self.scroll_offset;
130 let mut tree_row = TreeRow::new(
131 flat.node_idx,
132 flat.depth,
133 flat.has_children,
134 node.is_expanded,
135 node.is_selected,
136 self.focused,
137 node.icon,
138 node.label.clone(),
139 Arc::clone(&self.font),
140 font_size,
141 ind,
142 rh,
143 );
144
145 tree_row.layout(Size::new(w, rh));
146 tree_row.set_bounds(Rect::new(0.0, y_bot, w, rh));
147
148 let toggle_rect = if flat.has_children {
149 let tlb = tree_row.toggle_local_bounds;
150 Some(Rect::new(tlb.x, y_bot + tlb.y, tlb.width, tlb.height))
151 } else {
152 None
153 };
154
155 self.row_metas.push(RowMeta {
156 node_idx: flat.node_idx,
157 toggle_rect,
158 });
159 self.row_widgets.push(Box::new(tree_row));
160 }
161 self.last_row_content_sig = Some(new_sig);
162
163 available
164 }
165
166 fn paint(&mut self, ctx: &mut dyn DrawCtx) {
167 let h = self.bounds.height;
168 let w = self.bounds.width;
169 let content_w = w - SCROLLBAR_W;
170 let v = ctx.visuals().clone();
171
172 ctx.set_fill_color(v.window_fill);
174 ctx.begin_path();
175 ctx.rect(0.0, 0.0, w, h);
176 ctx.fill();
177
178 let sb_x = self.scrollbar_x();
180 if self.content_height > h {
181 ctx.set_fill_color(v.scroll_track);
182 ctx.begin_path();
183 ctx.rect(sb_x, 0.0, SCROLLBAR_W, h);
184 ctx.fill();
185 if let Some((thumb_y, thumb_h)) = self.thumb_metrics() {
186 let thumb_color = if self.dragging_scrollbar {
187 v.scroll_thumb_dragging
188 } else if self.hovered_scrollbar {
189 v.scroll_thumb_hovered
190 } else {
191 v.scroll_thumb
192 };
193 ctx.set_fill_color(thumb_color);
194 ctx.begin_path();
195 ctx.rounded_rect(sb_x + 2.0, thumb_y, SCROLLBAR_W - 4.0, thumb_h, 3.0);
196 ctx.fill();
197 }
198 }
199
200 ctx.clip_rect(0.0, 0.0, content_w, h);
203
204 if let Some(hi) = self.hovered_row {
210 if let (Some(meta), Some(row_widget)) =
211 (self.row_metas.get(hi), self.row_widgets.get(hi))
212 {
213 let is_sel = self
214 .nodes
215 .get(meta.node_idx)
216 .map(|n| n.is_selected)
217 .unwrap_or(false);
218 if !is_sel {
219 let rb = row_widget.bounds();
220 ctx.set_fill_color(crate::color::Color::rgba(
221 v.text_color.r,
222 v.text_color.g,
223 v.text_color.b,
224 0.08,
225 ));
226 ctx.begin_path();
227 ctx.rect(rb.x, rb.y, rb.width, rb.height);
228 ctx.fill();
229 }
230 }
231 }
232
233 let rows = flatten_visible(&self.nodes);
235 if let Some(drop_target) = self.drop_target {
236 if self.drag.as_ref().map_or(false, |d| d.live) {
237 let rh = self.row_height;
238 let off = self.scroll_offset;
239 let ind = self.indent_width;
240 let ref_node = match drop_target {
241 DropPosition::Before(ni)
242 | DropPosition::After(ni)
243 | DropPosition::AsChild(ni) => ni,
244 };
245 if let Some(ri) = rows.iter().position(|r| r.node_idx == ref_node) {
246 let y_bot = h - (ri as f64 + 1.0) * rh + off;
247 let indent = rows[ri].depth as f64 * ind + EXPAND_W;
248 match drop_target {
249 DropPosition::Before(_) => {
250 paint_drop_line(ctx, indent, y_bot + rh, content_w - indent)
251 }
252 DropPosition::After(_) => {
253 paint_drop_line(ctx, indent, y_bot, content_w - indent)
254 }
255 DropPosition::AsChild(_) => {
256 paint_drop_child_highlight(ctx, y_bot, content_w, rh)
257 }
258 }
259 }
260 }
261 }
262 if let Some(drag) = &self.drag {
263 if drag.live {
264 let label = self.nodes[drag.node_idx].label.clone();
265 let ic = icon_color(self.nodes[drag.node_idx].icon);
266 let pos = drag.current_pos;
267 let rh = self.row_height;
268 let font = Arc::clone(&self.font);
269 let fs = self.font_size;
270 paint_ghost(ctx, &label, pos, content_w, rh, &font, fs, ic);
271 }
272 }
273 }
274
275 fn on_event(&mut self, event: &Event) -> EventResult {
276 let result = match event {
282 Event::FocusGained => {
283 self.focused = true;
284 EventResult::Consumed
285 }
286 Event::FocusLost => {
287 self.focused = false;
288 EventResult::Consumed
289 }
290
291 Event::MouseWheel { delta_y, .. } => {
292 self.scroll_offset =
296 (self.scroll_offset - delta_y * 40.0).clamp(0.0, self.max_scroll());
297 self.hovered_row = None;
298 EventResult::Consumed
299 }
300
301 Event::MouseMove { pos } => self.handle_mouse_move(*pos),
302 Event::MouseDown {
303 pos,
304 button: MouseButton::Left,
305 modifiers,
306 } => self.handle_mouse_down(*pos, *modifiers),
307 Event::MouseUp {
308 button: MouseButton::Left,
309 pos,
310 ..
311 } => self.handle_mouse_up(*pos),
312 Event::KeyDown { key, modifiers } => self.handle_key_down(key, *modifiers),
313 _ => EventResult::Ignored,
314 };
315 if result == EventResult::Consumed {
316 crate::animation::request_draw();
317 }
318 result
319 }
320}