termrs_core/widgets/
wrapper.rs

1use std::{io::Error, marker::PhantomData};
2
3use crate::{
4    input::{Event, EventStatus},
5    render::{Margin, Position, RenderContext, Size},
6    widget::{EventContext, LayoutInfo, RenderInfo, Widget},
7};
8
9/// Adds to a [`Widget`] padding and border.
10pub struct Wrapper<Message, Content, L, R>
11where
12    Content: Widget<Message, LayoutInfo = L, RenderInfo = R>,
13    L: LayoutInfo,
14    R: RenderInfo,
15{
16    content: Content,
17    phantom_message: PhantomData<Message>,
18
19    padding: Margin,
20}
21
22pub struct WrapperLayout<L: LayoutInfo> {
23    padding: Margin,
24    child_layout: L,
25}
26
27impl<L: LayoutInfo> LayoutInfo for WrapperLayout<L> {
28    fn bounds(&self) -> Size {
29        self.child_layout.bounds().add_margin(&self.padding)
30    }
31}
32
33impl<Message, Content, L, R> Wrapper<Message, Content, L, R>
34where
35    Content: Widget<Message, LayoutInfo = L, RenderInfo = R>,
36    L: LayoutInfo,
37    R: RenderInfo,
38{
39    pub fn new(content: Content) -> Self {
40        Self {
41            content,
42            phantom_message: PhantomData,
43            padding: Margin::default(),
44        }
45    }
46
47    pub fn with_padding(mut self, padding: Margin) -> Self {
48        self.padding = padding;
49        self
50    }
51}
52
53impl<Message, Content, L, R> Widget<Message> for Wrapper<Message, Content, L, R>
54where
55    Content: Widget<Message, LayoutInfo = L, RenderInfo = R>,
56    L: LayoutInfo,
57    R: RenderInfo,
58{
59    type LayoutInfo = WrapperLayout<L>;
60    type RenderInfo = R;
61
62    fn render(
63        &self,
64        position: Position,
65        layout: &Self::LayoutInfo,
66        context: &mut dyn RenderContext,
67    ) -> std::io::Result<Self::RenderInfo> {
68        let position = position.offset_u16(self.padding.left(), self.padding.top());
69
70        if let Some(position) = position {
71            self.content.render(position, &layout.child_layout, context)
72        } else {
73            Err(Error::other("Invalid position"))
74        }
75    }
76
77    fn layout(&self, available_size: Size) -> Self::LayoutInfo {
78        let available_size = available_size.sub_margin(&self.padding);
79
80        let child_layout = self.content.layout(available_size);
81
82        WrapperLayout {
83            child_layout,
84            padding: self.padding.clone(),
85        }
86    }
87
88    fn on_event(
89        &mut self,
90        event: &Event,
91        render_info: &Self::RenderInfo,
92        event_context: &mut dyn EventContext<Message>,
93    ) -> EventStatus {
94        // Wrapper is not a container, so it shares keyboard
95        // and mouse focus with it's content widget.
96
97        self.content.on_event(event, render_info, event_context)
98    }
99}