use std::sync::Arc;
use crate::callback::Callback;
use crate::core::element::{Element, IntoElement};
use crate::style::{BorderStyle, Length, Padding, ScrollbarConfig, Style, StyleSlot};
use crate::widgets::{List, ListItem, Popover, PopoverOffset, PopoverPlacement};
#[derive(Clone)]
pub struct ContextMenu {
trigger: Element,
items: Vec<ListItem>,
open: bool,
on_select: Option<Callback<usize>>,
on_close: Option<Callback<()>>,
placement: PopoverPlacement,
offset: PopoverOffset,
clamp: bool,
auto_flip: bool,
anchor: Option<(u16, u16)>,
width: Length,
height: Length,
border: bool,
border_style: BorderStyle,
padding: Padding,
style: Style,
selection_style: StyleSlot,
unfocused_selection_style: StyleSlot,
item_hover_style: Option<StyleSlot>,
selection_symbol: Option<Arc<str>>,
selection_symbol_style: Option<Style>,
unfocused_selection_symbol_style: Option<Style>,
scrollbar: bool,
scrollbar_config: ScrollbarConfig,
}
impl ContextMenu {
pub fn new(trigger: impl IntoElement) -> Self {
Self {
trigger: trigger.into(),
items: Vec::new(),
open: false,
on_select: None,
on_close: None,
placement: PopoverPlacement::BelowStart,
offset: PopoverOffset::ZERO,
clamp: true,
auto_flip: true,
anchor: None,
width: Length::Px(20),
height: Length::Auto,
border: true,
border_style: BorderStyle::Plain,
padding: Padding::default(),
style: Style::default(),
selection_style: StyleSlot::Inherit,
unfocused_selection_style: StyleSlot::Inherit,
item_hover_style: None,
selection_symbol: Some("> ".into()),
selection_symbol_style: None,
unfocused_selection_symbol_style: None,
scrollbar: false,
scrollbar_config: ScrollbarConfig::default(),
}
}
pub fn items(mut self, items: impl IntoIterator<Item = impl Into<ListItem>>) -> Self {
self.items = items.into_iter().map(Into::into).collect();
self
}
pub fn open(mut self, open: bool) -> Self {
self.open = open;
self
}
pub fn on_select(mut self, cb: Callback<usize>) -> Self {
self.on_select = Some(cb);
self
}
pub fn on_close(mut self, cb: Callback<()>) -> Self {
self.on_close = Some(cb);
self
}
pub fn placement(mut self, placement: PopoverPlacement) -> Self {
self.placement = placement;
self
}
pub fn offset(mut self, offset: impl Into<PopoverOffset>) -> Self {
self.offset = offset.into();
self
}
pub fn clamp(mut self, clamp: bool) -> Self {
self.clamp = clamp;
self
}
pub fn auto_flip(mut self, auto_flip: bool) -> Self {
self.auto_flip = auto_flip;
self
}
pub fn anchor(mut self, anchor: Option<(u16, u16)>) -> Self {
self.anchor = anchor;
self
}
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
}
pub fn border(mut self, border: bool) -> Self {
self.border = border;
self
}
pub fn border_style(mut self, border_style: BorderStyle) -> Self {
self.border_style = border_style;
self
}
pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
self.padding = padding.into();
self
}
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn selection_style(mut self, style: Style) -> Self {
self.selection_style = StyleSlot::Replace(style);
self
}
pub fn extend_selection_style(mut self, style: Style) -> Self {
self.selection_style = StyleSlot::Extend(style);
self
}
pub fn inherit_selection_style(mut self) -> Self {
self.selection_style = StyleSlot::Inherit;
self
}
pub fn selection_style_slot(mut self, slot: StyleSlot) -> Self {
self.selection_style = slot;
self
}
pub fn unfocused_selection_style(mut self, style: Style) -> Self {
self.unfocused_selection_style = StyleSlot::Replace(style);
self
}
pub fn extend_unfocused_selection_style(mut self, style: Style) -> Self {
self.unfocused_selection_style = StyleSlot::Extend(style);
self
}
pub fn inherit_unfocused_selection_style(mut self) -> Self {
self.unfocused_selection_style = StyleSlot::Inherit;
self
}
pub fn unfocused_selection_style_slot(mut self, slot: StyleSlot) -> Self {
self.unfocused_selection_style = slot;
self
}
pub fn item_hover_style(mut self, style: Style) -> Self {
self.item_hover_style = Some(StyleSlot::Replace(style));
self
}
pub fn extend_item_hover_style(mut self, style: Style) -> Self {
self.item_hover_style = Some(StyleSlot::Extend(style));
self
}
pub fn inherit_item_hover_style(mut self) -> Self {
self.item_hover_style = Some(StyleSlot::Inherit);
self
}
pub fn item_hover_style_slot(mut self, slot: StyleSlot) -> Self {
self.item_hover_style = Some(slot);
self
}
pub fn selection_symbol(mut self, symbol: Option<impl Into<Arc<str>>>) -> Self {
self.selection_symbol = symbol.map(Into::into);
self
}
pub fn selection_symbol_style(mut self, style: Style) -> Self {
self.selection_symbol_style = Some(style);
self
}
pub fn unfocused_selection_symbol_style(mut self, style: Style) -> Self {
self.unfocused_selection_symbol_style = Some(style);
self
}
pub fn scrollbar(mut self, scrollbar: bool) -> Self {
self.scrollbar = scrollbar;
self
}
pub fn scrollbar_config(mut self, config: ScrollbarConfig) -> Self {
self.scrollbar_config = config;
self
}
}
impl From<ContextMenu> for Element {
fn from(menu: ContextMenu) -> Self {
let mut list = List::new()
.items(menu.items)
.border(menu.border)
.border_style(menu.border_style)
.padding(menu.padding)
.style(menu.style)
.selection_symbol_style(
menu.selection_symbol_style
.unwrap_or(menu.selection_style.explicit_style().unwrap_or_default()),
)
.unfocused_selection_symbol_style(
menu.unfocused_selection_symbol_style
.or_else(|| menu.unfocused_selection_style.explicit_style())
.unwrap_or(menu.selection_style.explicit_style().unwrap_or_default()),
)
.selection_symbol(menu.selection_symbol)
.scrollbar(menu.scrollbar)
.scrollbar_config(menu.scrollbar_config)
.width(menu.width)
.height(menu.height);
list = list
.selection_style_slot(menu.selection_style)
.unfocused_selection_style_slot(menu.unfocused_selection_style)
.item_hover_style_slot(menu.item_hover_style.unwrap_or(menu.selection_style));
if let Some(cb) = menu.on_select {
list = list.on_select(Callback::new(move |ev: crate::widgets::ListEvent| {
cb.emit(ev.index)
}));
}
Popover::new()
.trigger(menu.trigger)
.content(list)
.open(menu.open)
.placement(menu.placement)
.offset(menu.offset)
.clamp(menu.clamp)
.auto_flip(menu.auto_flip)
.min_trigger_width(false)
.anchor(menu.anchor)
.on_close(menu.on_close.unwrap_or_else(|| Callback::new(|_| {})))
.into()
}
}