use std::rc::Rc;
use gpui_kit::{
App, Entity, Pixels, Point, Window,
base::{
Disableable as _,
input::{
Copy, Cut, InputBaseState, InputContextMenuCapabilities, InputModeKind, Paste,
SelectAll,
},
},
};
use super::{MenuAnchor, MenuEntry, MenuItem, MenuState};
pub(crate) type TextMenuBuilder = Rc<
dyn Fn(Vec<MenuEntry>, InputContextMenuCapabilities, &mut Window, &mut App) -> Vec<MenuEntry>,
>;
pub(crate) fn text_entries(capabilities: InputContextMenuCapabilities, cx: &App) -> Vec<MenuEntry> {
let editable = capabilities.is_editable();
let copyable = capabilities.is_copyable();
let mut entries = Vec::with_capacity(5);
if !capabilities.is_masked() {
entries.push(
MenuItem::new("cut", "Cut")
.action(Cut)
.disabled(!(editable && copyable))
.into(),
);
entries.push(
MenuItem::new("copy", "Copy")
.action(Copy)
.disabled(!copyable)
.into(),
);
}
entries.push(
MenuItem::new("paste", "Paste")
.action(Paste)
.disabled(!(editable && has_text_to_paste(cx)))
.into(),
);
entries.push(MenuEntry::Separator);
entries.push(
MenuItem::new("select-all", "Select All")
.action(SelectAll)
.disabled(capabilities.is_disabled())
.into(),
);
entries
}
fn has_text_to_paste(cx: &App) -> bool {
if cfg!(target_os = "ios") {
return true;
}
cx.read_from_clipboard()
.is_some_and(|item| item.text().is_some())
}
pub(crate) fn open_text_menu<M: InputModeKind>(
menu: &Entity<MenuState>,
input: &Entity<InputBaseState<M>>,
position: Point<Pixels>,
builder: Option<&TextMenuBuilder>,
window: &mut Window,
cx: &mut App,
) {
let (capabilities, focus) = {
let input = input.read(cx);
(
input.context_menu_capabilities(),
input.presentation().focus_handle().clone(),
)
};
let mut entries = text_entries(capabilities, cx);
if let Some(builder) = builder {
entries = builder(entries, capabilities, window, cx);
}
if entries.is_empty() {
return;
}
focus.focus(window, cx);
menu.update(cx, |menu, cx| {
menu.open_for(entries, MenuAnchor::Point(position), &focus, window, cx)
});
}