1use {
2 std::rc::Rc,
3 std::cell::RefCell,
4 crate::{
5 makepad_derive_widget::*,
6 popup_menu::{PopupMenu, PopupMenuAction},
7 makepad_draw::*,
8 widget::*,
9 }
10};
11
12live_design!{
13 DrawLabelText = {{DrawLabelText}} {}
14 DropDownBase = {{DropDown}} {}
15}
16
17#[derive(Live)]
18pub struct DropDown {
19 #[animator] animator: Animator,
20
21 #[live] draw_bg: DrawQuad,
22 #[live] draw_text: DrawLabelText,
23
24 #[walk] walk: Walk,
25
26 #[live] bind: String,
27 #[live] bind_enum: String,
28
29 #[live] popup_menu: Option<LivePtr>,
30
31 #[live] labels: Vec<String>,
32 #[live] values: Vec<LiveValue>,
33
34 #[live] popup_shift: DVec2,
35
36 #[rust] is_open: bool,
37
38 #[live] selected_item: usize,
39
40 #[layout] layout: Layout,
41}
42
43#[derive(Default, Clone)]
44struct PopupMenuGlobal {
45 map: Rc<RefCell<ComponentMap<LivePtr, PopupMenu >> >
46}
47
48#[derive(Live, LiveHook)]#[repr(C)]
49struct DrawLabelText {
50 #[deref] draw_super: DrawText,
51 #[live] focus: f32,
52 #[live] hover: f32,
53 #[live] pressed: f32,
54}
55
56impl LiveHook for DropDown {
57 fn before_live_design(cx: &mut Cx) {
58 register_widget!(cx, DropDown)
59 }
60
61 fn after_apply(&mut self, cx: &mut Cx, from: ApplyFrom, _index: usize, _nodes: &[LiveNode]) {
62 if self.popup_menu.is_none() || !from.is_from_doc() {
63 return
64 }
65 let global = cx.global::<PopupMenuGlobal>().clone();
66 let mut map = global.map.borrow_mut();
67
68 map.retain( | k, _ | cx.live_registry.borrow().generation_valid(*k));
70
71 let list_box = self.popup_menu.unwrap();
72 map.get_or_insert(cx, list_box, | cx | {
73 PopupMenu::new_from_ptr(cx, Some(list_box))
74 });
75
76 }
77}
78#[derive(Clone, WidgetAction)]
79pub enum DropDownAction {
80 Select(usize, LiveValue),
81 None
82}
83
84
85impl DropDown {
86
87 pub fn set_open(&mut self, cx: &mut Cx) {
88 self.is_open = true;
89 self.draw_bg.redraw(cx);
90 let global = cx.global::<PopupMenuGlobal>().clone();
91 let mut map = global.map.borrow_mut();
92 let lb = map.get_mut(&self.popup_menu.unwrap()).unwrap();
93 let node_id = LiveId(self.selected_item as u64).into();
94 lb.init_select_item(node_id);
95 cx.sweep_lock(self.draw_bg.area());
96 }
97
98 pub fn set_closed(&mut self, cx: &mut Cx) {
99 self.is_open = false;
100 self.draw_bg.redraw(cx);
101 cx.sweep_unlock(self.draw_bg.area());
102 }
103
104 pub fn handle_event_with(&mut self, cx: &mut Cx, event: &Event, dispatch_action: &mut dyn FnMut(&mut Cx, DropDownAction)) {
105 self.animator_handle_event(cx, event);
106
107 if self.is_open && self.popup_menu.is_some() {
108 let global = cx.global::<PopupMenuGlobal>().clone();
110 let mut map = global.map.borrow_mut();
111 let menu = map.get_mut(&self.popup_menu.unwrap()).unwrap();
112 let mut close = false;
113 menu.handle_event_with(cx, event, self.draw_bg.area(), &mut | cx, action | {
114 match action {
115 PopupMenuAction::WasSweeped(_node_id) => {
116 }
118 PopupMenuAction::WasSelected(node_id) => {
119 self.selected_item = node_id.0.0 as usize;
121 dispatch_action(cx, DropDownAction::Select(self.selected_item, self.values.get(self.selected_item).cloned().unwrap_or(LiveValue::None)));
122 self.draw_bg.redraw(cx);
123 close = true;
124 }
125 _ => ()
126 }
127 });
128 if close {
129 self.set_closed(cx);
130 }
131
132 if let Event::MouseDown(e) = event {
134 if !menu.menu_contains_pos(cx, e.abs) {
135 self.set_closed(cx);
136 self.animator_play(cx, id!(hover.off));
137 }
138 }
139 }
140
141 match event.hits_with_sweep_area(cx, self.draw_bg.area(), self.draw_bg.area()) {
142 Hit::KeyFocusLost(_) => {
143 self.animator_play(cx, id!(focus.off));
144 self.set_closed(cx);
145 self.animator_play(cx, id!(hover.off));
146 self.draw_bg.redraw(cx);
147 }
148 Hit::KeyFocus(_) => {
149 self.animator_play(cx, id!(focus.on));
150 }
151 Hit::KeyDown(ke) => match ke.key_code {
152 KeyCode::ArrowUp => {
153 if self.selected_item > 0 {
154 self.selected_item -= 1;
155 dispatch_action(cx, DropDownAction::Select(self.selected_item, self.values.get(self.selected_item).cloned().unwrap_or(LiveValue::None)));
156 self.set_closed(cx);
157 self.draw_bg.redraw(cx);
158 }
159 }
160 KeyCode::ArrowDown => {
161 if self.values.len() > 0 && self.selected_item < self.values.len() - 1 {
162 self.selected_item += 1;
163 dispatch_action(cx, DropDownAction::Select(self.selected_item, self.values.get(self.selected_item).cloned().unwrap_or(LiveValue::None)));
164 self.set_closed(cx);
165 self.draw_bg.redraw(cx);
166 }
167 },
168 _ => ()
169 }
170 Hit::FingerDown(_fe) => {
171 cx.set_key_focus(self.draw_bg.area());
172 self.set_open(cx);
173 self.animator_play(cx, id!(hover.pressed));
174 },
175 Hit::FingerHoverIn(_) => {
176 cx.set_cursor(MouseCursor::Hand);
177 self.animator_play(cx, id!(hover.on));
178 }
179 Hit::FingerHoverOut(_) => {
180 self.animator_play(cx, id!(hover.off));
181 }
182 Hit::FingerUp(fe) => {
183 if fe.is_over {
184 if fe.device.has_hovers() {
185 self.animator_play(cx, id!(hover.on));
186 }
187 }
188 else {
189 self.animator_play(cx, id!(hover.off));
190 }
191 }
192 _ => ()
193 };
194 }
195
196 pub fn draw_text(&mut self, cx: &mut Cx2d, label: &str) {
197 self.draw_bg.begin(cx, self.walk, self.layout);
198 self.draw_text.draw_walk(cx, Walk::fit(), Align::default(), label);
199 self.draw_bg.end(cx);
200 }
201
202 pub fn draw_walk(&mut self, cx: &mut Cx2d, walk: Walk) {
203 self.draw_bg.begin(cx, walk, self.layout);
206 if let Some(val) = self.labels.get(self.selected_item) {
209 self.draw_text.draw_walk(cx, Walk::fit(), Align::default(), val);
210 }
211 else {
212 self.draw_text.draw_walk(cx, Walk::fit(), Align::default(), " ");
213 }
214 self.draw_bg.end(cx);
215
216 cx.add_nav_stop(self.draw_bg.area(), NavRole::DropDown, Margin::default());
217
218 if self.is_open && self.popup_menu.is_some() {
219 let global = cx.global::<PopupMenuGlobal>().clone();
223 let mut map = global.map.borrow_mut();
224 let popup_menu = map.get_mut(&self.popup_menu.unwrap()).unwrap();
225 let mut item_pos = None;
226
227 popup_menu.begin(cx);
230
231 for (i, item) in self.labels.iter().enumerate() {
232 let node_id = LiveId(i as u64).into();
233 if i == self.selected_item {
234 item_pos = Some(cx.turtle().pos());
235 }
236 popup_menu.draw_item(cx, node_id, &item);
237 }
238
239 popup_menu.end(cx, self.draw_bg.area(), -item_pos.unwrap_or(dvec2(0.0, 0.0)));
241 }
242 }
243}
244
245impl Widget for DropDown {
246
247 fn widget_to_data(&self, _cx: &mut Cx, actions: &WidgetActions, nodes: &mut LiveNodeVec, path: &[LiveId]) -> bool {
248 match actions.single_action(self.widget_uid()) {
249 DropDownAction::Select(_, value) => {
250 nodes.write_field_value(path, value.clone());
251 true
252 }
253 _ => false
254 }
255 }
256
257 fn data_to_widget(&mut self, cx: &mut Cx, nodes: &[LiveNode], path: &[LiveId]) {
258 if let Some(value) = nodes.read_field_value(path) {
259 if let Some(index) = self.values.iter().position( | v | v == value) {
260 if self.selected_item != index {
261 self.selected_item = index;
262 self.redraw(cx);
263 }
264 }
265 else {
266 error!("Value not in values list {:?}", value);
267 }
268 }
269 }
270
271 fn redraw(&mut self, cx: &mut Cx) {
272 self.draw_bg.redraw(cx);
273 }
274
275 fn handle_widget_event_with(&mut self, cx: &mut Cx, event: &Event, dispatch_action: &mut dyn FnMut(&mut Cx, WidgetActionItem)) {
276 let uid = self.widget_uid();
277 self.handle_event_with(cx, event, &mut | cx, action | {
278 dispatch_action(cx, WidgetActionItem::new(action.into(), uid))
279 });
280 }
281
282 fn walk(&mut self, _cx:&mut Cx) -> Walk {self.walk}
283
284 fn draw_walk_widget(&mut self, cx: &mut Cx2d, walk: Walk) -> WidgetDraw {
285 self.draw_walk(cx, walk);
286 WidgetDraw::done()
287 }
288}
289
290#[derive(Clone, PartialEq, WidgetRef)]
291pub struct DropDownRef(WidgetRef);
292
293impl DropDownRef {
294 pub fn set_labels(&self, labels: Vec<String>) {
295 if let Some(mut inner) = self.borrow_mut() {
296 inner.labels = labels
297 }
298 }
299
300 pub fn set_labels_and_redraw(&self, cx: &mut Cx, labels: Vec<String>) {
301 if let Some(mut inner) = self.borrow_mut() {
302 inner.labels = labels;
303 inner.draw_bg.redraw(cx);
304 }
305 }
306
307 pub fn selected(&self, actions: &WidgetActions) -> Option<usize> {
308 if let Some(item) = actions.find_single_action(self.widget_uid()) {
309 if let DropDownAction::Select(id, _) = item.action() {
310 return Some(id)
311 }
312 }
313 None
314 }
315
316 pub fn set_selected_item(&self, item: usize) {
317 if let Some(mut inner) = self.borrow_mut() {
318 inner.selected_item = item.min(inner.labels.len().max(1) - 1)
319 }
320 }
321
322 pub fn set_selected_item_and_redraw(&self, cx: &mut Cx, item: usize) {
323 if let Some(mut inner) = self.borrow_mut() {
324 let new_selected = item.min(inner.labels.len().max(1) - 1);
325 if new_selected != inner.selected_item{
326 inner.selected_item = new_selected;
327 inner.draw_bg.redraw(cx);
328 }
329 }
330 }
331 pub fn selected_item(&self) -> usize {
332 if let Some(inner) = self.borrow() {
333 return inner.selected_item
334 }
335 0
336 }
337
338 pub fn selected_label(&self) -> String {
339 if let Some(inner) = self.borrow() {
340 return inner.labels[inner.selected_item].clone()
341 }
342 "".to_string()
343 }
344
345 pub fn set_selected_by_label(&self, label: &str) {
346 if let Some(mut inner) = self.borrow_mut() {
347 if let Some(index) = inner.labels.iter().position( | v | v == label) {
348 inner.selected_item = index
349 }
350 }
351 }
352
353 pub fn set_selected_by_label_and_redraw(&self, label: &str, cx: &mut Cx) {
354 if let Some(mut inner) = self.borrow_mut() {
355 if let Some(index) = inner.labels.iter().position( | v | v == label) {
356 if inner.selected_item != index{
357 inner.selected_item = index;
358 inner.draw_bg.redraw(cx);
359 }
360 }
361 }
362 }
363
364}