context_menu.rs

 1use crate::prelude::*;
 2use crate::{
 3    theme, v_stack, Label, List, ListEntry, ListItem, ListItemVariant, ListSeparator, ListSubHeader,
 4};
 5
 6#[derive(Clone)]
 7pub enum ContextMenuItem<S: 'static + Send + Sync + Clone> {
 8    Header(&'static str),
 9    Entry(Label<S>),
10    Separator,
11}
12
13impl<S: 'static + Send + Sync + Clone> ContextMenuItem<S> {
14    fn to_list_item(self) -> ListItem<S> {
15        match self {
16            ContextMenuItem::Header(label) => ListSubHeader::new(label).into(),
17            ContextMenuItem::Entry(label) => {
18                ListEntry::new(label).variant(ListItemVariant::Inset).into()
19            }
20            ContextMenuItem::Separator => ListSeparator::new().into(),
21        }
22    }
23
24    pub fn header(label: &'static str) -> Self {
25        Self::Header(label)
26    }
27
28    pub fn separator() -> Self {
29        Self::Separator
30    }
31
32    pub fn entry(label: Label<S>) -> Self {
33        Self::Entry(label)
34    }
35}
36
37#[derive(Element)]
38pub struct ContextMenu<S: 'static + Send + Sync + Clone> {
39    items: Vec<ContextMenuItem<S>>,
40}
41
42impl<S: 'static + Send + Sync + Clone> ContextMenu<S> {
43    pub fn new(items: impl IntoIterator<Item = ContextMenuItem<S>>) -> Self {
44        Self {
45            items: items.into_iter().collect(),
46        }
47    }
48    fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
49        let theme = theme(cx);
50
51        v_stack()
52            .flex()
53            .fill(theme.lowest.base.default.background)
54            .border()
55            .border_color(theme.lowest.base.default.border)
56            .child(
57                List::new(
58                    self.items
59                        .clone()
60                        .into_iter()
61                        .map(ContextMenuItem::to_list_item)
62                        .collect(),
63                )
64                .set_toggle(ToggleState::Toggled),
65            )
66    }
67}