1use crate::{
11 border::BorderBuilder,
12 brush::Brush,
13 button::{Button, ButtonBuilder, ButtonMessage},
14 core::algebra::Vector2,
15 core::{color::Color, pool::Handle},
16 decorator::{DecoratorBuilder, DecoratorMessage},
17 define_constructor,
18 grid::{Column, GridBuilder, Row},
19 message::{MessageDirection, UiMessage},
20 stack_panel::StackPanelBuilder,
21 text::TextMessage,
22 widget::{Widget, WidgetBuilder, WidgetMessage},
23 BuildContext, Control, NodeHandleMapping, Thickness, UiNode, UserInterface, BRUSH_DARK,
24 BRUSH_DARKEST, BRUSH_LIGHT,
25};
26use std::any::{Any, TypeId};
27use std::ops::{Deref, DerefMut};
28
29#[derive(Debug, Copy, Clone, PartialEq)]
30pub struct SelectionState(pub(in crate) bool);
31
32#[derive(Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash, Debug)]
33pub enum TreeExpansionStrategy {
34 Direct,
36 RecursiveDescendants,
38 RecursiveAncestors,
40}
41
42#[derive(Debug, Clone, PartialEq)]
43pub enum TreeMessage {
44 Expand {
45 expand: bool,
46 expansion_strategy: TreeExpansionStrategy,
47 },
48 AddItem(Handle<UiNode>),
49 RemoveItem(Handle<UiNode>),
50 SetExpanderShown(bool),
51 SetItems(Vec<Handle<UiNode>>),
52 Select(SelectionState),
54}
55
56impl TreeMessage {
57 define_constructor!(TreeMessage:Expand => fn expand(expand: bool, expansion_strategy: TreeExpansionStrategy), layout: false);
58 define_constructor!(TreeMessage:AddItem => fn add_item(Handle<UiNode>), layout: false);
59 define_constructor!(TreeMessage:RemoveItem => fn remove_item(Handle<UiNode>), layout: false);
60 define_constructor!(TreeMessage:SetExpanderShown => fn set_expander_shown(bool), layout: false);
61 define_constructor!(TreeMessage:SetItems => fn set_items(Vec<Handle<UiNode >>), layout: false);
62 define_constructor!(TreeMessage:Select => fn select(SelectionState), layout: false);
63}
64
65#[derive(Debug, Clone, PartialEq)]
66pub enum TreeRootMessage {
67 AddItem(Handle<UiNode>),
68 RemoveItem(Handle<UiNode>),
69 Items(Vec<Handle<UiNode>>),
70 Selected(Vec<Handle<UiNode>>),
71 ExpandAll,
72 CollapseAll,
73}
74
75impl TreeRootMessage {
76 define_constructor!(TreeRootMessage:AddItem => fn add_item(Handle<UiNode>), layout: false);
77 define_constructor!(TreeRootMessage:RemoveItem=> fn remove_item(Handle<UiNode>), layout: false);
78 define_constructor!(TreeRootMessage:Items => fn items(Vec<Handle<UiNode >>), layout: false);
79 define_constructor!(TreeRootMessage:Selected => fn select(Vec<Handle<UiNode >>), layout: false);
80 define_constructor!(TreeRootMessage:ExpandAll => fn expand_all(), layout: false);
81 define_constructor!(TreeRootMessage:CollapseAll => fn collapse_all(), layout: false);
82}
83
84#[derive(Debug, Clone)]
85pub struct Tree {
86 widget: Widget,
87 expander: Handle<UiNode>,
88 content: Handle<UiNode>,
89 panel: Handle<UiNode>,
90 is_expanded: bool,
91 background: Handle<UiNode>,
92 items: Vec<Handle<UiNode>>,
93 is_selected: bool,
94 always_show_expander: bool,
95}
96
97crate::define_widget_deref!(Tree);
98
99impl Control for Tree {
100 fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
101 if type_id == TypeId::of::<Self>() {
102 Some(self)
103 } else {
104 None
105 }
106 }
107
108 fn resolve(&mut self, node_map: &NodeHandleMapping) {
109 node_map.resolve(&mut self.content);
110 node_map.resolve(&mut self.expander);
111 node_map.resolve(&mut self.panel);
112 node_map.resolve(&mut self.background);
113 }
114
115 fn arrange_override(&self, ui: &UserInterface, final_size: Vector2<f32>) -> Vector2<f32> {
116 let size = self.widget.arrange_override(ui, final_size);
117
118 let expander_visibility = !self.items.is_empty() || self.always_show_expander;
119 ui.send_message(WidgetMessage::visibility(
120 self.expander,
121 MessageDirection::ToWidget,
122 expander_visibility,
123 ));
124
125 size
126 }
127
128 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
129 self.widget.handle_routed_message(ui, message);
130
131 if let Some(ButtonMessage::Click) = message.data::<ButtonMessage>() {
132 if message.destination() == self.expander {
133 ui.send_message(TreeMessage::expand(
134 self.handle(),
135 MessageDirection::ToWidget,
136 !self.is_expanded,
137 TreeExpansionStrategy::Direct,
138 ));
139 }
140 } else if let Some(WidgetMessage::MouseDown { .. }) = message.data::<WidgetMessage>() {
141 if !message.handled() {
142 let keyboard_modifiers = ui.keyboard_modifiers();
143 if !keyboard_modifiers.alt {
145 if let Some((tree_root_handle, tree_root)) =
146 ui.try_borrow_by_type_up::<TreeRoot>(self.parent())
147 {
148 let selection = if keyboard_modifiers.control {
149 let mut selection = tree_root.selected.clone();
150 if let Some(existing) = selection.iter().position(|&h| h == self.handle)
151 {
152 selection.remove(existing);
153 } else {
154 selection.push(self.handle);
155 }
156 Some(selection)
157 } else if !self.is_selected {
158 Some(vec![self.handle()])
159 } else {
160 None
161 };
162 if let Some(selection) = selection {
163 ui.send_message(TreeRootMessage::select(
164 tree_root_handle,
165 MessageDirection::ToWidget,
166 selection,
167 ));
168 }
169 message.set_handled(true);
170 }
171 }
172 }
173 } else if let Some(msg) = message.data::<TreeMessage>() {
174 if message.destination() == self.handle() {
175 match msg {
176 &TreeMessage::Expand {
177 expand,
178 expansion_strategy,
179 } => {
180 self.is_expanded = expand;
181 ui.send_message(WidgetMessage::visibility(
182 self.panel,
183 MessageDirection::ToWidget,
184 self.is_expanded,
185 ));
186 if let Some(expander) = ui.node(self.expander).cast::<Button>() {
187 let content = expander.content();
188 let text = if expand { "-" } else { "+" };
189 ui.send_message(TextMessage::text(
190 content,
191 MessageDirection::ToWidget,
192 text.to_owned(),
193 ));
194 }
195
196 match expansion_strategy {
197 TreeExpansionStrategy::RecursiveDescendants => {
198 for &item in self.items() {
199 ui.send_message(TreeMessage::expand(
200 item,
201 MessageDirection::ToWidget,
202 expand,
203 expansion_strategy,
204 ));
205 }
206 }
207 TreeExpansionStrategy::RecursiveAncestors => {
208 let parent_tree =
213 self.find_by_criteria_up(ui, |n| n.cast::<Tree>().is_some());
214 if parent_tree.is_some() {
215 ui.send_message(TreeMessage::expand(
216 parent_tree,
217 MessageDirection::ToWidget,
218 expand,
219 expansion_strategy,
220 ));
221 }
222 }
223 TreeExpansionStrategy::Direct => {
224 }
227 }
228 }
229 &TreeMessage::SetExpanderShown(show) => {
230 self.always_show_expander = show;
231 self.invalidate_arrange();
232 }
233 &TreeMessage::AddItem(item) => {
234 ui.send_message(WidgetMessage::link(
235 item,
236 MessageDirection::ToWidget,
237 self.panel,
238 ));
239
240 self.items.push(item);
241 }
242 &TreeMessage::RemoveItem(item) => {
243 if let Some(pos) = self.items.iter().position(|&i| i == item) {
244 ui.send_message(WidgetMessage::remove(
245 item,
246 MessageDirection::ToWidget,
247 ));
248 self.items.remove(pos);
249 }
250 }
251 TreeMessage::SetItems(items) => {
252 for &item in self.items.iter() {
253 ui.send_message(WidgetMessage::remove(
254 item,
255 MessageDirection::ToWidget,
256 ));
257 }
258 for &item in items {
259 ui.send_message(WidgetMessage::link(
260 item,
261 MessageDirection::ToWidget,
262 self.panel,
263 ));
264 }
265 self.items = items.clone();
266 }
267 &TreeMessage::Select(state) => {
268 if self.is_selected != state.0 {
269 self.is_selected = state.0;
270 ui.send_message(DecoratorMessage::select(
271 self.background,
272 MessageDirection::ToWidget,
273 self.is_selected,
274 ));
275 }
276 }
277 }
278 }
279 }
280 }
281}
282
283impl Tree {
284 pub fn content(&self) -> Handle<UiNode> {
285 self.content
286 }
287
288 pub fn back(&self) -> Handle<UiNode> {
289 self.background
290 }
291
292 pub fn items(&self) -> &[Handle<UiNode>] {
293 &self.items
294 }
295
296 pub fn add_item(tree: Handle<UiNode>, item: Handle<UiNode>, ctx: &mut BuildContext) {
299 if let Some(tree) = ctx[tree].cast_mut::<Tree>() {
300 tree.items.push(item);
301 let panel = tree.panel;
302 ctx.link(item, panel);
303 }
304 }
305
306 pub fn expanded(&self) -> bool {
307 self.is_expanded
308 }
309
310 pub fn expander_shown(&self) -> bool {
311 self.always_show_expander
312 }
313}
314
315pub struct TreeBuilder {
316 widget_builder: WidgetBuilder,
317 items: Vec<Handle<UiNode>>,
318 content: Handle<UiNode>,
319 is_expanded: bool,
320 always_show_expander: bool,
321 back: Option<Handle<UiNode>>,
322}
323
324impl TreeBuilder {
325 pub fn new(widget_builder: WidgetBuilder) -> Self {
326 Self {
327 widget_builder,
328 items: Default::default(),
329 content: Default::default(),
330 is_expanded: true,
331 always_show_expander: false,
332 back: None,
333 }
334 }
335
336 pub fn with_items(mut self, items: Vec<Handle<UiNode>>) -> Self {
337 self.items = items;
338 self
339 }
340
341 pub fn with_content(mut self, content: Handle<UiNode>) -> Self {
342 self.content = content;
343 self
344 }
345
346 pub fn with_expanded(mut self, expanded: bool) -> Self {
347 self.is_expanded = expanded;
348 self
349 }
350
351 pub fn with_always_show_expander(mut self, state: bool) -> Self {
352 self.always_show_expander = state;
353 self
354 }
355
356 pub fn with_back(mut self, back: Handle<UiNode>) -> Self {
357 self.back = Some(back);
358 self
359 }
360
361 pub fn build_tree(self, ctx: &mut BuildContext) -> Tree {
362 let expander = build_expander_button(
363 self.always_show_expander,
364 !self.items.is_empty(),
365 self.is_expanded,
366 ctx,
367 );
368
369 if self.content.is_some() {
370 ctx[self.content].set_row(0).set_column(1);
371 };
372
373 let internals = GridBuilder::new(
374 WidgetBuilder::new()
375 .on_column(0)
376 .on_row(0)
377 .with_margin(Thickness {
378 left: 1.0,
379 top: 1.0,
380 right: 0.0,
381 bottom: 1.0,
382 })
383 .with_child(expander)
384 .with_child(self.content),
385 )
386 .add_column(Column::auto())
387 .add_column(Column::stretch())
388 .add_row(Row::strict(20.0))
389 .build(ctx);
390
391 let item_background = self.back.unwrap_or_else(|| {
392 DecoratorBuilder::new(BorderBuilder::new(
393 WidgetBuilder::new()
394 .with_foreground(BRUSH_LIGHT)
395 .with_background(Brush::Solid(Color::TRANSPARENT)),
396 ))
397 .with_selected_brush(BRUSH_DARKEST)
398 .with_hover_brush(BRUSH_DARK)
399 .with_normal_brush(Brush::Solid(Color::TRANSPARENT))
400 .with_pressed_brush(Brush::Solid(Color::TRANSPARENT))
401 .with_pressable(false)
402 .build(ctx)
403 });
404
405 ctx.link(internals, item_background);
406
407 let panel;
408 let grid = GridBuilder::new(
409 WidgetBuilder::new()
410 .with_child(item_background)
411 .with_child({
412 panel = StackPanelBuilder::new(
413 WidgetBuilder::new()
414 .on_row(1)
415 .on_column(0)
416 .with_margin(Thickness::left(15.0))
417 .with_visibility(self.is_expanded)
418 .with_children(self.items.iter().cloned()),
419 )
420 .build(ctx);
421 panel
422 }),
423 )
424 .add_column(Column::auto())
425 .add_row(Row::strict(24.0))
426 .add_row(Row::stretch())
427 .build(ctx);
428
429 Tree {
430 widget: self
431 .widget_builder
432 .with_allow_drag(true)
433 .with_allow_drop(true)
434 .with_child(grid)
435 .build(),
436 content: self.content,
437 panel,
438 is_expanded: self.is_expanded,
439 expander,
440 background: item_background,
441 items: self.items,
442 is_selected: false,
443 always_show_expander: self.always_show_expander,
444 }
445 }
446
447 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
448 let tree = self.build_tree(ctx);
449 ctx.add_node(UiNode::new(tree))
450 }
451}
452
453fn build_expander_button(
454 always_show_expander: bool,
455 items_populated: bool,
456 is_expanded: bool,
457 ctx: &mut BuildContext,
458) -> Handle<UiNode> {
459 ButtonBuilder::new(
460 WidgetBuilder::new()
461 .with_width(20.0)
462 .with_visibility(always_show_expander || items_populated)
463 .on_row(0)
464 .on_column(0),
465 )
466 .with_text(if is_expanded { "-" } else { "+" })
467 .build(ctx)
468}
469
470#[derive(Debug, Clone)]
471pub struct TreeRoot {
472 widget: Widget,
473 panel: Handle<UiNode>,
474 items: Vec<Handle<UiNode>>,
475 selected: Vec<Handle<UiNode>>,
476}
477
478crate::define_widget_deref!(TreeRoot);
479
480impl Control for TreeRoot {
481 fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
482 if type_id == TypeId::of::<Self>() {
483 Some(self)
484 } else {
485 None
486 }
487 }
488
489 fn resolve(&mut self, node_map: &NodeHandleMapping) {
490 node_map.resolve(&mut self.panel);
491 node_map.resolve_slice(&mut self.selected);
492 }
493
494 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
495 self.widget.handle_routed_message(ui, message);
496
497 if let Some(msg) = message.data::<TreeRootMessage>() {
498 if message.destination() == self.handle()
499 && message.direction() == MessageDirection::ToWidget
500 {
501 match msg {
502 &TreeRootMessage::AddItem(item) => {
503 ui.send_message(WidgetMessage::link(
504 item,
505 MessageDirection::ToWidget,
506 self.panel,
507 ));
508
509 self.items.push(item);
510 }
511 &TreeRootMessage::RemoveItem(item) => {
512 if let Some(pos) = self.items.iter().position(|&i| i == item) {
513 ui.send_message(WidgetMessage::remove(
514 item,
515 MessageDirection::ToWidget,
516 ));
517 self.items.remove(pos);
518 }
519 }
520 TreeRootMessage::Items(items) => {
521 for &item in self.items.iter() {
522 ui.send_message(WidgetMessage::remove(
523 item,
524 MessageDirection::ToWidget,
525 ));
526 }
527 for &item in items {
528 ui.send_message(WidgetMessage::link(
529 item,
530 MessageDirection::ToWidget,
531 self.panel,
532 ));
533 }
534 self.items = items.to_vec();
535 }
536 TreeRootMessage::Selected(selected) => {
537 if &self.selected != selected {
538 let mut stack = self.children().to_vec();
539 while let Some(handle) = stack.pop() {
540 let node = ui.node(handle);
541 stack.extend_from_slice(node.children());
542
543 let new_selection_state = if selected.contains(&handle) {
544 SelectionState(true)
545 } else {
546 SelectionState(false)
547 };
548
549 if let Some(tree_ref) = node
550 .query_component(TypeId::of::<Tree>())
551 .and_then(|tree_ref| tree_ref.downcast_ref::<Tree>())
552 {
553 if tree_ref.is_selected != new_selection_state.0 {
554 ui.send_message(TreeMessage::select(
555 handle,
556 MessageDirection::ToWidget,
557 new_selection_state,
558 ));
559 }
560 }
561 }
562 self.selected = selected.clone();
563 ui.send_message(message.reverse());
564 }
565 }
566 TreeRootMessage::CollapseAll => {
567 self.expand_all(ui, false);
568 }
569 TreeRootMessage::ExpandAll => {
570 self.expand_all(ui, true);
571 }
572 }
573 }
574 }
575 }
576}
577
578impl TreeRoot {
579 pub fn items(&self) -> &[Handle<UiNode>] {
580 &self.items
581 }
582
583 fn expand_all(&self, ui: &UserInterface, expand: bool) {
584 for &item in self.items.iter() {
585 ui.send_message(TreeMessage::expand(
586 item,
587 MessageDirection::ToWidget,
588 expand,
589 TreeExpansionStrategy::RecursiveDescendants,
590 ));
591 }
592 }
593}
594
595pub struct TreeRootBuilder {
596 widget_builder: WidgetBuilder,
597 items: Vec<Handle<UiNode>>,
598}
599
600impl TreeRootBuilder {
601 pub fn new(widget_builder: WidgetBuilder) -> Self {
602 Self {
603 widget_builder,
604 items: Default::default(),
605 }
606 }
607
608 pub fn with_items(mut self, items: Vec<Handle<UiNode>>) -> Self {
609 self.items = items;
610 self
611 }
612
613 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
614 let panel =
615 StackPanelBuilder::new(WidgetBuilder::new().with_children(self.items.iter().cloned()))
616 .build(ctx);
617
618 let tree = TreeRoot {
619 widget: self.widget_builder.with_child(panel).build(),
620 panel,
621 items: self.items,
622 selected: Default::default(),
623 };
624
625 ctx.add_node(UiNode::new(tree))
626 }
627}