use crate::view::{root::Cydonia, sidebar::Row};
use bezel::{
gpui::{self, AnyElement, Context, Div, SharedString, Stateful, Window, prelude::*, px},
theme::Theme,
ui::{
icons::Icon,
menu::{self, Hit, Item},
widgets::Buttons,
},
};
type Act = Box<dyn Fn(&mut Cydonia, &[usize], &mut Window, &mut Context<Cydonia>)>;
#[derive(Clone, PartialEq, Eq)]
pub(crate) enum Menu {
Add(usize),
Project(usize),
Entry(Row),
Header,
Column(usize),
Lane(String),
Turn,
Card(String),
}
pub(crate) fn row(
item: Item,
act: impl Fn(&mut Cydonia, &mut Window, &mut Context<Cydonia>) + 'static,
) -> (Item, Act) {
(
item,
Box::new(move |this, _, window, cx| act(this, window, cx)),
)
}
pub(crate) fn submenu(
label: impl Into<SharedString>,
icon: impl Into<Icon>,
rows: Vec<(Item, Act)>,
) -> (Item, Act) {
let (items, acts): (Vec<Item>, Vec<Act>) = rows.into_iter().unzip();
let item = Item::submenu(label, items).with_icon(icon);
let act: Act = Box::new(move |this, path, window, cx| {
if let Some((&at, rest)) = path.split_first()
&& let Some(act) = acts.get(at)
{
act(this, rest, window, cx);
}
});
(item, act)
}
impl Cydonia {
pub(crate) fn toggle_menu(&mut self, menu: Menu, cx: &mut Context<Self>) {
let closed_by_this_press = std::mem::take(&mut self.menu_pressed);
let shut = closed_by_this_press || self.menu.as_ref() == Some(&menu);
self.menu = (!shut).then_some(menu);
self.menu_cursor.clear();
cx.notify();
}
fn shut_menu(&mut self) {
self.menu = None;
self.menu_cursor.clear();
}
pub(crate) fn menu_press(
&self,
el: Stateful<Div>,
menu: Menu,
cx: &Context<Self>,
) -> Stateful<Div> {
el.capture_any_mouse_down(cx.listener(move |this, _, _, _| {
this.menu_pressed = this.menu.as_ref() == Some(&menu);
}))
}
pub(crate) fn menu_button(
&self,
id: impl Into<gpui::ElementId>,
group: Option<&'static str>,
mark: impl IntoElement,
menu: Menu,
cx: &Context<Self>,
) -> Stateful<Div> {
let button = Theme::of(cx)
.ghost(id)
.flex_none()
.relative()
.when_some(
group.filter(|_| self.menu.as_ref() != Some(&menu)),
|el, group| {
if matches!(menu, Menu::Add(_) | Menu::Entry(_)) {
el.when(self.sidebar_hovered.as_ref() != Some(&menu), |el| {
el.hidden()
})
} else {
el.invisible().group_hover(group, |el| el.visible())
}
},
)
.p(px(3.))
.child(mark)
.on_click({
let menu = menu.clone();
cx.listener(move |this, _, _, cx| {
cx.stop_propagation();
this.toggle_menu(menu.clone(), cx);
})
});
self.menu_press(button, menu, cx)
}
pub(crate) fn menu_card(
&self,
id: impl Into<SharedString>,
rows: Vec<(Item, Act)>,
cx: &mut Context<Self>,
) -> AnyElement {
let theme = Theme::of(cx).clone();
let (items, acts): (Vec<Item>, Vec<Act>) = rows.into_iter().unzip();
let paths = items.clone();
menu::card(
&theme,
id,
&items,
&self.menu_cursor,
cx,
move |this, hit, window, cx| match hit {
Hit::Point(path) => {
if this.menu_cursor.point_at(&paths, &path) {
cx.notify();
}
}
Hit::Choose(path) => {
let Some((&row, rest)) = path.split_first() else {
return;
};
this.shut_menu();
if let Some(act) = acts.get(row) {
act(this, rest, window, cx);
}
cx.notify();
}
Hit::Dismiss => {
this.shut_menu();
cx.notify();
}
},
)
.into_any_element()
}
}