ratatui_kit/components/
border.rs

1//! Border 组件:为内容添加可定制的边框、标题、内边距等。
2//!
3//! 常用于包裹内容、分组、突出显示等场景。
4//!
5//! ## 用法示例
6//! ```rust
7//! element!(Border(
8//!     border_style: Style::default().blue(),
9//!     top_title: Some(Line::from("标题")),
10//!     padding: Padding::new(1, 1, 0, 0),
11//! ){
12//!     ChildComponent()
13//! })
14//! ```
15//! 支持自定义边框样式、边框字符集、上下标题、内边距等属性。
16
17use ratatui::{
18    symbols::border,
19    text::Line,
20    widgets::{Block, Padding, Widget},
21};
22use ratatui_kit_macros::{Props, with_layout_style};
23
24use crate::{AnyElement, Component};
25
26#[with_layout_style]
27#[derive(Props)]
28/// Border 组件属性。
29pub struct BorderProps<'a> {
30    /// 内边距。
31    pub padding: Padding,
32    /// 边框样式。
33    pub border_style: ratatui::style::Style,
34    /// 显示哪些边。
35    pub borders: ratatui::widgets::Borders,
36    /// 边框字符集。
37    pub border_set: border::Set,
38    /// 整体样式。
39    pub style: ratatui::style::Style,
40    /// 子元素列表。
41    pub children: Vec<AnyElement<'a>>,
42    /// 顶部标题。
43    pub top_title: Option<Line<'static>>,
44    /// 底部标题。
45    pub bottom_title: Option<Line<'static>>,
46}
47
48impl Default for BorderProps<'_> {
49    fn default() -> Self {
50        Self {
51            padding: Padding::default(),
52            border_style: ratatui::style::Style::default(),
53            borders: ratatui::widgets::Borders::ALL,
54            children: Vec::new(),
55            border_set: border::Set::default(),
56            style: ratatui::style::Style::default(),
57            top_title: None,
58            bottom_title: None,
59            margin: Default::default(),
60            offset: Default::default(),
61            width: Default::default(),
62            height: Default::default(),
63            gap: Default::default(),
64            flex_direction: Default::default(),
65            justify_content: Default::default(),
66        }
67    }
68}
69
70/// Border 组件实现。
71pub struct Border {
72    pub padding: Padding,
73    pub border_style: ratatui::style::Style,
74    pub borders: ratatui::widgets::Borders,
75    pub border_set: border::Set,
76    pub style: ratatui::style::Style,
77    pub top_title: Option<Line<'static>>,
78    pub bottom_title: Option<Line<'static>>,
79}
80
81impl Component for Border {
82    type Props<'a> = BorderProps<'a>;
83
84    /// 根据属性创建 Border 组件实例
85    fn new(props: &Self::Props<'_>) -> Self {
86        Self {
87            padding: props.padding,
88            border_style: props.border_style,
89            borders: props.borders,
90            border_set: props.border_set,
91            style: props.style,
92            top_title: props.top_title.clone(),
93            bottom_title: props.bottom_title.clone(),
94        }
95    }
96
97    /// 根据最新属性和子组件更新自身状态
98    fn update(
99        &mut self,
100        props: &mut Self::Props<'_>,
101        _hooks: crate::Hooks,
102        updater: &mut crate::ComponentUpdater,
103    ) {
104        // 获取布局属性
105        let layout_style = props.layout_style();
106        // 用新属性重建自身
107        *self = Self {
108            padding: props.padding,
109            border_style: props.border_style,
110            borders: props.borders,
111            border_set: props.border_set,
112            style: props.style,
113            top_title: props.top_title.clone(),
114            bottom_title: props.bottom_title.clone(),
115        };
116        // 设置布局样式
117        updater.set_layout_style(layout_style);
118        // 更新子组件
119        updater.update_children(&mut props.children, None);
120    }
121
122    /// 渲染 Border 组件
123    fn draw(&mut self, drawer: &mut crate::ComponentDrawer<'_, '_>) {
124        // 构建 Block,设置样式、边框、内边距等
125        let mut block = Block::new()
126            .style(self.style)
127            .borders(self.borders)
128            .border_set(self.border_set)
129            .border_style(self.border_style)
130            .padding(self.padding);
131
132        // 设置顶部标题(如有)
133        if let Some(top_title) = &self.top_title {
134            block = block.title_top(top_title.clone());
135        }
136
137        // 设置底部标题(如有)
138        if let Some(bottom_title) = &self.bottom_title {
139            block = block.title_bottom(bottom_title.clone());
140        }
141
142        // 计算内容区域
143        let inner_area = block.inner(drawer.area);
144        // 渲染边框
145        block.render(drawer.area, drawer.buffer_mut());
146        // 更新绘制区域为内容区,供子组件使用
147        drawer.area = inner_area;
148    }
149}