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.hovered_row == Some(i),
137 self.focused,
138 node.icon,
139 node.label.clone(),
140 Arc::clone(&self.font),
141 font_size,
142 ind,
143 rh,
144 );
145
146 tree_row.layout(Size::new(w, rh));
147 tree_row.set_bounds(Rect::new(0.0, y_bot, w, rh));
148
149 let toggle_rect = if flat.has_children {
150 let tlb = tree_row.toggle_local_bounds;
151 Some(Rect::new(tlb.x, y_bot + tlb.y, tlb.width, tlb.height))
152 } else {
153 None
154 };
155
156 self.row_metas.push(RowMeta {
157 node_idx: flat.node_idx,
158 toggle_rect,
159 });
160 self.row_widgets.push(Box::new(tree_row));
161 }
162 self.last_row_content_sig = Some(new_sig);
163
164 available
165 }
166
167 fn paint(&mut self, ctx: &mut dyn DrawCtx) {
168 let h = self.bounds.height;
169 let w = self.bounds.width;
170 let content_w = w - SCROLLBAR_W;
171 let v = ctx.visuals().clone();
172
173 ctx.set_fill_color(v.window_fill);
175 ctx.begin_path();
176 ctx.rect(0.0, 0.0, w, h);
177 ctx.fill();
178
179 let sb_x = self.scrollbar_x();
181 if self.content_height > h {
182 ctx.set_fill_color(v.scroll_track);
183 ctx.begin_path();
184 ctx.rect(sb_x, 0.0, SCROLLBAR_W, h);
185 ctx.fill();
186 if let Some((thumb_y, thumb_h)) = self.thumb_metrics() {
187 let thumb_color = if self.dragging_scrollbar {
188 v.scroll_thumb_dragging
189 } else if self.hovered_scrollbar {
190 v.scroll_thumb_hovered
191 } else {
192 v.scroll_thumb
193 };
194 ctx.set_fill_color(thumb_color);
195 ctx.begin_path();
196 ctx.rounded_rect(sb_x + 2.0, thumb_y, SCROLLBAR_W - 4.0, thumb_h, 3.0);
197 ctx.fill();
198 }
199 }
200
201 ctx.clip_rect(0.0, 0.0, content_w, h);
204
205 let rows = flatten_visible(&self.nodes);
207 if let Some(drop_target) = self.drop_target {
208 if self.drag.as_ref().map_or(false, |d| d.live) {
209 let rh = self.row_height;
210 let off = self.scroll_offset;
211 let ind = self.indent_width;
212 let ref_node = match drop_target {
213 DropPosition::Before(ni)
214 | DropPosition::After(ni)
215 | DropPosition::AsChild(ni) => ni,
216 };
217 if let Some(ri) = rows.iter().position(|r| r.node_idx == ref_node) {
218 let y_bot = h - (ri as f64 + 1.0) * rh + off;
219 let indent = rows[ri].depth as f64 * ind + EXPAND_W;
220 match drop_target {
221 DropPosition::Before(_) => {
222 paint_drop_line(ctx, indent, y_bot + rh, content_w - indent)
223 }
224 DropPosition::After(_) => {
225 paint_drop_line(ctx, indent, y_bot, content_w - indent)
226 }
227 DropPosition::AsChild(_) => {
228 paint_drop_child_highlight(ctx, y_bot, content_w, rh)
229 }
230 }
231 }
232 }
233 }
234 if let Some(drag) = &self.drag {
235 if drag.live {
236 let label = self.nodes[drag.node_idx].label.clone();
237 let ic = icon_color(self.nodes[drag.node_idx].icon);
238 let pos = drag.current_pos;
239 let rh = self.row_height;
240 let font = Arc::clone(&self.font);
241 let fs = self.font_size;
242 paint_ghost(ctx, &label, pos, content_w, rh, &font, fs, ic);
243 }
244 }
245 }
246
247 fn on_event(&mut self, event: &Event) -> EventResult {
248 let result = match event {
254 Event::FocusGained => {
255 self.focused = true;
256 EventResult::Consumed
257 }
258 Event::FocusLost => {
259 self.focused = false;
260 EventResult::Consumed
261 }
262
263 Event::MouseWheel { delta_y, .. } => {
264 self.scroll_offset =
267 (self.scroll_offset + delta_y * 40.0).clamp(0.0, self.max_scroll());
268 self.hovered_row = None;
269 EventResult::Consumed
270 }
271
272 Event::MouseMove { pos } => self.handle_mouse_move(*pos),
273 Event::MouseDown {
274 pos,
275 button: MouseButton::Left,
276 modifiers,
277 } => self.handle_mouse_down(*pos, *modifiers),
278 Event::MouseUp {
279 button: MouseButton::Left,
280 pos,
281 ..
282 } => self.handle_mouse_up(*pos),
283 Event::KeyDown { key, modifiers } => self.handle_key_down(key, *modifiers),
284 _ => EventResult::Ignored,
285 };
286 if result == EventResult::Consumed {
287 crate::animation::request_draw();
288 }
289 result
290 }
291}