mathtex-editor-core 0.3.0

Headless core of the mathtex structural math editor: model, operations, navigation, selection, IR matching
Documentation
//! State of the swap or delete menu that Backspace opens next to a swappable structure.

use crate::model::NodeId;
use crate::ops::{SwapKind, SwapVariant};

/// What committing a row does.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MenuItemKind {
    /// Deletes the structure, always the first row so hosts can localize its label.
    Delete,
    /// Swaps the structure's delimiters, operator, accent, decoration, font, or environment.
    Swap,
}

/// One row the host renders.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MenuItem {
    /// The English row label.
    pub label: String,
    /// What the row does.
    pub kind: MenuItemKind,
}

/// The rows and state of the open menu, its anchor rectangle comes from `Editor::render`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MenuView {
    /// The visible rows, filtered by `query`.
    pub items: Vec<MenuItem>,
    /// The highlighted row index.
    pub selected: usize,
    /// The typed filter.
    pub query: String,
}

/// A visible row effect.
pub(crate) enum RowEffect<'a> {
    Delete,
    Swap(&'a SwapKind),
}

/// Editor private pending menu state.
pub(crate) struct Menu {
    pub(crate) anchor: NodeId,
    variants: Vec<SwapVariant>,
    pub(crate) query: String,
    pub(crate) selected: usize,
}

impl Menu {
    pub(crate) fn for_node(anchor: NodeId, variants: Vec<SwapVariant>) -> Self {
        Self { anchor, variants, query: String::new(), selected: 0 }
    }

    fn visible_variants(&self) -> impl Iterator<Item = &SwapVariant> {
        let q = self.query.to_lowercase();
        self.variants.iter().filter(move |v| q.is_empty() || v.label.to_lowercase().contains(&q))
    }

    /// Row effects in display order, Delete pinned first.
    pub(crate) fn visible(&self) -> Vec<RowEffect<'_>> {
        std::iter::once(RowEffect::Delete).chain(self.visible_variants().map(|v| RowEffect::Swap(&v.kind))).collect()
    }

    pub(crate) fn view(&self) -> MenuView {
        let delete = MenuItem { label: "Delete".to_string(), kind: MenuItemKind::Delete };
        let swaps = self.visible_variants().map(|v| MenuItem { label: v.label.clone(), kind: MenuItemKind::Swap });
        MenuView { items: std::iter::once(delete).chain(swaps).collect(), selected: self.selected, query: self.query.clone() }
    }
}