1use crate::{
2 check_box::{CheckBoxBuilder, CheckBoxMessage},
3 core::pool::Handle,
4 define_constructor,
5 grid::{Column, GridBuilder, Row},
6 message::{MessageDirection, UiMessage},
7 utils::{make_arrow, ArrowDirection},
8 widget::{Widget, WidgetBuilder, WidgetMessage},
9 BuildContext, Control, UiNode, UserInterface, VerticalAlignment,
10};
11use std::{
12 any::{Any, TypeId},
13 ops::{Deref, DerefMut},
14};
15
16#[derive(Debug, Clone, PartialEq)]
17pub enum ExpanderMessage {
18 Expand(bool),
19}
20
21impl ExpanderMessage {
22 define_constructor!(ExpanderMessage:Expand => fn expand(bool), layout: false);
23}
24
25#[derive(Clone)]
26pub struct Expander {
27 widget: Widget,
28 content: Handle<UiNode>,
29 expander: Handle<UiNode>,
30 is_expanded: bool,
31}
32
33crate::define_widget_deref!(Expander);
34
35impl Control for Expander {
36 fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
37 if type_id == TypeId::of::<Self>() {
38 Some(self)
39 } else {
40 None
41 }
42 }
43
44 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
45 if let Some(&ExpanderMessage::Expand(expand)) = message.data::<ExpanderMessage>() {
46 if message.destination() == self.handle()
47 && message.direction() == MessageDirection::ToWidget
48 && self.is_expanded != expand
49 {
50 ui.send_message(CheckBoxMessage::checked(
52 self.expander,
53 MessageDirection::ToWidget,
54 Some(expand),
55 ));
56 ui.send_message(WidgetMessage::visibility(
58 self.content,
59 MessageDirection::ToWidget,
60 expand,
61 ));
62 self.is_expanded = expand;
63 }
64 } else if let Some(CheckBoxMessage::Check(value)) = message.data::<CheckBoxMessage>() {
65 if message.destination() == self.expander
66 && message.direction() == MessageDirection::FromWidget
67 {
68 ui.send_message(ExpanderMessage::expand(
69 self.handle,
70 MessageDirection::ToWidget,
71 value.unwrap_or(false),
72 ));
73 }
74 }
75 self.widget.handle_routed_message(ui, message);
76 }
77}
78
79pub struct ExpanderBuilder {
80 pub widget_builder: WidgetBuilder,
81 header: Handle<UiNode>,
82 content: Handle<UiNode>,
83 check_box: Handle<UiNode>,
84 is_expanded: bool,
85 expander_column: Option<Column>,
86}
87
88impl ExpanderBuilder {
89 pub fn new(widget_builder: WidgetBuilder) -> Self {
90 Self {
91 widget_builder,
92 header: Handle::NONE,
93 content: Handle::NONE,
94 check_box: Default::default(),
95 is_expanded: true,
96 expander_column: None,
97 }
98 }
99
100 pub fn with_header(mut self, header: Handle<UiNode>) -> Self {
101 self.header = header;
102 self
103 }
104
105 pub fn with_content(mut self, content: Handle<UiNode>) -> Self {
106 self.content = content;
107 self
108 }
109
110 pub fn with_expanded(mut self, expanded: bool) -> Self {
111 self.is_expanded = expanded;
112 self
113 }
114
115 pub fn with_checkbox(mut self, check_box: Handle<UiNode>) -> Self {
116 self.check_box = check_box;
117 self
118 }
119
120 pub fn with_expander_column(mut self, expander_column: Column) -> Self {
121 self.expander_column = Some(expander_column);
122 self
123 }
124
125 pub fn build(self, ctx: &mut BuildContext<'_>) -> Handle<UiNode> {
126 let expander = if self.check_box.is_some() {
127 self.check_box
128 } else {
129 CheckBoxBuilder::new(
130 WidgetBuilder::new().with_vertical_alignment(VerticalAlignment::Center),
131 )
132 .with_check_mark(make_arrow(ctx, ArrowDirection::Bottom, 8.0))
133 .with_uncheck_mark(make_arrow(ctx, ArrowDirection::Right, 8.0))
134 .checked(Some(self.is_expanded))
135 .build(ctx)
136 };
137
138 ctx[expander].set_row(0).set_column(0);
139
140 if self.header.is_some() {
141 ctx[self.header].set_row(0).set_column(1);
142 }
143
144 let grid = GridBuilder::new(
145 WidgetBuilder::new()
146 .with_child(expander)
147 .with_child(self.header),
148 )
149 .add_row(Row::auto())
150 .add_column(self.expander_column.unwrap_or_else(Column::auto))
151 .add_column(Column::stretch())
152 .build(ctx);
153
154 if self.content.is_some() {
155 ctx[self.content]
156 .set_row(1)
157 .set_column(0)
158 .set_visibility(self.is_expanded);
159 }
160
161 let e = UiNode::new(Expander {
162 widget: self
163 .widget_builder
164 .with_child(
165 GridBuilder::new(
166 WidgetBuilder::new()
167 .with_child(grid)
168 .with_child(self.content),
169 )
170 .add_column(Column::auto())
171 .add_row(Row::auto())
172 .add_row(Row::stretch())
173 .build(ctx),
174 )
175 .build(),
176 content: self.content,
177 expander,
178 is_expanded: self.is_expanded,
179 });
180 ctx.add_node(e)
181 }
182}