Skip to main content

mathtex_editor_core/
menu.rs

1//! State of the swap or delete menu that Backspace opens next to a swappable structure.
2
3use crate::model::NodeId;
4use crate::ops::{SwapKind, SwapVariant};
5
6/// What committing a row does.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub enum MenuItemKind {
9    /// Deletes the structure, always the first row so hosts can localize its label.
10    Delete,
11    /// Swaps the structure's delimiters, operator, accent, decoration, font, or environment.
12    Swap,
13}
14
15/// One row the host renders.
16#[derive(Debug, Clone, PartialEq, Eq, Hash)]
17pub struct MenuItem {
18    /// The English row label.
19    pub label: String,
20    /// What the row does.
21    pub kind: MenuItemKind,
22}
23
24/// The rows and state of the open menu, its anchor rectangle comes from `Editor::render`.
25#[derive(Debug, Clone, PartialEq, Eq, Hash)]
26pub struct MenuView {
27    /// The visible rows, filtered by `query`.
28    pub items: Vec<MenuItem>,
29    /// The highlighted row index.
30    pub selected: usize,
31    /// The typed filter.
32    pub query: String,
33}
34
35/// A visible row effect.
36pub(crate) enum RowEffect<'a> {
37    Delete,
38    Swap(&'a SwapKind),
39}
40
41/// Editor private pending menu state.
42pub(crate) struct Menu {
43    pub(crate) anchor: NodeId,
44    variants: Vec<SwapVariant>,
45    pub(crate) query: String,
46    pub(crate) selected: usize,
47}
48
49impl Menu {
50    pub(crate) fn for_node(anchor: NodeId, variants: Vec<SwapVariant>) -> Self {
51        Self { anchor, variants, query: String::new(), selected: 0 }
52    }
53
54    fn visible_variants(&self) -> impl Iterator<Item = &SwapVariant> {
55        let q = self.query.to_lowercase();
56        self.variants.iter().filter(move |v| q.is_empty() || v.label.to_lowercase().contains(&q))
57    }
58
59    /// Row effects in display order, Delete pinned first.
60    pub(crate) fn visible(&self) -> Vec<RowEffect<'_>> {
61        std::iter::once(RowEffect::Delete).chain(self.visible_variants().map(|v| RowEffect::Swap(&v.kind))).collect()
62    }
63
64    pub(crate) fn view(&self) -> MenuView {
65        let delete = MenuItem { label: "Delete".to_string(), kind: MenuItemKind::Delete };
66        let swaps = self.visible_variants().map(|v| MenuItem { label: v.label.clone(), kind: MenuItemKind::Swap });
67        MenuView { items: std::iter::once(delete).chain(swaps).collect(), selected: self.selected, query: self.query.clone() }
68    }
69}