context_menu.rs

 1use crate::{prelude::*, ListItemVariant};
 2use crate::{v_stack, Label, List, ListEntry, ListItem, ListSeparator, ListSubHeader};
 3
 4pub enum ContextMenuItem {
 5    Header(SharedString),
 6    Entry(Label),
 7    Separator,
 8}
 9
10impl ContextMenuItem {
11    fn to_list_item<V: 'static>(self) -> ListItem<V> {
12        match self {
13            ContextMenuItem::Header(label) => ListSubHeader::new(label).into(),
14            ContextMenuItem::Entry(label) => {
15                ListEntry::new(label).variant(ListItemVariant::Inset).into()
16            }
17            ContextMenuItem::Separator => ListSeparator::new().into(),
18        }
19    }
20
21    pub fn header(label: impl Into<SharedString>) -> Self {
22        Self::Header(label.into())
23    }
24
25    pub fn separator() -> Self {
26        Self::Separator
27    }
28
29    pub fn entry(label: Label) -> Self {
30        Self::Entry(label)
31    }
32}
33
34#[derive(Component)]
35pub struct ContextMenu {
36    items: Vec<ContextMenuItem>,
37}
38
39impl ContextMenu {
40    pub fn new(items: impl IntoIterator<Item = ContextMenuItem>) -> Self {
41        Self {
42            items: items.into_iter().collect(),
43        }
44    }
45    fn render<S: 'static>(mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Component<S> {
46        let theme = theme(cx);
47
48        v_stack()
49            .flex()
50            .bg(theme.elevated_surface)
51            .border()
52            .border_color(theme.border)
53            .child(
54                List::new(
55                    self.items
56                        .drain(..)
57                        .map(ContextMenuItem::to_list_item)
58                        .collect(),
59                )
60                .toggle(ToggleState::Toggled),
61            )
62    }
63}
64
65#[cfg(feature = "stories")]
66pub use stories::*;
67
68#[cfg(feature = "stories")]
69mod stories {
70    use crate::story::Story;
71
72    use super::*;
73
74    #[derive(Component)]
75    pub struct ContextMenuStory;
76
77    impl ContextMenuStory {
78        pub fn new() -> Self {
79            Self
80        }
81
82        fn render<S: 'static>(self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Component<S> {
83            Story::container(cx)
84                .child(Story::title_for::<_, ContextMenu>(cx))
85                .child(Story::label(cx, "Default"))
86                .child(ContextMenu::new([
87                    ContextMenuItem::header("Section header"),
88                    ContextMenuItem::Separator,
89                    ContextMenuItem::entry(Label::new("Some entry")),
90                ]))
91        }
92    }
93}