1use crate::{
5 border::BorderBuilder,
6 core::{algebra::Vector2, pool::Handle},
7 define_constructor,
8 grid::{Column, GridBuilder, Row},
9 list_view::{ListViewBuilder, ListViewMessage},
10 message::{MessageDirection, UiMessage},
11 popup::{Placement, PopupBuilder, PopupMessage},
12 utils::{make_arrow, ArrowDirection},
13 widget::Widget,
14 widget::{WidgetBuilder, WidgetMessage},
15 BuildContext, Control, NodeHandleMapping, UiNode, UserInterface, BRUSH_LIGHT,
16};
17use std::sync::mpsc::Sender;
18use std::{
19 any::{Any, TypeId},
20 ops::{Deref, DerefMut},
21};
22
23#[derive(Debug, Clone, PartialEq)]
24pub enum DropdownListMessage {
25 SelectionChanged(Option<usize>),
26 Items(Vec<Handle<UiNode>>),
27 AddItem(Handle<UiNode>),
28 Open,
29 Close,
30}
31
32impl DropdownListMessage {
33 define_constructor!(DropdownListMessage:SelectionChanged => fn selection(Option<usize>), layout: false);
34 define_constructor!(DropdownListMessage:Items => fn items(Vec<Handle<UiNode >>), layout: false);
35 define_constructor!(DropdownListMessage:AddItem => fn add_item(Handle<UiNode>), layout: false);
36 define_constructor!(DropdownListMessage:Open => fn open(), layout: false);
37 define_constructor!(DropdownListMessage:Close => fn close(), layout: false);
38}
39
40#[derive(Clone)]
41pub struct DropdownList {
42 widget: Widget,
43 popup: Handle<UiNode>,
44 items: Vec<Handle<UiNode>>,
45 list_view: Handle<UiNode>,
46 current: Handle<UiNode>,
47 selection: Option<usize>,
48 close_on_selection: bool,
49 main_grid: Handle<UiNode>,
50}
51
52crate::define_widget_deref!(DropdownList);
53
54impl Control for DropdownList {
55 fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
56 if type_id == TypeId::of::<Self>() {
57 Some(self)
58 } else {
59 None
60 }
61 }
62
63 fn on_remove(&self, sender: &Sender<UiMessage>) {
64 sender
67 .send(WidgetMessage::remove(
68 self.popup,
69 MessageDirection::ToWidget,
70 ))
71 .unwrap();
72 }
73
74 fn resolve(&mut self, node_map: &NodeHandleMapping) {
75 node_map.resolve(&mut self.popup);
76 node_map.resolve(&mut self.list_view);
77 node_map.resolve(&mut self.current);
78 node_map.resolve(&mut self.main_grid);
79 node_map.resolve_slice(&mut self.items);
80 }
81
82 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
83 self.widget.handle_routed_message(ui, message);
84
85 if let Some(WidgetMessage::MouseDown { .. }) = message.data::<WidgetMessage>() {
86 if message.destination() == self.handle()
87 || self.widget.has_descendant(message.destination(), ui)
88 {
89 ui.send_message(DropdownListMessage::open(
90 self.handle,
91 MessageDirection::ToWidget,
92 ));
93 }
94 } else if let Some(msg) = message.data::<DropdownListMessage>() {
95 if message.destination() == self.handle()
96 && message.direction() == MessageDirection::ToWidget
97 {
98 match msg {
99 DropdownListMessage::Open => {
100 ui.send_message(WidgetMessage::width(
101 self.popup,
102 MessageDirection::ToWidget,
103 self.actual_size().x,
104 ));
105 ui.send_message(PopupMessage::placement(
106 self.popup,
107 MessageDirection::ToWidget,
108 Placement::LeftBottom(self.handle),
109 ));
110 ui.send_message(PopupMessage::open(self.popup, MessageDirection::ToWidget));
111 }
112 DropdownListMessage::Close => {
113 ui.send_message(PopupMessage::close(
114 self.popup,
115 MessageDirection::ToWidget,
116 ));
117 }
118 DropdownListMessage::Items(items) => {
119 ui.send_message(ListViewMessage::items(
120 self.list_view,
121 MessageDirection::ToWidget,
122 items.clone(),
123 ));
124 self.items = items.clone();
125 }
126 &DropdownListMessage::AddItem(item) => {
127 ListViewMessage::add_item(self.list_view, MessageDirection::ToWidget, item);
128 self.items.push(item);
129 }
130 &DropdownListMessage::SelectionChanged(selection) => {
131 if selection != self.selection {
132 self.selection = selection;
133 ui.send_message(ListViewMessage::selection(
134 self.list_view,
135 MessageDirection::ToWidget,
136 selection,
137 ));
138
139 if self.current.is_some() {
144 ui.send_message(WidgetMessage::remove(
145 self.current,
146 MessageDirection::ToWidget,
147 ));
148 }
149 if let Some(index) = selection {
150 if let Some(item) = self.items.get(index) {
151 self.current = ui.copy_node(*item);
152 ui.send_message(WidgetMessage::link(
153 self.current,
154 MessageDirection::ToWidget,
155 self.main_grid,
156 ));
157 } else {
158 self.current = Handle::NONE;
159 }
160 } else {
161 self.current = Handle::NONE;
162 }
163
164 if self.close_on_selection {
165 ui.send_message(PopupMessage::close(
166 self.popup,
167 MessageDirection::ToWidget,
168 ));
169 }
170
171 ui.send_message(message.reverse());
172 }
173 }
174 }
175 }
176 }
177 }
178
179 fn preview_message(&self, ui: &UserInterface, message: &mut UiMessage) {
180 if let Some(ListViewMessage::SelectionChanged(selection)) =
181 message.data::<ListViewMessage>()
182 {
183 if message.direction() == MessageDirection::FromWidget
184 && message.destination() == self.list_view
185 && &self.selection != selection
186 {
187 ui.send_message(DropdownListMessage::selection(
190 self.handle,
191 MessageDirection::ToWidget,
192 *selection,
193 ));
194 }
195 } else if let Some(msg) = message.data::<PopupMessage>() {
196 if message.destination() == self.popup {
197 match msg {
198 PopupMessage::Open => {
199 ui.send_message(DropdownListMessage::open(
200 self.handle,
201 MessageDirection::FromWidget,
202 ));
203 }
204 PopupMessage::Close => {
205 ui.send_message(DropdownListMessage::open(
206 self.handle,
207 MessageDirection::FromWidget,
208 ));
209 }
210 _ => (),
211 }
212 }
213 }
214 }
215}
216
217impl DropdownList {
218 pub fn selection(&self) -> Option<usize> {
219 self.selection
220 }
221
222 pub fn close_on_selection(&self) -> bool {
223 self.close_on_selection
224 }
225
226 pub fn items(&self) -> &[Handle<UiNode>] {
227 &self.items
228 }
229}
230
231pub struct DropdownListBuilder {
232 widget_builder: WidgetBuilder,
233 items: Vec<Handle<UiNode>>,
234 selected: Option<usize>,
235 close_on_selection: bool,
236}
237
238impl DropdownListBuilder {
239 pub fn new(widget_builder: WidgetBuilder) -> Self {
240 Self {
241 widget_builder,
242 items: Default::default(),
243 selected: None,
244 close_on_selection: false,
245 }
246 }
247
248 pub fn with_items(mut self, items: Vec<Handle<UiNode>>) -> Self {
249 self.items = items;
250 self
251 }
252
253 pub fn with_selected(mut self, index: usize) -> Self {
254 self.selected = Some(index);
255 self
256 }
257
258 pub fn with_opt_selected(mut self, index: Option<usize>) -> Self {
259 self.selected = index;
260 self
261 }
262
263 pub fn with_close_on_selection(mut self, value: bool) -> Self {
264 self.close_on_selection = value;
265 self
266 }
267
268 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode>
269 where
270 Self: Sized,
271 {
272 let items_control = ListViewBuilder::new(
273 WidgetBuilder::new().with_max_size(Vector2::new(f32::INFINITY, 200.0)),
274 )
275 .with_items(self.items.clone())
276 .build(ctx);
277
278 let popup = PopupBuilder::new(WidgetBuilder::new())
279 .with_content(items_control)
280 .build(ctx);
281
282 let current = if let Some(selected) = self.selected {
283 self.items
284 .get(selected)
285 .map_or(Handle::NONE, |&f| ctx.copy(f))
286 } else {
287 Handle::NONE
288 };
289
290 let arrow = make_arrow(ctx, ArrowDirection::Bottom, 10.0);
291 ctx[arrow].set_column(1);
292
293 let main_grid =
294 GridBuilder::new(WidgetBuilder::new().with_child(current).with_child(arrow))
295 .add_row(Row::stretch())
296 .add_column(Column::stretch())
297 .add_column(Column::strict(20.0))
298 .build(ctx);
299
300 let dropdown_list = UiNode::new(DropdownList {
301 widget: self
302 .widget_builder
303 .with_preview_messages(true)
304 .with_child(
305 BorderBuilder::new(
306 WidgetBuilder::new()
307 .with_foreground(BRUSH_LIGHT)
308 .with_child(main_grid),
309 )
310 .build(ctx),
311 )
312 .build(),
313 popup,
314 items: self.items,
315 list_view: items_control,
316 current,
317 selection: self.selected,
318 close_on_selection: self.close_on_selection,
319 main_grid,
320 });
321
322 ctx.add_node(dropdown_list)
323 }
324}