跳转到内容

WrappedText 长文本

WrappedText 用来渲染纯文本长正文。它会先按指定宽度把文本硬换行,再把换行后的真实行数写回布局高度,所以外层 ScrollView 可以正确计算滚动范围。

wrapped text demo

这个录屏由 docs/tapes/wrapped-text.tape 生成。示例把一段中英文说明文本放进 ScrollView:按 j/k 逐行滚动,按 PageDown/PageUp 翻页,底部标题会显示当前 offset。

cargo run --example wrapped_text

j/k 或方向键逐行滚动,按 PageDown/PageUp 翻页,按 Home/End 跳到顶部或底部,按 q 退出。

Text(wrap: true) 适合短句、标签、提示语和富文本片段。它在绘制时软换行,但布局阶段仍然很难知道最终会占多少行。

长正文不一样。阅读器、日志详情、帮助文档、说明面板都需要一个稳定的内容高度,否则滚动条、EndPageDown 和外部状态面板都会失真。

WrappedText 在布局前完成硬换行,让 ScrollView 拿到真实内容高度。
ScrollView(
    flex_direction: Direction::Vertical,
    scroll_view_state: scroll_state,
    scroll_bars: ScrollBars {
        vertical_scrollbar_visibility: ScrollbarVisibility::Always,
        horizontal_scrollbar_visibility: ScrollbarVisibility::Never,
        ..Default::default()
    },
    block: Block::bordered()
        .title(Line::from(" measured prose ").cyan().centered()),
) {
    WrappedText(
        text: BODY,
        wrap_width: 72,
        style: Style::new().white(),
    )
}

text 是纯文本。组件不会内置章节、搜索命中、高亮或阅读进度,这些都应该由页面自己组合。

wrap_width 表示换行宽度,单位是终端列。长正文放在 ScrollView 里时,优先显式传入它:

WrappedText(
    text: body,
    wrap_width: 72,
)

这是因为当前组件更新阶段还拿不到最终绘制宽度;显式宽度能让 WrappedText 在布局前算出准确行数。

如果没有传 wrap_width,组件会尝试从 width: Constraint::Length(...) 推导。两个都没有时会使用保守的默认宽度,适合临时预览,不适合作为滚动容器里的最终写法。

auto_height 默认开启。关闭后组件仍然会硬换行,但不会自动把高度改成行数,适合你想用固定高度截断内容的特殊场景:

WrappedText(
    text: body,
    wrap_width: 72,
    auto_height: false,
    height: Constraint::Length(8),
)

WrappedText 只负责正文测量。滚动键位、滚动条和外部 offset 仍然由 ScrollView 或页面 handler 处理:

hooks.use_event_handler(EventScope::Current, EventPriority::Normal, move |event| {
    let Event::Key(key) = &event else {
        scroll_state.write().handle_event(&event);
        return EventResult::Ignored;
    };

    match key.code {
        KeyCode::Char('j') | KeyCode::Down | KeyCode::PageDown => {
            scroll_state.write().handle_event(&event);
            EventResult::Consumed
        }
        KeyCode::Char('k') | KeyCode::Up | KeyCode::PageUp => {
            scroll_state.write().handle_event(&event);
            EventResult::Consumed
        }
        _ => EventResult::Ignored,
    }
});

如果你需要逐行高亮、选择、虚拟化或按索引跳转,看 VirtualList。如果只是短文本展示,继续使用 Text