Skip to main content

ratatui_kit/components/
text.rs

1use crate::{AnyElement, element, prelude::Fragment};
2use ratatui::{
3    buffer::Buffer,
4    layout::{Position, Rect},
5    style::Style,
6    text::{Line, Text as RataText},
7    widgets::{Paragraph, Widget},
8};
9use ratatui_kit_macros::{Props, component};
10use std::ops::{Deref, DerefMut};
11
12#[derive(Clone, Default)]
13pub struct TextParagraph<'a> {
14    inner: Paragraph<'a>,
15}
16
17impl<'a> Deref for TextParagraph<'a> {
18    type Target = Paragraph<'a>;
19
20    fn deref(&self) -> &Self::Target {
21        &self.inner
22    }
23}
24
25impl DerefMut for TextParagraph<'_> {
26    fn deref_mut(&mut self) -> &mut Self::Target {
27        &mut self.inner
28    }
29}
30
31// 让 `TextParagraph` 成为**按值** `Widget`,以匹配 WidgetAdapter 改后的 `T: Widget` 约束。
32// `Paragraph` 0.30 起本就是按值 Widget,直接消费式转发。
33impl Widget for TextParagraph<'_> {
34    fn render(self, area: Rect, buf: &mut Buffer) {
35        self.inner.render(area, buf);
36    }
37}
38
39impl From<String> for TextParagraph<'_> {
40    fn from(value: String) -> Self {
41        Self {
42            inner: Paragraph::new(value),
43        }
44    }
45}
46
47impl<'a> From<Paragraph<'a>> for TextParagraph<'a> {
48    fn from(value: Paragraph<'a>) -> Self {
49        Self { inner: value }
50    }
51}
52
53// 让 Text 组件的 `text:` 字段直接吃字符串字面量 / Line / Text(都经 `(#expr).into()`),
54// 从而 `Text(text: "速度:", style: s)` 可替代高频的 `$Line::from("速度:").style(s)`。
55impl<'a> From<&'a str> for TextParagraph<'a> {
56    fn from(value: &'a str) -> Self {
57        Self {
58            inner: Paragraph::new(value),
59        }
60    }
61}
62
63impl<'a> From<Line<'a>> for TextParagraph<'a> {
64    fn from(value: Line<'a>) -> Self {
65        Self {
66            inner: Paragraph::new(value),
67        }
68    }
69}
70
71impl<'a> From<RataText<'a>> for TextParagraph<'a> {
72    fn from(value: RataText<'a>) -> Self {
73        Self {
74            inner: Paragraph::new(value),
75        }
76    }
77}
78
79#[derive(Default, Props)]
80pub struct TextProps {
81    pub text: TextParagraph<'static>,
82    pub style: Style,
83    pub alignment: ratatui::layout::Alignment,
84    pub scroll: Position,
85    // 是否换行(trim)。可直接传 `bool`(自动 `Some`)或 `Option<bool>`。
86    pub wrap: Option<bool>,
87}
88
89#[component]
90pub fn Text(props: &TextProps) -> impl Into<AnyElement<'static>> {
91    let paragraph = props
92        .text
93        .inner
94        .clone()
95        .style(props.style)
96        .scroll((props.scroll.x, props.scroll.y))
97        .alignment(props.alignment);
98
99    let paragraph = if let Some(wrap) = props.wrap {
100        paragraph.wrap(ratatui::widgets::Wrap { trim: wrap })
101    } else {
102        paragraph
103    };
104
105    let paragraph = TextParagraph::from(paragraph);
106
107    element! {
108        Fragment{
109            widget(paragraph)
110        }
111    }
112}