select.rs

  1use crate::{
  2    elements::*, platform::MouseButton, AppContext, Entity, View, ViewContext, WeakViewHandle,
  3};
  4
  5pub struct Select {
  6    handle: WeakViewHandle<Self>,
  7    render_item: Box<dyn Fn(usize, ItemType, bool, &AppContext) -> AnyElement<Self>>,
  8    selected_item_ix: usize,
  9    item_count: usize,
 10    is_open: bool,
 11    list_state: UniformListState,
 12    build_style: Option<Box<dyn FnMut(&mut AppContext) -> SelectStyle>>,
 13}
 14
 15#[derive(Clone, Default)]
 16pub struct SelectStyle {
 17    pub header: ContainerStyle,
 18    pub menu: ContainerStyle,
 19}
 20
 21pub enum ItemType {
 22    Header,
 23    Selected,
 24    Unselected,
 25}
 26
 27pub enum Event {}
 28
 29impl Select {
 30    pub fn new<F: 'static + Fn(usize, ItemType, bool, &AppContext) -> AnyElement<Self>>(
 31        item_count: usize,
 32        cx: &mut ViewContext<Self>,
 33        render_item: F,
 34    ) -> Self {
 35        Self {
 36            handle: cx.weak_handle(),
 37            render_item: Box::new(render_item),
 38            selected_item_ix: 0,
 39            item_count,
 40            is_open: false,
 41            list_state: UniformListState::default(),
 42            build_style: Default::default(),
 43        }
 44    }
 45
 46    pub fn with_style(mut self, f: impl 'static + FnMut(&mut AppContext) -> SelectStyle) -> Self {
 47        self.build_style = Some(Box::new(f));
 48        self
 49    }
 50
 51    pub fn set_item_count(&mut self, count: usize, cx: &mut ViewContext<Self>) {
 52        self.item_count = count;
 53        cx.notify();
 54    }
 55
 56    fn toggle(&mut self, cx: &mut ViewContext<Self>) {
 57        self.is_open = !self.is_open;
 58        cx.notify();
 59    }
 60
 61    pub fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Self>) {
 62        self.selected_item_ix = ix;
 63        self.is_open = false;
 64        cx.notify();
 65    }
 66
 67    pub fn selected_index(&self) -> usize {
 68        self.selected_item_ix
 69    }
 70}
 71
 72impl Entity for Select {
 73    type Event = Event;
 74}
 75
 76impl View for Select {
 77    fn ui_name() -> &'static str {
 78        "Select"
 79    }
 80
 81    fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
 82        if self.item_count == 0 {
 83            return Empty::new().into_any();
 84        }
 85
 86        enum Header {}
 87        enum Item {}
 88
 89        let style = if let Some(build_style) = self.build_style.as_mut() {
 90            (build_style)(cx)
 91        } else {
 92            Default::default()
 93        };
 94        let mut result = Flex::column().with_child(
 95            MouseEventHandler::new::<Header, _>(self.handle.id(), cx, |mouse_state, cx| {
 96                (self.render_item)(
 97                    self.selected_item_ix,
 98                    ItemType::Header,
 99                    mouse_state.hovered(),
100                    cx,
101                )
102                .contained()
103                .with_style(style.header)
104            })
105            .on_click(MouseButton::Left, move |_, this, cx| {
106                this.toggle(cx);
107            }),
108        );
109        if self.is_open {
110            result.add_child(Overlay::new(
111                UniformList::new(
112                    self.list_state.clone(),
113                    self.item_count,
114                    cx,
115                    move |this, mut range, items, cx| {
116                        let selected_item_ix = this.selected_item_ix;
117                        range.end = range.end.min(this.item_count);
118                        items.extend(range.map(|ix| {
119                            MouseEventHandler::new::<Item, _>(ix, cx, |mouse_state, cx| {
120                                (this.render_item)(
121                                    ix,
122                                    if ix == selected_item_ix {
123                                        ItemType::Selected
124                                    } else {
125                                        ItemType::Unselected
126                                    },
127                                    mouse_state.hovered(),
128                                    cx,
129                                )
130                            })
131                            .on_click(MouseButton::Left, move |_, this, cx| {
132                                this.set_selected_index(ix, cx);
133                            })
134                            .into_any()
135                        }))
136                    },
137                )
138                .constrained()
139                .with_max_height(200.)
140                .contained()
141                .with_style(style.menu),
142            ));
143        }
144        result.into_any()
145    }
146}