chargrid_menu 0.3.1

chargrid menus
Documentation
use crate::{
    MenuEntryRichString, MenuEntryToRender, MenuIndexFromScreenCoord, MenuInstance,
    MenuInstanceChoose, MenuInstanceMouseTracker,
};
use chargrid_event_routine::{event_or_peek_with_handled, EventOrPeek, EventRoutine, Handled};
use chargrid_input::Input;
use chargrid_render::*;
use chargrid_text::StringViewSingleLine;
use std::marker::PhantomData;

pub struct DynamicStyleMenuInstanceView {
    mouse_tracker: MenuInstanceMouseTracker,
    buf: String,
}

impl DynamicStyleMenuInstanceView {
    pub fn new() -> Self {
        Self {
            buf: String::new(),
            mouse_tracker: Default::default(),
        }
    }
}

impl Default for DynamicStyleMenuInstanceView {
    fn default() -> Self {
        Self::new()
    }
}

pub struct DynamicStyleMenuInstanceModel<'a, E, S>
where
    E: Clone,
    S: MenuEntryRichString<Entry = E>,
{
    menu_instance: &'a MenuInstance<E>,
    menu_entry_rich_string: &'a S,
}

impl<'a, E, S> View<DynamicStyleMenuInstanceModel<'a, E, S>> for DynamicStyleMenuInstanceView
where
    E: Clone,
    S: MenuEntryRichString<Entry = E>,
{
    fn view<F: Frame, C: ColModify>(
        &mut self,
        DynamicStyleMenuInstanceModel {
            menu_instance,
            menu_entry_rich_string,
        }: DynamicStyleMenuInstanceModel<'a, E, S>,
        context: ViewContext<C>,
        frame: &mut F,
    ) {
        self.mouse_tracker.new_frame(context.offset);
        for (i, entry, maybe_selected) in menu_instance.enumerate() {
            self.buf.clear();
            let entry_to_render = MenuEntryToRender {
                index: i,
                entry,
                selected: maybe_selected.is_some(),
            };
            let style = menu_entry_rich_string.render_rich_string(entry_to_render, &mut self.buf);
            let mut view = StringViewSingleLine::new(style);
            let size = view.view_size(
                &self.buf,
                context.add_offset(Coord::new(0, i as i32)),
                frame,
            );
            self.mouse_tracker.on_entry_view_size(size);
        }
    }
}

impl MenuIndexFromScreenCoord for DynamicStyleMenuInstanceView {
    fn menu_index_from_screen_coord(&self, len: usize, coord: Coord) -> Option<usize> {
        self.mouse_tracker.menu_index_from_screen_coord(len, coord)
    }
}

pub struct DynamicStyleMenuInstanceRoutine<C, S> {
    choose: PhantomData<C>,
    menu_entry_rich_string: S,
}
impl<C, S> DynamicStyleMenuInstanceRoutine<C, S>
where
    C: MenuInstanceChoose,
    S: MenuEntryRichString<Entry = C::Entry>,
{
    pub fn new(menu_entry_rich_string: S) -> Self {
        Self {
            choose: PhantomData,
            menu_entry_rich_string,
        }
    }
}
impl<C, S> Clone for DynamicStyleMenuInstanceRoutine<C, S>
where
    C: MenuInstanceChoose,
    S: MenuEntryRichString<Entry = C::Entry> + Clone,
{
    fn clone(&self) -> Self {
        Self {
            choose: PhantomData,
            menu_entry_rich_string: self.menu_entry_rich_string.clone(),
        }
    }
}

impl<C, S> EventRoutine for DynamicStyleMenuInstanceRoutine<C, S>
where
    C: MenuInstanceChoose,
    S: MenuEntryRichString<Entry = C::Entry>,
{
    type Return = C::Output;
    type Data = C;
    type View = DynamicStyleMenuInstanceView;
    type Event = Input;

    fn handle<EP>(
        self,
        data: &mut Self::Data,
        view: &Self::View,
        event_or_peek: EP,
    ) -> Handled<Self::Return, Self>
    where
        EP: EventOrPeek<Event = Self::Event>,
    {
        event_or_peek_with_handled(event_or_peek, self, |s, event| {
            if let Some(menu_output) = data.choose(view, event) {
                Handled::Return(menu_output)
            } else {
                Handled::Continue(s)
            }
        })
    }

    fn view<F, CM>(
        &self,
        data: &Self::Data,
        view: &mut Self::View,
        context: ViewContext<CM>,
        frame: &mut F,
    ) where
        F: Frame,
        CM: ColModify,
    {
        let model = DynamicStyleMenuInstanceModel {
            menu_instance: data.menu_instance(),
            menu_entry_rich_string: &self.menu_entry_rich_string,
        };
        view.view(model, context, frame);
    }
}