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}