mathtex-editor-core 0.1.0

Headless core of the mathtex structural math editor: model, operations, navigation, selection, IR matching
Documentation
//! Host facing state for the Backspace dropdown menu.

use crate::host::Rect;
use crate::model::NodeId;
use crate::ops::SwapVariant;

/// One row the host renders.
#[derive(Debug, Clone, PartialEq)]
pub struct MenuItem {
    /// The visible row label.
    pub label: String,
}

/// The data the host needs to draw and drive the open menu.
#[derive(Debug, Clone, PartialEq)]
pub struct MenuView {
    /// The rectangle where the menu is anchored.
    pub anchor: Rect,
    /// The visible menu rows.
    pub items: Vec<MenuItem>,
    /// The selected row index.
    pub selected: usize,
    /// The typed filter query.
    pub query: String,
}

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

impl RowEffect<'_> {
    pub(crate) fn label(&self) -> String {
        match self {
            RowEffect::Delete => "Delete".to_string(),
            RowEffect::Swap(v) => v.label().to_string(),
        }
    }
}

/// 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,
        }
    }

    /// Rows currently shown to the host.
    pub(crate) fn visible(&self) -> Vec<RowEffect<'_>> {
        let mut out = vec![RowEffect::Delete];
        let q = self.query.to_lowercase();
        out.extend(
            self.variants
                .iter()
                .filter(|v| q.is_empty() || v.label().to_lowercase().contains(&q))
                .map(RowEffect::Swap),
        );
        out
    }
}