termrs_core 0.3.0

The core library of termrs
Documentation
use std::{io::Error, marker::PhantomData};

use crate::{
    input::{Event, EventStatus},
    render::{Margin, Position, RenderContext, Size},
    widget::{EventContext, LayoutInfo, RenderInfo, Widget},
};

/// Adds to a [`Widget`] padding and border.
pub struct Wrapper<Message, Content, L, R>
where
    Content: Widget<Message, LayoutInfo = L, RenderInfo = R>,
    L: LayoutInfo,
    R: RenderInfo,
{
    content: Content,
    phantom_message: PhantomData<Message>,

    padding: Margin,
}

pub struct WrapperLayout<L: LayoutInfo> {
    padding: Margin,
    child_layout: L,
}

impl<L: LayoutInfo> LayoutInfo for WrapperLayout<L> {
    fn bounds(&self) -> Size {
        self.child_layout.bounds().add_margin(&self.padding)
    }
}

impl<Message, Content, L, R> Wrapper<Message, Content, L, R>
where
    Content: Widget<Message, LayoutInfo = L, RenderInfo = R>,
    L: LayoutInfo,
    R: RenderInfo,
{
    pub fn new(content: Content) -> Self {
        Self {
            content,
            phantom_message: PhantomData,
            padding: Margin::default(),
        }
    }

    pub fn with_padding(mut self, padding: Margin) -> Self {
        self.padding = padding;
        self
    }
}

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

    fn render(
        &self,
        position: Position,
        layout: &Self::LayoutInfo,
        context: &mut dyn RenderContext,
    ) -> std::io::Result<Self::RenderInfo> {
        let position = position.offset_u16(self.padding.left(), self.padding.top());

        if let Some(position) = position {
            self.content.render(position, &layout.child_layout, context)
        } else {
            Err(Error::other("Invalid position"))
        }
    }

    fn layout(&self, available_size: Size) -> Self::LayoutInfo {
        let available_size = available_size.sub_margin(&self.padding);

        let child_layout = self.content.layout(available_size);

        WrapperLayout {
            child_layout,
            padding: self.padding.clone(),
        }
    }

    fn on_event(
        &mut self,
        event: &Event,
        render_info: &Self::RenderInfo,
        event_context: &mut dyn EventContext<Message>,
    ) -> EventStatus {
        // Wrapper is not a container, so it shares keyboard
        // and mouse focus with it's content widget.

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