termrs_core 0.3.0

The core library of termrs
Documentation
use crate::{
    input::{Event, EventStatus, MouseEventKind},
    render::{Position, RenderContext, Size},
    widget::{EventContext, LayoutInfo, RenderInfo, Widget},
};

/// Produces message on click.
pub struct Button<Message, Content, L, R>
where
    Content: Widget<Message, LayoutInfo = L, RenderInfo = R>,
    Message: Clone,
    L: LayoutInfo,
    R: RenderInfo,
{
    content: Content,
    on_click: Option<Message>,

    /// Value indicating whether the user
    /// pressed the button and didn't release it yet.
    is_pressing: bool,
}

impl<Message, Content, L, R> Button<Message, Content, L, R>
where
    Content: Widget<Message, LayoutInfo = L, RenderInfo = R>,
    Message: Clone,
    L: LayoutInfo,
    R: RenderInfo,
{
    pub fn new(content: Content) -> Self {
        Self {
            content,
            on_click: None,
            is_pressing: false,
        }
    }

    pub fn with_content(mut self, content: Content) -> Self {
        self.content = content;
        self
    }

    pub fn with_on_press(mut self, on_click: Option<Message>) -> Self {
        self.on_click = on_click;
        self
    }
}

impl<Message, Content, L, R> Widget<Message> for Button<Message, Content, L, R>
where
    Content: Widget<Message, LayoutInfo = L, RenderInfo = R>,
    Message: Clone,
    L: LayoutInfo,
    R: RenderInfo,
{
    type LayoutInfo = L;
    type RenderInfo = R;

    fn render(
        &self,
        position: Position,
        layout_info: &Self::LayoutInfo,
        context: &mut dyn RenderContext,
    ) -> std::io::Result<Self::RenderInfo> {
        self.content.render(position, layout_info, context)
    }

    fn layout(&self, available_size: Size) -> Self::LayoutInfo {
        self.content.layout(available_size)
    }

    fn on_event(
        &mut self,
        event: &Event,
        render_info: &Self::RenderInfo,
        event_context: &mut dyn EventContext<Message>,
    ) -> EventStatus {
        // if it does make sense to handle pressing.
        if let Some(on_click) = self.on_click.clone() {
            if let Event::Mouse(ref mouse_event) = event {
                match mouse_event.kind {
                    MouseEventKind::Down(_) => {
                        let render_position = render_info.position();
                        let relative_position = mouse_event
                            .position
                            .offset_u16_negative(render_position.column, render_position.row);

                        if let Some(relative_position) = relative_position {
                            if render_info.bounds().contains(relative_position) {
                                self.is_pressing = true;
                                event_context.set_mouse_focus(true);

                                return EventStatus::Handled;
                            }
                        }
                    }
                    MouseEventKind::Up(_) => {
                        if self.is_pressing {
                            self.is_pressing = false;

                            event_context.set_mouse_focus(false);

                            event_context.emit_message(on_click);

                            return EventStatus::Handled;
                        }
                    }
                    _ => (),
                }
            }
        }

        self.content.on_event(event, render_info, event_context)
    }
}