mirror of
https://github.com/ratatui-org/ratatui
synced 2024-11-10 07:04:17 +00:00
refactor(text): replace Spans
with Line
(#178)
* refactor: add Line type to replace Spans `Line` is a significantly better name over `Spans` as the plural causes confusion and the type really is a representation of a line of text made up of spans. This is a backwards compatible version of the approach from https://github.com/tui-rs-revival/ratatui/pull/175 There is a significant amount of code that uses the Spans type and methods, so instead of just renaming it, we add a new type and replace parameters that accepts a `Spans` with a parameter that accepts `Into<Line>`. Note that the examples have been intentionally left using `Spans` in this commit to demonstrate the compiler warnings that will be emitted in existing code. Implementation notes: - moves the Spans code to text::spans and publicly reexports on the text module. This makes the test in that module only relevant to the Spans type. - adds a line module with a copy of the code and tests from Spans with a single addition: `impl<'a> From<Spans<'a>> for Line<'a>` - adds tests for `Spans` (created and checked before refactoring) - adds the same tests for `Line` - updates all widget methods that accept and store Spans to instead store `Line` and accept `Into<Line>` * refactor: move text::Masked to text::masked::Masked Re-exports the Masked type at text::Masked * refactor: replace Spans with Line in tests/examples/docs
This commit is contained in:
parent
4437835057
commit
728f82c084
26 changed files with 757 additions and 385 deletions
|
@ -4,8 +4,8 @@ use ratatui::{
|
||||||
layout::{Constraint, Direction, Layout, Rect},
|
layout::{Constraint, Direction, Layout, Rect},
|
||||||
style::{Color, Modifier, Style},
|
style::{Color, Modifier, Style},
|
||||||
symbols,
|
symbols,
|
||||||
text::{Span, Spans},
|
text::{Line, Span},
|
||||||
widgets::canvas::{Canvas, Circle, Line, Map, MapResolution, Rectangle},
|
widgets::canvas::{Canvas, Circle, Line as CanvasLine, Map, MapResolution, Rectangle},
|
||||||
widgets::{
|
widgets::{
|
||||||
Axis, BarChart, Block, Borders, Cell, Chart, Dataset, Gauge, LineGauge, List, ListItem,
|
Axis, BarChart, Block, Borders, Cell, Chart, Dataset, Gauge, LineGauge, List, ListItem,
|
||||||
Paragraph, Row, Sparkline, Table, Tabs, Wrap,
|
Paragraph, Row, Sparkline, Table, Tabs, Wrap,
|
||||||
|
@ -21,7 +21,7 @@ pub fn draw<B: Backend>(f: &mut Frame<B>, app: &mut App) {
|
||||||
.tabs
|
.tabs
|
||||||
.titles
|
.titles
|
||||||
.iter()
|
.iter()
|
||||||
.map(|t| Spans::from(Span::styled(*t, Style::default().fg(Color::Green))))
|
.map(|t| Line::from(Span::styled(*t, Style::default().fg(Color::Green))))
|
||||||
.collect();
|
.collect();
|
||||||
let tabs = Tabs::new(titles)
|
let tabs = Tabs::new(titles)
|
||||||
.block(Block::default().borders(Borders::ALL).title(app.title))
|
.block(Block::default().borders(Borders::ALL).title(app.title))
|
||||||
|
@ -137,7 +137,7 @@ where
|
||||||
.tasks
|
.tasks
|
||||||
.items
|
.items
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| ListItem::new(vec![Spans::from(Span::raw(*i))]))
|
.map(|i| ListItem::new(vec![Line::from(Span::raw(*i))]))
|
||||||
.collect();
|
.collect();
|
||||||
let tasks = List::new(tasks)
|
let tasks = List::new(tasks)
|
||||||
.block(Block::default().borders(Borders::ALL).title("List"))
|
.block(Block::default().borders(Borders::ALL).title("List"))
|
||||||
|
@ -161,7 +161,7 @@ where
|
||||||
"WARNING" => warning_style,
|
"WARNING" => warning_style,
|
||||||
_ => info_style,
|
_ => info_style,
|
||||||
};
|
};
|
||||||
let content = vec![Spans::from(vec![
|
let content = vec![Line::from(vec![
|
||||||
Span::styled(format!("{:<9}", level), s),
|
Span::styled(format!("{:<9}", level), s),
|
||||||
Span::raw(evt),
|
Span::raw(evt),
|
||||||
])];
|
])];
|
||||||
|
@ -261,9 +261,9 @@ where
|
||||||
B: Backend,
|
B: Backend,
|
||||||
{
|
{
|
||||||
let text = vec![
|
let text = vec![
|
||||||
Spans::from("This is a paragraph with several lines. You can change style your text the way you want"),
|
Line::from("This is a paragraph with several lines. You can change style your text the way you want"),
|
||||||
Spans::from(""),
|
Line::from(""),
|
||||||
Spans::from(vec![
|
Line::from(vec![
|
||||||
Span::from("For example: "),
|
Span::from("For example: "),
|
||||||
Span::styled("under", Style::default().fg(Color::Red)),
|
Span::styled("under", Style::default().fg(Color::Red)),
|
||||||
Span::raw(" "),
|
Span::raw(" "),
|
||||||
|
@ -272,7 +272,7 @@ where
|
||||||
Span::styled("rainbow", Style::default().fg(Color::Blue)),
|
Span::styled("rainbow", Style::default().fg(Color::Blue)),
|
||||||
Span::raw("."),
|
Span::raw("."),
|
||||||
]),
|
]),
|
||||||
Spans::from(vec![
|
Line::from(vec![
|
||||||
Span::raw("Oh and if you didn't "),
|
Span::raw("Oh and if you didn't "),
|
||||||
Span::styled("notice", Style::default().add_modifier(Modifier::ITALIC)),
|
Span::styled("notice", Style::default().add_modifier(Modifier::ITALIC)),
|
||||||
Span::raw(" you can "),
|
Span::raw(" you can "),
|
||||||
|
@ -283,7 +283,7 @@ where
|
||||||
Span::styled("text", Style::default().add_modifier(Modifier::UNDERLINED)),
|
Span::styled("text", Style::default().add_modifier(Modifier::UNDERLINED)),
|
||||||
Span::raw(".")
|
Span::raw(".")
|
||||||
]),
|
]),
|
||||||
Spans::from(
|
Line::from(
|
||||||
"One more thing is that it should display unicode characters: 10€"
|
"One more thing is that it should display unicode characters: 10€"
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
@ -354,7 +354,7 @@ where
|
||||||
});
|
});
|
||||||
for (i, s1) in app.servers.iter().enumerate() {
|
for (i, s1) in app.servers.iter().enumerate() {
|
||||||
for s2 in &app.servers[i + 1..] {
|
for s2 in &app.servers[i + 1..] {
|
||||||
ctx.draw(&Line {
|
ctx.draw(&CanvasLine {
|
||||||
x1: s1.coords.1,
|
x1: s1.coords.1,
|
||||||
y1: s1.coords.0,
|
y1: s1.coords.0,
|
||||||
y2: s2.coords.0,
|
y2: s2.coords.0,
|
||||||
|
|
|
@ -4,7 +4,7 @@ use ratatui::{
|
||||||
layout::{Alignment, Constraint, Direction, Layout, Rect},
|
layout::{Alignment, Constraint, Direction, Layout, Rect},
|
||||||
style::{Color, Modifier, Style},
|
style::{Color, Modifier, Style},
|
||||||
symbols,
|
symbols,
|
||||||
text::{Span, Spans},
|
text::{Line, Span},
|
||||||
widgets::{Block, Gauge, LineGauge, List, ListItem, Paragraph, Widget},
|
widgets::{Block, Gauge, LineGauge, List, ListItem, Paragraph, Widget},
|
||||||
Frame, Terminal, TerminalOptions, Viewport,
|
Frame, Terminal, TerminalOptions, Viewport,
|
||||||
};
|
};
|
||||||
|
@ -193,7 +193,7 @@ fn run_app<B: Backend>(
|
||||||
Event::DownloadDone(worker_id, download_id) => {
|
Event::DownloadDone(worker_id, download_id) => {
|
||||||
let download = downloads.in_progress.remove(&worker_id).unwrap();
|
let download = downloads.in_progress.remove(&worker_id).unwrap();
|
||||||
terminal.insert_before(1, |buf| {
|
terminal.insert_before(1, |buf| {
|
||||||
Paragraph::new(Spans::from(vec![
|
Paragraph::new(Line::from(vec![
|
||||||
Span::from("Finished "),
|
Span::from("Finished "),
|
||||||
Span::styled(
|
Span::styled(
|
||||||
format!("download {}", download_id),
|
format!("download {}", download_id),
|
||||||
|
@ -254,7 +254,7 @@ fn ui<B: Backend>(f: &mut Frame<B>, downloads: &Downloads) {
|
||||||
.in_progress
|
.in_progress
|
||||||
.values()
|
.values()
|
||||||
.map(|download| {
|
.map(|download| {
|
||||||
ListItem::new(Spans::from(vec![
|
ListItem::new(Line::from(vec![
|
||||||
Span::raw(symbols::DOT),
|
Span::raw(symbols::DOT),
|
||||||
Span::styled(
|
Span::styled(
|
||||||
format!(" download {:>2}", download.id),
|
format!(" download {:>2}", download.id),
|
||||||
|
|
|
@ -7,7 +7,7 @@ use ratatui::{
|
||||||
backend::{Backend, CrosstermBackend},
|
backend::{Backend, CrosstermBackend},
|
||||||
layout::{Constraint, Corner, Direction, Layout},
|
layout::{Constraint, Corner, Direction, Layout},
|
||||||
style::{Color, Modifier, Style},
|
style::{Color, Modifier, Style},
|
||||||
text::{Span, Spans},
|
text::{Line, Span},
|
||||||
widgets::{Block, Borders, List, ListItem, ListState},
|
widgets::{Block, Borders, List, ListItem, ListState},
|
||||||
Frame, Terminal,
|
Frame, Terminal,
|
||||||
};
|
};
|
||||||
|
@ -217,9 +217,9 @@ fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
|
||||||
.items
|
.items
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
let mut lines = vec![Spans::from(i.0)];
|
let mut lines = vec![Line::from(i.0)];
|
||||||
for _ in 0..i.1 {
|
for _ in 0..i.1 {
|
||||||
lines.push(Spans::from(Span::styled(
|
lines.push(Line::from(Span::styled(
|
||||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
||||||
Style::default().add_modifier(Modifier::ITALIC),
|
Style::default().add_modifier(Modifier::ITALIC),
|
||||||
)));
|
)));
|
||||||
|
@ -257,7 +257,7 @@ fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
|
||||||
_ => Style::default(),
|
_ => Style::default(),
|
||||||
};
|
};
|
||||||
// Add a example datetime and apply proper spacing between them
|
// Add a example datetime and apply proper spacing between them
|
||||||
let header = Spans::from(vec![
|
let header = Line::from(vec![
|
||||||
Span::styled(format!("{:<9}", level), s),
|
Span::styled(format!("{:<9}", level), s),
|
||||||
Span::raw(" "),
|
Span::raw(" "),
|
||||||
Span::styled(
|
Span::styled(
|
||||||
|
@ -266,7 +266,7 @@ fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
// The event gets its own line
|
// The event gets its own line
|
||||||
let log = Spans::from(vec![Span::raw(event)]);
|
let log = Line::from(vec![Span::raw(event)]);
|
||||||
|
|
||||||
// Here several things happen:
|
// Here several things happen:
|
||||||
// 1. Add a `---` spacing line above the final list entry
|
// 1. Add a `---` spacing line above the final list entry
|
||||||
|
@ -274,9 +274,9 @@ fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
|
||||||
// 3. Add a spacer line
|
// 3. Add a spacer line
|
||||||
// 4. Add the actual event
|
// 4. Add the actual event
|
||||||
ListItem::new(vec![
|
ListItem::new(vec![
|
||||||
Spans::from("-".repeat(chunks[1].width as usize)),
|
Line::from("-".repeat(chunks[1].width as usize)),
|
||||||
header,
|
header,
|
||||||
Spans::from(""),
|
Line::from(""),
|
||||||
log,
|
log,
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
|
@ -26,7 +26,7 @@ use crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen};
|
||||||
|
|
||||||
use ratatui::backend::{Backend, CrosstermBackend};
|
use ratatui::backend::{Backend, CrosstermBackend};
|
||||||
use ratatui::layout::Alignment;
|
use ratatui::layout::Alignment;
|
||||||
use ratatui::text::Spans;
|
use ratatui::text::Line;
|
||||||
use ratatui::widgets::{Block, Borders, Paragraph};
|
use ratatui::widgets::{Block, Borders, Paragraph};
|
||||||
use ratatui::{Frame, Terminal};
|
use ratatui::{Frame, Terminal};
|
||||||
|
|
||||||
|
@ -113,23 +113,23 @@ fn run_tui<B: Backend>(terminal: &mut Terminal<B>, app: &mut App) -> io::Result<
|
||||||
fn ui<B: Backend>(f: &mut Frame<B>, app: &App) {
|
fn ui<B: Backend>(f: &mut Frame<B>, app: &App) {
|
||||||
let text = vec![
|
let text = vec![
|
||||||
if app.hook_enabled {
|
if app.hook_enabled {
|
||||||
Spans::from("HOOK IS CURRENTLY **ENABLED**")
|
Line::from("HOOK IS CURRENTLY **ENABLED**")
|
||||||
} else {
|
} else {
|
||||||
Spans::from("HOOK IS CURRENTLY **DISABLED**")
|
Line::from("HOOK IS CURRENTLY **DISABLED**")
|
||||||
},
|
},
|
||||||
Spans::from(""),
|
Line::from(""),
|
||||||
Spans::from("press `p` to panic"),
|
Line::from("press `p` to panic"),
|
||||||
Spans::from("press `e` to enable the terminal-resetting panic hook"),
|
Line::from("press `e` to enable the terminal-resetting panic hook"),
|
||||||
Spans::from("press any other key to quit without panic"),
|
Line::from("press any other key to quit without panic"),
|
||||||
Spans::from(""),
|
Line::from(""),
|
||||||
Spans::from("when you panic without the chained hook,"),
|
Line::from("when you panic without the chained hook,"),
|
||||||
Spans::from("you will likely have to reset your terminal afterwards"),
|
Line::from("you will likely have to reset your terminal afterwards"),
|
||||||
Spans::from("with the `reset` command"),
|
Line::from("with the `reset` command"),
|
||||||
Spans::from(""),
|
Line::from(""),
|
||||||
Spans::from("with the chained panic hook enabled,"),
|
Line::from("with the chained panic hook enabled,"),
|
||||||
Spans::from("you should see the panic report as you would without ratatui"),
|
Line::from("you should see the panic report as you would without ratatui"),
|
||||||
Spans::from(""),
|
Line::from(""),
|
||||||
Spans::from("try first without the panic handler to see the difference"),
|
Line::from("try first without the panic handler to see the difference"),
|
||||||
];
|
];
|
||||||
|
|
||||||
let b = Block::default()
|
let b = Block::default()
|
||||||
|
|
|
@ -7,7 +7,7 @@ use ratatui::{
|
||||||
backend::{Backend, CrosstermBackend},
|
backend::{Backend, CrosstermBackend},
|
||||||
layout::{Alignment, Constraint, Direction, Layout},
|
layout::{Alignment, Constraint, Direction, Layout},
|
||||||
style::{Color, Modifier, Style},
|
style::{Color, Modifier, Style},
|
||||||
text::{Masked, Span, Spans},
|
text::{Line, Masked, Span},
|
||||||
widgets::{Block, Borders, Paragraph, Wrap},
|
widgets::{Block, Borders, Paragraph, Wrap},
|
||||||
Frame, Terminal,
|
Frame, Terminal,
|
||||||
};
|
};
|
||||||
|
@ -113,27 +113,27 @@ fn ui<B: Backend>(f: &mut Frame<B>, app: &App) {
|
||||||
.split(size);
|
.split(size);
|
||||||
|
|
||||||
let text = vec![
|
let text = vec![
|
||||||
Spans::from("This is a line "),
|
Line::from("This is a line "),
|
||||||
Spans::from(Span::styled(
|
Line::from(Span::styled(
|
||||||
"This is a line ",
|
"This is a line ",
|
||||||
Style::default().fg(Color::Red),
|
Style::default().fg(Color::Red),
|
||||||
)),
|
)),
|
||||||
Spans::from(Span::styled(
|
Line::from(Span::styled(
|
||||||
"This is a line",
|
"This is a line",
|
||||||
Style::default().bg(Color::Blue),
|
Style::default().bg(Color::Blue),
|
||||||
)),
|
)),
|
||||||
Spans::from(Span::styled(
|
Line::from(Span::styled(
|
||||||
"This is a longer line",
|
"This is a longer line",
|
||||||
Style::default().add_modifier(Modifier::CROSSED_OUT),
|
Style::default().add_modifier(Modifier::CROSSED_OUT),
|
||||||
)),
|
)),
|
||||||
Spans::from(Span::styled(&long_line, Style::default().bg(Color::Green))),
|
Line::from(Span::styled(&long_line, Style::default().bg(Color::Green))),
|
||||||
Spans::from(Span::styled(
|
Line::from(Span::styled(
|
||||||
"This is a line",
|
"This is a line",
|
||||||
Style::default()
|
Style::default()
|
||||||
.fg(Color::Green)
|
.fg(Color::Green)
|
||||||
.add_modifier(Modifier::ITALIC),
|
.add_modifier(Modifier::ITALIC),
|
||||||
)),
|
)),
|
||||||
Spans::from(vec![
|
Line::from(vec![
|
||||||
Span::raw("Masked text: "),
|
Span::raw("Masked text: "),
|
||||||
Span::styled(
|
Span::styled(
|
||||||
Masked::new("password", '*'),
|
Masked::new("password", '*'),
|
||||||
|
|
|
@ -7,7 +7,7 @@ use ratatui::{
|
||||||
backend::{Backend, CrosstermBackend},
|
backend::{Backend, CrosstermBackend},
|
||||||
layout::{Constraint, Direction, Layout},
|
layout::{Constraint, Direction, Layout},
|
||||||
style::{Color, Modifier, Style},
|
style::{Color, Modifier, Style},
|
||||||
text::{Span, Spans},
|
text::{Line, Span},
|
||||||
widgets::{Block, Borders, Tabs},
|
widgets::{Block, Borders, Tabs},
|
||||||
Frame, Terminal,
|
Frame, Terminal,
|
||||||
};
|
};
|
||||||
|
@ -99,7 +99,7 @@ fn ui<B: Backend>(f: &mut Frame<B>, app: &App) {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|t| {
|
.map(|t| {
|
||||||
let (first, rest) = t.split_at(1);
|
let (first, rest) = t.split_at(1);
|
||||||
Spans::from(vec![
|
Line::from(vec![
|
||||||
Span::styled(first, Style::default().fg(Color::Yellow)),
|
Span::styled(first, Style::default().fg(Color::Yellow)),
|
||||||
Span::styled(rest, Style::default().fg(Color::Green)),
|
Span::styled(rest, Style::default().fg(Color::Green)),
|
||||||
])
|
])
|
||||||
|
|
|
@ -18,7 +18,7 @@ use ratatui::{
|
||||||
backend::{Backend, CrosstermBackend},
|
backend::{Backend, CrosstermBackend},
|
||||||
layout::{Constraint, Direction, Layout},
|
layout::{Constraint, Direction, Layout},
|
||||||
style::{Color, Modifier, Style},
|
style::{Color, Modifier, Style},
|
||||||
text::{Span, Spans, Text},
|
text::{Line, Span, Text},
|
||||||
widgets::{Block, Borders, List, ListItem, Paragraph},
|
widgets::{Block, Borders, List, ListItem, Paragraph},
|
||||||
Frame, Terminal,
|
Frame, Terminal,
|
||||||
};
|
};
|
||||||
|
@ -150,7 +150,7 @@ fn ui<B: Backend>(f: &mut Frame<B>, app: &App) {
|
||||||
Style::default(),
|
Style::default(),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
let mut text = Text::from(Spans::from(msg));
|
let mut text = Text::from(Line::from(msg));
|
||||||
text.patch_style(style);
|
text.patch_style(style);
|
||||||
let help_message = Paragraph::new(text);
|
let help_message = Paragraph::new(text);
|
||||||
f.render_widget(help_message, chunks[0]);
|
f.render_widget(help_message, chunks[0]);
|
||||||
|
@ -183,7 +183,7 @@ fn ui<B: Backend>(f: &mut Frame<B>, app: &App) {
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, m)| {
|
.map(|(i, m)| {
|
||||||
let content = Spans::from(Span::raw(format!("{}: {}", i, m)));
|
let content = Line::from(Span::raw(format!("{}: {}", i, m)));
|
||||||
ListItem::new(content)
|
ListItem::new(content)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
#[allow(deprecated)]
|
||||||
use crate::{
|
use crate::{
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
style::{Color, Modifier, Style},
|
style::{Color, Modifier, Style},
|
||||||
text::{Span, Spans},
|
text::{Line, Span, Spans},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
cmp::min,
|
cmp::min,
|
||||||
|
@ -309,6 +310,8 @@ impl Buffer {
|
||||||
(x_offset as u16, y)
|
(x_offset as u16, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
|
#[deprecated(note = "Use `Buffer::set_line` instead")]
|
||||||
pub fn set_spans(&mut self, x: u16, y: u16, spans: &Spans<'_>, width: u16) -> (u16, u16) {
|
pub fn set_spans(&mut self, x: u16, y: u16, spans: &Spans<'_>, width: u16) -> (u16, u16) {
|
||||||
let mut remaining_width = width;
|
let mut remaining_width = width;
|
||||||
let mut x = x;
|
let mut x = x;
|
||||||
|
@ -330,6 +333,27 @@ impl Buffer {
|
||||||
(x, y)
|
(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_line(&mut self, x: u16, y: u16, line: &Line<'_>, width: u16) -> (u16, u16) {
|
||||||
|
let mut remaining_width = width;
|
||||||
|
let mut x = x;
|
||||||
|
for span in &line.spans {
|
||||||
|
if remaining_width == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let pos = self.set_stringn(
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
span.content.as_ref(),
|
||||||
|
remaining_width as usize,
|
||||||
|
span.style,
|
||||||
|
);
|
||||||
|
let w = pos.0.saturating_sub(x);
|
||||||
|
x = pos.0;
|
||||||
|
remaining_width = remaining_width.saturating_sub(w);
|
||||||
|
}
|
||||||
|
(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_span(&mut self, x: u16, y: u16, span: &Span<'_>, width: u16) -> (u16, u16) {
|
pub fn set_span(&mut self, x: u16, y: u16, span: &Span<'_>, width: u16) -> (u16, u16) {
|
||||||
self.set_stringn(x, y, span.content.as_ref(), width as usize, span.style)
|
self.set_stringn(x, y, span.content.as_ref(), width as usize, span.style)
|
||||||
}
|
}
|
||||||
|
|
|
@ -386,14 +386,14 @@ where
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ratatui::widgets::{Paragraph, Widget};
|
/// # use ratatui::widgets::{Paragraph, Widget};
|
||||||
/// # use ratatui::text::{Spans, Span};
|
/// # use ratatui::text::{Line, Span};
|
||||||
/// # use ratatui::style::{Color, Style};
|
/// # use ratatui::style::{Color, Style};
|
||||||
/// # use ratatui::{Terminal};
|
/// # use ratatui::{Terminal};
|
||||||
/// # use ratatui::backend::TestBackend;
|
/// # use ratatui::backend::TestBackend;
|
||||||
/// # let backend = TestBackend::new(10, 10);
|
/// # let backend = TestBackend::new(10, 10);
|
||||||
/// # let mut terminal = Terminal::new(backend).unwrap();
|
/// # let mut terminal = Terminal::new(backend).unwrap();
|
||||||
/// terminal.insert_before(1, |buf| {
|
/// terminal.insert_before(1, |buf| {
|
||||||
/// Paragraph::new(Spans::from(vec![
|
/// Paragraph::new(Line::from(vec![
|
||||||
/// Span::raw("This line will be added "),
|
/// Span::raw("This line will be added "),
|
||||||
/// Span::styled("before", Style::default().fg(Color::Blue)),
|
/// Span::styled("before", Style::default().fg(Color::Blue)),
|
||||||
/// Span::raw(" the current viewport")
|
/// Span::raw(" the current viewport")
|
||||||
|
|
309
src/text.rs
309
src/text.rs
|
@ -3,12 +3,12 @@
|
||||||
//! A terminal UI is at its root a lot of strings. In order to make it accessible and stylish,
|
//! A terminal UI is at its root a lot of strings. In order to make it accessible and stylish,
|
||||||
//! those strings may be associated to a set of styles. `ratatui` has three ways to represent them:
|
//! those strings may be associated to a set of styles. `ratatui` has three ways to represent them:
|
||||||
//! - A single line string where all graphemes have the same style is represented by a [`Span`].
|
//! - A single line string where all graphemes have the same style is represented by a [`Span`].
|
||||||
//! - A single line string where each grapheme may have its own style is represented by [`Spans`].
|
//! - A single line string where each grapheme may have its own style is represented by [`Line`].
|
||||||
//! - A multiple line string where each grapheme may have its own style is represented by a
|
//! - A multiple line string where each grapheme may have its own style is represented by a
|
||||||
//! [`Text`].
|
//! [`Text`].
|
||||||
//!
|
//!
|
||||||
//! These types form a hierarchy: [`Spans`] is a collection of [`Span`] and each line of [`Text`]
|
//! These types form a hierarchy: [`Line`] is a collection of [`Span`] and each line of [`Text`]
|
||||||
//! is a [`Spans`].
|
//! is a [`Line`].
|
||||||
//!
|
//!
|
||||||
//! Keep it mind that a lot of widgets will use those types to advertise what kind of string is
|
//! Keep it mind that a lot of widgets will use those types to advertise what kind of string is
|
||||||
//! supported for their properties. Moreover, `ratatui` provides convenient `From` implementations so
|
//! supported for their properties. Moreover, `ratatui` provides convenient `From` implementations so
|
||||||
|
@ -16,20 +16,20 @@
|
||||||
//! primitives when you need additional styling capabilities.
|
//! primitives when you need additional styling capabilities.
|
||||||
//!
|
//!
|
||||||
//! For example, for the [`crate::widgets::Block`] widget, all the following calls are valid to set
|
//! For example, for the [`crate::widgets::Block`] widget, all the following calls are valid to set
|
||||||
//! its `title` property (which is a [`Spans`] under the hood):
|
//! its `title` property (which is a [`Line`] under the hood):
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! # use ratatui::widgets::Block;
|
//! # use ratatui::widgets::Block;
|
||||||
//! # use ratatui::text::{Span, Spans};
|
//! # use ratatui::text::{Span, Line};
|
||||||
//! # use ratatui::style::{Color, Style};
|
//! # use ratatui::style::{Color, Style};
|
||||||
//! // A simple string with no styling.
|
//! // A simple string with no styling.
|
||||||
//! // Converted to Spans(vec![
|
//! // Converted to Line(vec![
|
||||||
//! // Span { content: Cow::Borrowed("My title"), style: Style { .. } }
|
//! // Span { content: Cow::Borrowed("My title"), style: Style { .. } }
|
||||||
//! // ])
|
//! // ])
|
||||||
//! let block = Block::default().title("My title");
|
//! let block = Block::default().title("My title");
|
||||||
//!
|
//!
|
||||||
//! // A simple string with a unique style.
|
//! // A simple string with a unique style.
|
||||||
//! // Converted to Spans(vec![
|
//! // Converted to Line(vec![
|
||||||
//! // Span { content: Cow::Borrowed("My title"), style: Style { fg: Some(Color::Yellow), .. }
|
//! // Span { content: Cow::Borrowed("My title"), style: Style { fg: Some(Color::Yellow), .. }
|
||||||
//! // ])
|
//! // ])
|
||||||
//! let block = Block::default().title(
|
//! let block = Block::default().title(
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
//! );
|
//! );
|
||||||
//!
|
//!
|
||||||
//! // A string with multiple styles.
|
//! // A string with multiple styles.
|
||||||
//! // Converted to Spans(vec![
|
//! // Converted to Line(vec![
|
||||||
//! // Span { content: Cow::Borrowed("My"), style: Style { fg: Some(Color::Yellow), .. } },
|
//! // Span { content: Cow::Borrowed("My"), style: Style { fg: Some(Color::Yellow), .. } },
|
||||||
//! // Span { content: Cow::Borrowed(" title"), .. }
|
//! // Span { content: Cow::Borrowed(" title"), .. }
|
||||||
//! // ])
|
//! // ])
|
||||||
|
@ -47,11 +47,16 @@
|
||||||
//! ]);
|
//! ]);
|
||||||
//! ```
|
//! ```
|
||||||
use crate::style::Style;
|
use crate::style::Style;
|
||||||
use std::borrow::Cow;
|
use std::{borrow::Cow, fmt::Debug};
|
||||||
use std::fmt::{self, Debug, Display};
|
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
|
mod line;
|
||||||
|
mod masked;
|
||||||
|
mod spans;
|
||||||
|
#[allow(deprecated)]
|
||||||
|
pub use {line::Line, masked::Masked, spans::Spans};
|
||||||
|
|
||||||
/// A grapheme associated to a style.
|
/// A grapheme associated to a style.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct StyledGrapheme<'a> {
|
pub struct StyledGrapheme<'a> {
|
||||||
|
@ -231,113 +236,6 @@ impl<'a> From<&'a str> for Span<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A string composed of clusters of graphemes, each with their own style.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Default, Eq)]
|
|
||||||
pub struct Spans<'a>(pub Vec<Span<'a>>);
|
|
||||||
|
|
||||||
impl<'a> Spans<'a> {
|
|
||||||
/// Returns the width of the underlying string.
|
|
||||||
///
|
|
||||||
/// ## Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # use ratatui::text::{Span, Spans};
|
|
||||||
/// # use ratatui::style::{Color, Style};
|
|
||||||
/// let spans = Spans::from(vec![
|
|
||||||
/// Span::styled("My", Style::default().fg(Color::Yellow)),
|
|
||||||
/// Span::raw(" text"),
|
|
||||||
/// ]);
|
|
||||||
/// assert_eq!(7, spans.width());
|
|
||||||
/// ```
|
|
||||||
pub fn width(&self) -> usize {
|
|
||||||
self.0.iter().map(Span::width).sum()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Patches the style of each Span in an existing Spans, adding modifiers from the given style.
|
|
||||||
///
|
|
||||||
/// ## Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # use ratatui::text::{Span, Spans};
|
|
||||||
/// # use ratatui::style::{Color, Style, Modifier};
|
|
||||||
/// let style = Style::default().fg(Color::Yellow).add_modifier(Modifier::ITALIC);
|
|
||||||
/// let mut raw_spans = Spans::from(vec![
|
|
||||||
/// Span::raw("My"),
|
|
||||||
/// Span::raw(" text"),
|
|
||||||
/// ]);
|
|
||||||
/// let mut styled_spans = Spans::from(vec![
|
|
||||||
/// Span::styled("My", style),
|
|
||||||
/// Span::styled(" text", style),
|
|
||||||
/// ]);
|
|
||||||
///
|
|
||||||
/// assert_ne!(raw_spans, styled_spans);
|
|
||||||
///
|
|
||||||
/// raw_spans.patch_style(style);
|
|
||||||
/// assert_eq!(raw_spans, styled_spans);
|
|
||||||
/// ```
|
|
||||||
pub fn patch_style(&mut self, style: Style) {
|
|
||||||
for span in &mut self.0 {
|
|
||||||
span.patch_style(style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resets the style of each Span in the Spans.
|
|
||||||
/// Equivalent to calling `patch_style(Style::reset())`.
|
|
||||||
///
|
|
||||||
/// ## Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # use ratatui::text::{Span, Spans};
|
|
||||||
/// # use ratatui::style::{Color, Style, Modifier};
|
|
||||||
/// let mut spans = Spans::from(vec![
|
|
||||||
/// Span::styled("My", Style::default().fg(Color::Yellow)),
|
|
||||||
/// Span::styled(" text", Style::default().add_modifier(Modifier::BOLD)),
|
|
||||||
/// ]);
|
|
||||||
///
|
|
||||||
/// spans.reset_style();
|
|
||||||
/// assert_eq!(Style::reset(), spans.0[0].style);
|
|
||||||
/// assert_eq!(Style::reset(), spans.0[1].style);
|
|
||||||
/// ```
|
|
||||||
pub fn reset_style(&mut self) {
|
|
||||||
for span in &mut self.0 {
|
|
||||||
span.reset_style();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<String> for Spans<'a> {
|
|
||||||
fn from(s: String) -> Spans<'a> {
|
|
||||||
Spans(vec![Span::from(s)])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a str> for Spans<'a> {
|
|
||||||
fn from(s: &'a str) -> Spans<'a> {
|
|
||||||
Spans(vec![Span::from(s)])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<Vec<Span<'a>>> for Spans<'a> {
|
|
||||||
fn from(spans: Vec<Span<'a>>) -> Spans<'a> {
|
|
||||||
Spans(spans)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<Span<'a>> for Spans<'a> {
|
|
||||||
fn from(span: Span<'a>) -> Spans<'a> {
|
|
||||||
Spans(vec![span])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<Spans<'a>> for String {
|
|
||||||
fn from(line: Spans<'a>) -> String {
|
|
||||||
line.0.iter().fold(String::new(), |mut acc, s| {
|
|
||||||
acc.push_str(s.content.as_ref());
|
|
||||||
acc
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A string split over multiple lines where each line is composed of several clusters, each with
|
/// A string split over multiple lines where each line is composed of several clusters, each with
|
||||||
/// their own style.
|
/// their own style.
|
||||||
///
|
///
|
||||||
|
@ -364,7 +262,7 @@ impl<'a> From<Spans<'a>> for String {
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone, PartialEq, Default, Eq)]
|
#[derive(Debug, Clone, PartialEq, Default, Eq)]
|
||||||
pub struct Text<'a> {
|
pub struct Text<'a> {
|
||||||
pub lines: Vec<Spans<'a>>,
|
pub lines: Vec<Line<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Text<'a> {
|
impl<'a> Text<'a> {
|
||||||
|
@ -382,13 +280,13 @@ impl<'a> Text<'a> {
|
||||||
T: Into<Cow<'a, str>>,
|
T: Into<Cow<'a, str>>,
|
||||||
{
|
{
|
||||||
let lines: Vec<_> = match content.into() {
|
let lines: Vec<_> = match content.into() {
|
||||||
Cow::Borrowed("") => vec![Spans::from("")],
|
Cow::Borrowed("") => vec![Line::from("")],
|
||||||
Cow::Borrowed(s) => s.lines().map(Spans::from).collect(),
|
Cow::Borrowed(s) => s.lines().map(Line::from).collect(),
|
||||||
Cow::Owned(s) if s.is_empty() => vec![Spans::from("")],
|
Cow::Owned(s) if s.is_empty() => vec![Line::from("")],
|
||||||
Cow::Owned(s) => s.lines().map(|l| Spans::from(l.to_owned())).collect(),
|
Cow::Owned(s) => s.lines().map(|l| Line::from(l.to_owned())).collect(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Text { lines }
|
Text::from(lines)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create some text (potentially multiple lines) with a style.
|
/// Create some text (potentially multiple lines) with a style.
|
||||||
|
@ -421,11 +319,7 @@ impl<'a> Text<'a> {
|
||||||
/// assert_eq!(15, text.width());
|
/// assert_eq!(15, text.width());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn width(&self) -> usize {
|
pub fn width(&self) -> usize {
|
||||||
self.lines
|
self.lines.iter().map(Line::width).max().unwrap_or_default()
|
||||||
.iter()
|
|
||||||
.map(Spans::width)
|
|
||||||
.max()
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the height.
|
/// Returns the height.
|
||||||
|
@ -468,14 +362,14 @@ impl<'a> Text<'a> {
|
||||||
/// ## Examples
|
/// ## Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ratatui::text::{Span, Spans, Text};
|
/// # use ratatui::text::{Span, Line, Text};
|
||||||
/// # use ratatui::style::{Color, Style, Modifier};
|
/// # use ratatui::style::{Color, Style, Modifier};
|
||||||
/// let style = Style::default().fg(Color::Yellow).add_modifier(Modifier::ITALIC);
|
/// let style = Style::default().fg(Color::Yellow).add_modifier(Modifier::ITALIC);
|
||||||
/// let mut text = Text::styled("The first line\nThe second line", style);
|
/// let mut text = Text::styled("The first line\nThe second line", style);
|
||||||
///
|
///
|
||||||
/// text.reset_style();
|
/// text.reset_style();
|
||||||
/// for line in &text.lines {
|
/// for line in &text.lines {
|
||||||
/// for span in &line.0 {
|
/// for span in &line.spans {
|
||||||
/// assert_eq!(Style::reset(), span.style);
|
/// assert_eq!(Style::reset(), span.style);
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
|
@ -508,25 +402,43 @@ impl<'a> From<Cow<'a, str>> for Text<'a> {
|
||||||
impl<'a> From<Span<'a>> for Text<'a> {
|
impl<'a> From<Span<'a>> for Text<'a> {
|
||||||
fn from(span: Span<'a>) -> Text<'a> {
|
fn from(span: Span<'a>) -> Text<'a> {
|
||||||
Text {
|
Text {
|
||||||
lines: vec![Spans::from(span)],
|
lines: vec![Line::from(span)],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
impl<'a> From<Spans<'a>> for Text<'a> {
|
impl<'a> From<Spans<'a>> for Text<'a> {
|
||||||
fn from(spans: Spans<'a>) -> Text<'a> {
|
fn from(spans: Spans<'a>) -> Text<'a> {
|
||||||
Text { lines: vec![spans] }
|
Text {
|
||||||
|
lines: vec![spans.into()],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> From<Line<'a>> for Text<'a> {
|
||||||
|
fn from(line: Line<'a>) -> Text<'a> {
|
||||||
|
Text { lines: vec![line] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
impl<'a> From<Vec<Spans<'a>>> for Text<'a> {
|
impl<'a> From<Vec<Spans<'a>>> for Text<'a> {
|
||||||
fn from(lines: Vec<Spans<'a>>) -> Text<'a> {
|
fn from(lines: Vec<Spans<'a>>) -> Text<'a> {
|
||||||
|
Text {
|
||||||
|
lines: lines.into_iter().map(|l| l.0.into()).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<Vec<Line<'a>>> for Text<'a> {
|
||||||
|
fn from(lines: Vec<Line<'a>>) -> Text<'a> {
|
||||||
Text { lines }
|
Text { lines }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoIterator for Text<'a> {
|
impl<'a> IntoIterator for Text<'a> {
|
||||||
type Item = Spans<'a>;
|
type Item = Line<'a>;
|
||||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
@ -534,129 +446,12 @@ impl<'a> IntoIterator for Text<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Extend<Spans<'a>> for Text<'a> {
|
impl<'a, T> Extend<T> for Text<'a>
|
||||||
fn extend<T: IntoIterator<Item = Spans<'a>>>(&mut self, iter: T) {
|
where
|
||||||
self.lines.extend(iter);
|
T: Into<Line<'a>>,
|
||||||
}
|
{
|
||||||
}
|
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
|
||||||
|
let lines = iter.into_iter().map(|s| s.into());
|
||||||
/// A wrapper around a string that is masked when displayed.
|
self.lines.extend(lines);
|
||||||
///
|
|
||||||
/// The masked string is displayed as a series of the same character.
|
|
||||||
/// This might be used to display a password field or similar secure data.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use ratatui::{buffer::Buffer, layout::Rect, text::Masked, widgets::{Paragraph, Widget}};
|
|
||||||
///
|
|
||||||
/// let mut buffer = Buffer::empty(Rect::new(0, 0, 5, 1));
|
|
||||||
/// let password = Masked::new("12345", 'x');
|
|
||||||
///
|
|
||||||
/// Paragraph::new(password).render(buffer.area, &mut buffer);
|
|
||||||
/// assert_eq!(buffer, Buffer::with_lines(vec!["xxxxx"]));
|
|
||||||
/// ```
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Masked<'a> {
|
|
||||||
inner: Cow<'a, str>,
|
|
||||||
mask_char: char,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Masked<'a> {
|
|
||||||
pub fn new(s: impl Into<Cow<'a, str>>, mask_char: char) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: s.into(),
|
|
||||||
mask_char,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The character to use for masking.
|
|
||||||
pub fn mask_char(&self) -> char {
|
|
||||||
self.mask_char
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The underlying string, with all characters masked.
|
|
||||||
pub fn value(&self) -> Cow<'a, str> {
|
|
||||||
self.inner.chars().map(|_| self.mask_char).collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Masked<'_> {
|
|
||||||
/// Debug representation of a masked string is the underlying string
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.write_str(&self.inner).map_err(|_| fmt::Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Masked<'_> {
|
|
||||||
/// Display representation of a masked string is the masked string
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.write_str(&self.value()).map_err(|_| fmt::Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a Masked<'a>> for Cow<'a, str> {
|
|
||||||
fn from(masked: &'a Masked) -> Cow<'a, str> {
|
|
||||||
masked.value()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<Masked<'a>> for Cow<'a, str> {
|
|
||||||
fn from(masked: Masked<'a>) -> Cow<'a, str> {
|
|
||||||
masked.value()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a Masked<'_>> for Text<'a> {
|
|
||||||
fn from(masked: &'a Masked) -> Text<'a> {
|
|
||||||
Text::raw(masked.value())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<Masked<'a>> for Text<'a> {
|
|
||||||
fn from(masked: Masked<'a>) -> Text<'a> {
|
|
||||||
Text::raw(masked.value())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::borrow::Borrow;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_masked_value() {
|
|
||||||
let masked = Masked::new("12345", 'x');
|
|
||||||
assert_eq!(masked.value(), "xxxxx");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_masked_debug() {
|
|
||||||
let masked = Masked::new("12345", 'x');
|
|
||||||
assert_eq!(format!("{masked:?}"), "12345");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_masked_display() {
|
|
||||||
let masked = Masked::new("12345", 'x');
|
|
||||||
assert_eq!(format!("{masked}"), "xxxxx");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_masked_conversions() {
|
|
||||||
let masked = Masked::new("12345", 'x');
|
|
||||||
|
|
||||||
let text: Text = masked.borrow().into();
|
|
||||||
assert_eq!(text.lines, vec![Spans::from("xxxxx")]);
|
|
||||||
|
|
||||||
let text: Text = masked.to_owned().into();
|
|
||||||
assert_eq!(text.lines, vec![Spans::from("xxxxx")]);
|
|
||||||
|
|
||||||
let cow: Cow<str> = masked.borrow().into();
|
|
||||||
assert_eq!(cow, "xxxxx");
|
|
||||||
|
|
||||||
let cow: Cow<str> = masked.to_owned().into();
|
|
||||||
assert_eq!(cow, "xxxxx");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
213
src/text/line.rs
Normal file
213
src/text/line.rs
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
#![allow(deprecated)]
|
||||||
|
use super::{Span, Spans, Style};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default, Eq)]
|
||||||
|
pub struct Line<'a> {
|
||||||
|
pub spans: Vec<Span<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Line<'a> {
|
||||||
|
/// Returns the width of the underlying string.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use ratatui::text::{Span, Line};
|
||||||
|
/// # use ratatui::style::{Color, Style};
|
||||||
|
/// let line = Line::from(vec![
|
||||||
|
/// Span::styled("My", Style::default().fg(Color::Yellow)),
|
||||||
|
/// Span::raw(" text"),
|
||||||
|
/// ]);
|
||||||
|
/// assert_eq!(7, line.width());
|
||||||
|
/// ```
|
||||||
|
pub fn width(&self) -> usize {
|
||||||
|
self.spans.iter().map(Span::width).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Patches the style of each Span in an existing Line, adding modifiers from the given style.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use ratatui::text::{Span, Line};
|
||||||
|
/// # use ratatui::style::{Color, Style, Modifier};
|
||||||
|
/// let style = Style::default().fg(Color::Yellow).add_modifier(Modifier::ITALIC);
|
||||||
|
/// let mut raw_line = Line::from(vec![
|
||||||
|
/// Span::raw("My"),
|
||||||
|
/// Span::raw(" text"),
|
||||||
|
/// ]);
|
||||||
|
/// let mut styled_line = Line::from(vec![
|
||||||
|
/// Span::styled("My", style),
|
||||||
|
/// Span::styled(" text", style),
|
||||||
|
/// ]);
|
||||||
|
///
|
||||||
|
/// assert_ne!(raw_line, styled_line);
|
||||||
|
///
|
||||||
|
/// raw_line.patch_style(style);
|
||||||
|
/// assert_eq!(raw_line, styled_line);
|
||||||
|
/// ```
|
||||||
|
pub fn patch_style(&mut self, style: Style) {
|
||||||
|
for span in &mut self.spans {
|
||||||
|
span.patch_style(style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resets the style of each Span in the Line.
|
||||||
|
/// Equivalent to calling `patch_style(Style::reset())`.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use ratatui::text::{Span, Line};
|
||||||
|
/// # use ratatui::style::{Color, Style, Modifier};
|
||||||
|
/// let mut line = Line::from(vec![
|
||||||
|
/// Span::styled("My", Style::default().fg(Color::Yellow)),
|
||||||
|
/// Span::styled(" text", Style::default().add_modifier(Modifier::BOLD)),
|
||||||
|
/// ]);
|
||||||
|
///
|
||||||
|
/// line.reset_style();
|
||||||
|
/// assert_eq!(Style::reset(), line.spans[0].style);
|
||||||
|
/// assert_eq!(Style::reset(), line.spans[1].style);
|
||||||
|
/// ```
|
||||||
|
pub fn reset_style(&mut self) {
|
||||||
|
for span in &mut self.spans {
|
||||||
|
span.reset_style();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<String> for Line<'a> {
|
||||||
|
fn from(s: String) -> Self {
|
||||||
|
Self::from(vec![Span::from(s)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a str> for Line<'a> {
|
||||||
|
fn from(s: &'a str) -> Self {
|
||||||
|
Self::from(vec![Span::from(s)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<Vec<Span<'a>>> for Line<'a> {
|
||||||
|
fn from(spans: Vec<Span<'a>>) -> Self {
|
||||||
|
Self { spans }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<Span<'a>> for Line<'a> {
|
||||||
|
fn from(span: Span<'a>) -> Self {
|
||||||
|
Self::from(vec![span])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<Line<'a>> for String {
|
||||||
|
fn from(line: Line<'a>) -> String {
|
||||||
|
line.spans.iter().fold(String::new(), |mut acc, s| {
|
||||||
|
acc.push_str(s.content.as_ref());
|
||||||
|
acc
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<Spans<'a>> for Line<'a> {
|
||||||
|
fn from(value: Spans<'a>) -> Self {
|
||||||
|
Self::from(value.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::style::{Color, Modifier, Style};
|
||||||
|
use crate::text::{Line, Span, Spans};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_width() {
|
||||||
|
let line = Line::from(vec![
|
||||||
|
Span::styled("My", Style::default().fg(Color::Yellow)),
|
||||||
|
Span::raw(" text"),
|
||||||
|
]);
|
||||||
|
assert_eq!(7, line.width());
|
||||||
|
|
||||||
|
let empty_line = Line::default();
|
||||||
|
assert_eq!(0, empty_line.width());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_patch_style() {
|
||||||
|
let style = Style::default()
|
||||||
|
.fg(Color::Yellow)
|
||||||
|
.add_modifier(Modifier::ITALIC);
|
||||||
|
let mut raw_line = Line::from(vec![Span::raw("My"), Span::raw(" text")]);
|
||||||
|
let styled_line = Line::from(vec![
|
||||||
|
Span::styled("My", style),
|
||||||
|
Span::styled(" text", style),
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_ne!(raw_line, styled_line);
|
||||||
|
|
||||||
|
raw_line.patch_style(style);
|
||||||
|
assert_eq!(raw_line, styled_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_reset_style() {
|
||||||
|
let mut line = Line::from(vec![
|
||||||
|
Span::styled("My", Style::default().fg(Color::Yellow)),
|
||||||
|
Span::styled(" text", Style::default().add_modifier(Modifier::BOLD)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
line.reset_style();
|
||||||
|
assert_eq!(Style::reset(), line.spans[0].style);
|
||||||
|
assert_eq!(Style::reset(), line.spans[1].style);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_string() {
|
||||||
|
let s = String::from("Hello, world!");
|
||||||
|
let line = Line::from(s);
|
||||||
|
assert_eq!(vec![Span::from("Hello, world!")], line.spans);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_str() {
|
||||||
|
let s = "Hello, world!";
|
||||||
|
let line = Line::from(s);
|
||||||
|
assert_eq!(vec![Span::from("Hello, world!")], line.spans);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_vec() {
|
||||||
|
let spans = vec![
|
||||||
|
Span::styled("Hello,", Style::default().fg(Color::Red)),
|
||||||
|
Span::styled(" world!", Style::default().fg(Color::Green)),
|
||||||
|
];
|
||||||
|
let line = Line::from(spans.clone());
|
||||||
|
assert_eq!(spans, line.spans);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_span() {
|
||||||
|
let span = Span::styled("Hello, world!", Style::default().fg(Color::Yellow));
|
||||||
|
let line = Line::from(span.clone());
|
||||||
|
assert_eq!(vec![span], line.spans);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_spans() {
|
||||||
|
let spans = vec![
|
||||||
|
Span::styled("Hello,", Style::default().fg(Color::Red)),
|
||||||
|
Span::styled(" world!", Style::default().fg(Color::Green)),
|
||||||
|
];
|
||||||
|
assert_eq!(Line::from(Spans::from(spans.clone())), Line::from(spans));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_into_string() {
|
||||||
|
let line = Line::from(vec![
|
||||||
|
Span::styled("Hello,", Style::default().fg(Color::Red)),
|
||||||
|
Span::styled(" world!", Style::default().fg(Color::Green)),
|
||||||
|
]);
|
||||||
|
let s: String = line.into();
|
||||||
|
assert_eq!("Hello, world!", s);
|
||||||
|
}
|
||||||
|
}
|
127
src/text/masked.rs
Normal file
127
src/text/masked.rs
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
|
fmt::{self, Debug, Display},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::Text;
|
||||||
|
|
||||||
|
/// A wrapper around a string that is masked when displayed.
|
||||||
|
///
|
||||||
|
/// The masked string is displayed as a series of the same character.
|
||||||
|
/// This might be used to display a password field or similar secure data.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use ratatui::{buffer::Buffer, layout::Rect, text::Masked, widgets::{Paragraph, Widget}};
|
||||||
|
///
|
||||||
|
/// let mut buffer = Buffer::empty(Rect::new(0, 0, 5, 1));
|
||||||
|
/// let password = Masked::new("12345", 'x');
|
||||||
|
///
|
||||||
|
/// Paragraph::new(password).render(buffer.area, &mut buffer);
|
||||||
|
/// assert_eq!(buffer, Buffer::with_lines(vec!["xxxxx"]));
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Masked<'a> {
|
||||||
|
inner: Cow<'a, str>,
|
||||||
|
mask_char: char,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Masked<'a> {
|
||||||
|
pub fn new(s: impl Into<Cow<'a, str>>, mask_char: char) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: s.into(),
|
||||||
|
mask_char,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The character to use for masking.
|
||||||
|
pub fn mask_char(&self) -> char {
|
||||||
|
self.mask_char
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The underlying string, with all characters masked.
|
||||||
|
pub fn value(&self) -> Cow<'a, str> {
|
||||||
|
self.inner.chars().map(|_| self.mask_char).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Masked<'_> {
|
||||||
|
/// Debug representation of a masked string is the underlying string
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str(&self.inner).map_err(|_| fmt::Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Masked<'_> {
|
||||||
|
/// Display representation of a masked string is the masked string
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str(&self.value()).map_err(|_| fmt::Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Masked<'a>> for Cow<'a, str> {
|
||||||
|
fn from(masked: &'a Masked) -> Cow<'a, str> {
|
||||||
|
masked.value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<Masked<'a>> for Cow<'a, str> {
|
||||||
|
fn from(masked: Masked<'a>) -> Cow<'a, str> {
|
||||||
|
masked.value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Masked<'_>> for Text<'a> {
|
||||||
|
fn from(masked: &'a Masked) -> Text<'a> {
|
||||||
|
Text::raw(masked.value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<Masked<'a>> for Text<'a> {
|
||||||
|
fn from(masked: Masked<'a>) -> Text<'a> {
|
||||||
|
Text::raw(masked.value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::text::Line;
|
||||||
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_masked_value() {
|
||||||
|
let masked = Masked::new("12345", 'x');
|
||||||
|
assert_eq!(masked.value(), "xxxxx");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_masked_debug() {
|
||||||
|
let masked = Masked::new("12345", 'x');
|
||||||
|
assert_eq!(format!("{masked:?}"), "12345");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_masked_display() {
|
||||||
|
let masked = Masked::new("12345", 'x');
|
||||||
|
assert_eq!(format!("{masked}"), "xxxxx");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_masked_conversions() {
|
||||||
|
let masked = Masked::new("12345", 'x');
|
||||||
|
|
||||||
|
let text: Text = masked.borrow().into();
|
||||||
|
assert_eq!(text.lines, vec![Line::from("xxxxx")]);
|
||||||
|
|
||||||
|
let text: Text = masked.to_owned().into();
|
||||||
|
assert_eq!(text.lines, vec![Line::from("xxxxx")]);
|
||||||
|
|
||||||
|
let cow: Cow<str> = masked.borrow().into();
|
||||||
|
assert_eq!(cow, "xxxxx");
|
||||||
|
|
||||||
|
let cow: Cow<str> = masked.to_owned().into();
|
||||||
|
assert_eq!(cow, "xxxxx");
|
||||||
|
}
|
||||||
|
}
|
203
src/text/spans.rs
Normal file
203
src/text/spans.rs
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
#![allow(deprecated)]
|
||||||
|
use super::{Span, Style};
|
||||||
|
|
||||||
|
/// A string composed of clusters of graphemes, each with their own style.
|
||||||
|
///
|
||||||
|
/// `Spans` has been deprecated in favor of `Line`, and will be removed in the
|
||||||
|
/// future. All methods that accept Spans have been replaced with methods that
|
||||||
|
/// accept Into<Line<'a>> (which is implemented on `Spans`) to allow users of
|
||||||
|
/// this crate to gradually transition to Line.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default, Eq)]
|
||||||
|
#[deprecated(note = "Use `ratatui::text::Line` instead")]
|
||||||
|
pub struct Spans<'a>(pub Vec<Span<'a>>);
|
||||||
|
|
||||||
|
impl<'a> Spans<'a> {
|
||||||
|
/// Returns the width of the underlying string.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use ratatui::text::{Span, Spans};
|
||||||
|
/// # use ratatui::style::{Color, Style};
|
||||||
|
/// let spans = Spans::from(vec![
|
||||||
|
/// Span::styled("My", Style::default().fg(Color::Yellow)),
|
||||||
|
/// Span::raw(" text"),
|
||||||
|
/// ]);
|
||||||
|
/// assert_eq!(7, spans.width());
|
||||||
|
/// ```
|
||||||
|
pub fn width(&self) -> usize {
|
||||||
|
self.0.iter().map(Span::width).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Patches the style of each Span in an existing Spans, adding modifiers from the given style.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use ratatui::text::{Span, Spans};
|
||||||
|
/// # use ratatui::style::{Color, Style, Modifier};
|
||||||
|
/// let style = Style::default().fg(Color::Yellow).add_modifier(Modifier::ITALIC);
|
||||||
|
/// let mut raw_spans = Spans::from(vec![
|
||||||
|
/// Span::raw("My"),
|
||||||
|
/// Span::raw(" text"),
|
||||||
|
/// ]);
|
||||||
|
/// let mut styled_spans = Spans::from(vec![
|
||||||
|
/// Span::styled("My", style),
|
||||||
|
/// Span::styled(" text", style),
|
||||||
|
/// ]);
|
||||||
|
///
|
||||||
|
/// assert_ne!(raw_spans, styled_spans);
|
||||||
|
///
|
||||||
|
/// raw_spans.patch_style(style);
|
||||||
|
/// assert_eq!(raw_spans, styled_spans);
|
||||||
|
/// ```
|
||||||
|
pub fn patch_style(&mut self, style: Style) {
|
||||||
|
for span in &mut self.0 {
|
||||||
|
span.patch_style(style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resets the style of each Span in the Spans.
|
||||||
|
/// Equivalent to calling `patch_style(Style::reset())`.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use ratatui::text::{Span, Spans};
|
||||||
|
/// # use ratatui::style::{Color, Style, Modifier};
|
||||||
|
/// let mut spans = Spans::from(vec![
|
||||||
|
/// Span::styled("My", Style::default().fg(Color::Yellow)),
|
||||||
|
/// Span::styled(" text", Style::default().add_modifier(Modifier::BOLD)),
|
||||||
|
/// ]);
|
||||||
|
///
|
||||||
|
/// spans.reset_style();
|
||||||
|
/// assert_eq!(Style::reset(), spans.0[0].style);
|
||||||
|
/// assert_eq!(Style::reset(), spans.0[1].style);
|
||||||
|
/// ```
|
||||||
|
pub fn reset_style(&mut self) {
|
||||||
|
for span in &mut self.0 {
|
||||||
|
span.reset_style();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<String> for Spans<'a> {
|
||||||
|
fn from(s: String) -> Spans<'a> {
|
||||||
|
Spans(vec![Span::from(s)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a str> for Spans<'a> {
|
||||||
|
fn from(s: &'a str) -> Spans<'a> {
|
||||||
|
Spans(vec![Span::from(s)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<Vec<Span<'a>>> for Spans<'a> {
|
||||||
|
fn from(spans: Vec<Span<'a>>) -> Spans<'a> {
|
||||||
|
Spans(spans)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<Span<'a>> for Spans<'a> {
|
||||||
|
fn from(span: Span<'a>) -> Spans<'a> {
|
||||||
|
Spans(vec![span])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<Spans<'a>> for String {
|
||||||
|
fn from(line: Spans<'a>) -> String {
|
||||||
|
line.0.iter().fold(String::new(), |mut acc, s| {
|
||||||
|
acc.push_str(s.content.as_ref());
|
||||||
|
acc
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::style::{Color, Modifier, Style};
|
||||||
|
use crate::text::{Span, Spans};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_width() {
|
||||||
|
let spans = Spans::from(vec![
|
||||||
|
Span::styled("My", Style::default().fg(Color::Yellow)),
|
||||||
|
Span::raw(" text"),
|
||||||
|
]);
|
||||||
|
assert_eq!(7, spans.width());
|
||||||
|
|
||||||
|
let empty_spans = Spans::default();
|
||||||
|
assert_eq!(0, empty_spans.width());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_patch_style() {
|
||||||
|
let style = Style::default()
|
||||||
|
.fg(Color::Yellow)
|
||||||
|
.add_modifier(Modifier::ITALIC);
|
||||||
|
let mut raw_spans = Spans::from(vec![Span::raw("My"), Span::raw(" text")]);
|
||||||
|
let styled_spans = Spans::from(vec![
|
||||||
|
Span::styled("My", style),
|
||||||
|
Span::styled(" text", style),
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_ne!(raw_spans, styled_spans);
|
||||||
|
|
||||||
|
raw_spans.patch_style(style);
|
||||||
|
assert_eq!(raw_spans, styled_spans);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_reset_style() {
|
||||||
|
let mut spans = Spans::from(vec![
|
||||||
|
Span::styled("My", Style::default().fg(Color::Yellow)),
|
||||||
|
Span::styled(" text", Style::default().add_modifier(Modifier::BOLD)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
spans.reset_style();
|
||||||
|
assert_eq!(Style::reset(), spans.0[0].style);
|
||||||
|
assert_eq!(Style::reset(), spans.0[1].style);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_string() {
|
||||||
|
let s = String::from("Hello, world!");
|
||||||
|
let spans = Spans::from(s);
|
||||||
|
assert_eq!(vec![Span::from("Hello, world!")], spans.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_str() {
|
||||||
|
let s = "Hello, world!";
|
||||||
|
let spans = Spans::from(s);
|
||||||
|
assert_eq!(vec![Span::from("Hello, world!")], spans.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_vec() {
|
||||||
|
let spans_vec = vec![
|
||||||
|
Span::styled("Hello,", Style::default().fg(Color::Red)),
|
||||||
|
Span::styled(" world!", Style::default().fg(Color::Green)),
|
||||||
|
];
|
||||||
|
let spans = Spans::from(spans_vec.clone());
|
||||||
|
assert_eq!(spans_vec, spans.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_span() {
|
||||||
|
let span = Span::styled("Hello, world!", Style::default().fg(Color::Yellow));
|
||||||
|
let spans = Spans::from(span.clone());
|
||||||
|
assert_eq!(vec![span], spans.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_into_string() {
|
||||||
|
let spans = Spans::from(vec![
|
||||||
|
Span::styled("Hello,", Style::default().fg(Color::Red)),
|
||||||
|
Span::styled(" world!", Style::default().fg(Color::Green)),
|
||||||
|
]);
|
||||||
|
let s: String = spans.into();
|
||||||
|
assert_eq!("Hello, world!", s);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ use crate::{
|
||||||
layout::{Alignment, Rect},
|
layout::{Alignment, Rect},
|
||||||
style::Style,
|
style::Style,
|
||||||
symbols::line,
|
symbols::line,
|
||||||
text::{Span, Spans},
|
text::{Line, Span},
|
||||||
widgets::{Borders, Widget},
|
widgets::{Borders, Widget},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ impl Padding {
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Block<'a> {
|
pub struct Block<'a> {
|
||||||
/// Optional title place on the upper left of the block
|
/// Optional title place on the upper left of the block
|
||||||
title: Option<Spans<'a>>,
|
title: Option<Line<'a>>,
|
||||||
/// Title alignment. The default is top left of the block, but one can choose to place
|
/// Title alignment. The default is top left of the block, but one can choose to place
|
||||||
/// title in the top middle, or top right of the block
|
/// title in the top middle, or top right of the block
|
||||||
title_alignment: Alignment,
|
title_alignment: Alignment,
|
||||||
|
@ -136,7 +136,7 @@ impl<'a> Default for Block<'a> {
|
||||||
impl<'a> Block<'a> {
|
impl<'a> Block<'a> {
|
||||||
pub fn title<T>(mut self, title: T) -> Block<'a>
|
pub fn title<T>(mut self, title: T) -> Block<'a>
|
||||||
where
|
where
|
||||||
T: Into<Spans<'a>>,
|
T: Into<Line<'a>>,
|
||||||
{
|
{
|
||||||
self.title = Some(title.into());
|
self.title = Some(title.into());
|
||||||
self
|
self
|
||||||
|
@ -144,12 +144,12 @@ impl<'a> Block<'a> {
|
||||||
|
|
||||||
#[deprecated(
|
#[deprecated(
|
||||||
since = "0.10.0",
|
since = "0.10.0",
|
||||||
note = "You should use styling capabilities of `text::Spans` given as argument of the `title` method to apply styling to the title."
|
note = "You should use styling capabilities of `text::Line` given as argument of the `title` method to apply styling to the title."
|
||||||
)]
|
)]
|
||||||
pub fn title_style(mut self, style: Style) -> Block<'a> {
|
pub fn title_style(mut self, style: Style) -> Block<'a> {
|
||||||
if let Some(t) = self.title {
|
if let Some(t) = self.title {
|
||||||
let title = String::from(t);
|
let title = String::from(t);
|
||||||
self.title = Some(Spans::from(Span::styled(title, style)));
|
self.title = Some(Line::from(Span::styled(title, style)));
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -337,7 +337,7 @@ impl<'a> Widget for Block<'a> {
|
||||||
area.top()
|
area.top()
|
||||||
};
|
};
|
||||||
|
|
||||||
buf.set_spans(title_x, title_y, &title, title_area_width);
|
buf.set_line(title_x, title_y, &title, title_area_width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
style::Style,
|
style::Style,
|
||||||
text::{Span, Spans},
|
text::Span,
|
||||||
widgets::{Block, Widget},
|
widgets::{Block, Widget},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ impl<'a, S: DateStyler> Widget for Monthly<'a, S> {
|
||||||
);
|
);
|
||||||
// cal is 21 cells wide, so hard code the 11
|
// cal is 21 cells wide, so hard code the 11
|
||||||
let x_off = 11_u16.saturating_sub(line.width() as u16 / 2);
|
let x_off = 11_u16.saturating_sub(line.width() as u16 / 2);
|
||||||
buf.set_spans(area.x + x_off, area.y, &line.into(), area.width);
|
buf.set_line(area.x + x_off, area.y, &line.into(), area.width);
|
||||||
area.y += 1
|
area.y += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,19 +146,19 @@ impl<'a, S: DateStyler> Widget for Monthly<'a, S> {
|
||||||
|
|
||||||
// go through all the weeks containing a day in the target month.
|
// go through all the weeks containing a day in the target month.
|
||||||
while curr_day.month() as u8 != self.display_date.month().next() as u8 {
|
while curr_day.month() as u8 != self.display_date.month().next() as u8 {
|
||||||
let mut line = Spans(Vec::with_capacity(14));
|
let mut spans = Vec::with_capacity(14);
|
||||||
for i in 0..7 {
|
for i in 0..7 {
|
||||||
// Draw the gutter. Do it here so we can avoid worrying about
|
// Draw the gutter. Do it here so we can avoid worrying about
|
||||||
// styling the ' ' in the format_date method
|
// styling the ' ' in the format_date method
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
line.0.push(Span::styled(" ", Style::default()));
|
spans.push(Span::styled(" ", Style::default()));
|
||||||
} else {
|
} else {
|
||||||
line.0.push(Span::styled(" ", self.default_bg()));
|
spans.push(Span::styled(" ", self.default_bg()));
|
||||||
}
|
}
|
||||||
line.0.push(self.format_date(curr_day));
|
spans.push(self.format_date(curr_day));
|
||||||
curr_day += Duration::DAY;
|
curr_day += Duration::DAY;
|
||||||
}
|
}
|
||||||
buf.set_spans(area.x, area.y, &line, area.width);
|
buf.set_line(area.x, area.y, &spans.into(), area.width);
|
||||||
area.y += 1;
|
area.y += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ use crate::{
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
style::{Color, Style},
|
style::{Color, Style},
|
||||||
symbols,
|
symbols,
|
||||||
text::Spans,
|
text::Line as TextLine,
|
||||||
widgets::{Block, Widget},
|
widgets::{Block, Widget},
|
||||||
};
|
};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
@ -31,7 +31,7 @@ pub trait Shape {
|
||||||
pub struct Label<'a> {
|
pub struct Label<'a> {
|
||||||
x: f64,
|
x: f64,
|
||||||
y: f64,
|
y: f64,
|
||||||
spans: Spans<'a>,
|
line: TextLine<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -299,14 +299,14 @@ impl<'a> Context<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Print a string on the canvas at the given position
|
/// Print a string on the canvas at the given position
|
||||||
pub fn print<T>(&mut self, x: f64, y: f64, spans: T)
|
pub fn print<T>(&mut self, x: f64, y: f64, line: T)
|
||||||
where
|
where
|
||||||
T: Into<Spans<'a>>,
|
T: Into<TextLine<'a>>,
|
||||||
{
|
{
|
||||||
self.labels.push(Label {
|
self.labels.push(Label {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
spans: spans.into(),
|
line: line.into(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,7 +510,7 @@ where
|
||||||
{
|
{
|
||||||
let x = ((label.x - left) * resolution.0 / width) as u16 + canvas_area.left();
|
let x = ((label.x - left) * resolution.0 / width) as u16 + canvas_area.left();
|
||||||
let y = ((top - label.y) * resolution.1 / height) as u16 + canvas_area.top();
|
let y = ((top - label.y) * resolution.1 / height) as u16 + canvas_area.top();
|
||||||
buf.set_spans(x, y, &label.spans, canvas_area.right() - x);
|
buf.set_line(x, y, &label.line, canvas_area.right() - x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
layout::{Constraint, Rect},
|
layout::{Constraint, Rect},
|
||||||
style::{Color, Style},
|
style::{Color, Style},
|
||||||
symbols,
|
symbols,
|
||||||
text::{Span, Spans},
|
text::{Line as TextLine, Span},
|
||||||
widgets::{
|
widgets::{
|
||||||
canvas::{Canvas, Line, Points},
|
canvas::{Canvas, Line, Points},
|
||||||
Block, Borders, Widget,
|
Block, Borders, Widget,
|
||||||
|
@ -19,7 +19,7 @@ use crate::{
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Axis<'a> {
|
pub struct Axis<'a> {
|
||||||
/// Title displayed next to axis end
|
/// Title displayed next to axis end
|
||||||
title: Option<Spans<'a>>,
|
title: Option<TextLine<'a>>,
|
||||||
/// Bounds for the axis (all data points outside these limits will not be represented)
|
/// Bounds for the axis (all data points outside these limits will not be represented)
|
||||||
bounds: [f64; 2],
|
bounds: [f64; 2],
|
||||||
/// A list of labels to put to the left or below the axis
|
/// A list of labels to put to the left or below the axis
|
||||||
|
@ -45,7 +45,7 @@ impl<'a> Default for Axis<'a> {
|
||||||
impl<'a> Axis<'a> {
|
impl<'a> Axis<'a> {
|
||||||
pub fn title<T>(mut self, title: T) -> Axis<'a>
|
pub fn title<T>(mut self, title: T) -> Axis<'a>
|
||||||
where
|
where
|
||||||
T: Into<Spans<'a>>,
|
T: Into<TextLine<'a>>,
|
||||||
{
|
{
|
||||||
self.title = Some(title.into());
|
self.title = Some(title.into());
|
||||||
self
|
self
|
||||||
|
@ -53,12 +53,12 @@ impl<'a> Axis<'a> {
|
||||||
|
|
||||||
#[deprecated(
|
#[deprecated(
|
||||||
since = "0.10.0",
|
since = "0.10.0",
|
||||||
note = "You should use styling capabilities of `text::Spans` given as argument of the `title` method to apply styling to the title."
|
note = "You should use styling capabilities of `text::Line` given as argument of the `title` method to apply styling to the title."
|
||||||
)]
|
)]
|
||||||
pub fn title_style(mut self, style: Style) -> Axis<'a> {
|
pub fn title_style(mut self, style: Style) -> Axis<'a> {
|
||||||
if let Some(t) = self.title {
|
if let Some(t) = self.title {
|
||||||
let title = String::from(t);
|
let title = String::from(t);
|
||||||
self.title = Some(Spans::from(Span::styled(title, style)));
|
self.title = Some(TextLine::from(Span::styled(title, style)));
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -597,7 +597,7 @@ impl<'a> Widget for Chart<'a> {
|
||||||
},
|
},
|
||||||
original_style,
|
original_style,
|
||||||
);
|
);
|
||||||
buf.set_spans(x, y, &title, width);
|
buf.set_line(x, y, &title, width);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((x, y)) = layout.title_y {
|
if let Some((x, y)) = layout.title_y {
|
||||||
|
@ -612,7 +612,7 @@ impl<'a> Widget for Chart<'a> {
|
||||||
},
|
},
|
||||||
original_style,
|
original_style,
|
||||||
);
|
);
|
||||||
buf.set_spans(x, y, &title, width);
|
buf.set_line(x, y, &title, width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
style::{Color, Style},
|
style::{Color, Style},
|
||||||
symbols,
|
symbols,
|
||||||
text::{Span, Spans},
|
text::{Line, Span},
|
||||||
widgets::{Block, Widget},
|
widgets::{Block, Widget},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ impl<'a> Widget for Gauge<'a> {
|
||||||
.set_symbol(get_unicode_block(filled_width % 1.0));
|
.set_symbol(get_unicode_block(filled_width % 1.0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// set the span
|
// set the line
|
||||||
buf.set_span(label_col, label_row, &label, clamped_label_width);
|
buf.set_span(label_col, label_row, &label, clamped_label_width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,7 +174,7 @@ fn get_unicode_block<'a>(frac: f64) -> &'a str {
|
||||||
pub struct LineGauge<'a> {
|
pub struct LineGauge<'a> {
|
||||||
block: Option<Block<'a>>,
|
block: Option<Block<'a>>,
|
||||||
ratio: f64,
|
ratio: f64,
|
||||||
label: Option<Spans<'a>>,
|
label: Option<Line<'a>>,
|
||||||
line_set: symbols::line::Set,
|
line_set: symbols::line::Set,
|
||||||
style: Style,
|
style: Style,
|
||||||
gauge_style: Style,
|
gauge_style: Style,
|
||||||
|
@ -215,7 +215,7 @@ impl<'a> LineGauge<'a> {
|
||||||
|
|
||||||
pub fn label<T>(mut self, label: T) -> Self
|
pub fn label<T>(mut self, label: T) -> Self
|
||||||
where
|
where
|
||||||
T: Into<Spans<'a>>,
|
T: Into<Line<'a>>,
|
||||||
{
|
{
|
||||||
self.label = Some(label.into());
|
self.label = Some(label.into());
|
||||||
self
|
self
|
||||||
|
@ -251,8 +251,8 @@ impl<'a> Widget for LineGauge<'a> {
|
||||||
let ratio = self.ratio;
|
let ratio = self.ratio;
|
||||||
let label = self
|
let label = self
|
||||||
.label
|
.label
|
||||||
.unwrap_or_else(move || Spans::from(format!("{:.0}%", ratio * 100.0)));
|
.unwrap_or_else(move || Line::from(format!("{:.0}%", ratio * 100.0)));
|
||||||
let (col, row) = buf.set_spans(
|
let (col, row) = buf.set_line(
|
||||||
gauge_area.left(),
|
gauge_area.left(),
|
||||||
gauge_area.top(),
|
gauge_area.top(),
|
||||||
&label,
|
&label,
|
||||||
|
|
|
@ -269,7 +269,7 @@ impl<'a> StatefulWidget for List<'a> {
|
||||||
} else {
|
} else {
|
||||||
(x, list_area.width)
|
(x, list_area.width)
|
||||||
};
|
};
|
||||||
buf.set_spans(elem_x, y + j as u16, line, max_element_width);
|
buf.set_line(elem_x, y + j as u16, line, max_element_width);
|
||||||
}
|
}
|
||||||
if is_selected {
|
if is_selected {
|
||||||
buf.set_style(area, self.highlight_style);
|
buf.set_style(area, self.highlight_style);
|
||||||
|
@ -292,7 +292,7 @@ mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
assert_buffer_eq,
|
assert_buffer_eq,
|
||||||
style::Color,
|
style::Color,
|
||||||
text::{Span, Spans},
|
text::{Line, Span},
|
||||||
widgets::{Borders, StatefulWidget, Widget},
|
widgets::{Borders, StatefulWidget, Widget},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -356,7 +356,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_list_item_new_from_spans() {
|
fn test_list_item_new_from_spans() {
|
||||||
let spans = Spans::from(vec![
|
let spans = Line::from(vec![
|
||||||
Span::styled("Test ", Style::default().fg(Color::Blue)),
|
Span::styled("Test ", Style::default().fg(Color::Blue)),
|
||||||
Span::styled("item", Style::default().fg(Color::Red)),
|
Span::styled("item", Style::default().fg(Color::Red)),
|
||||||
]);
|
]);
|
||||||
|
@ -368,11 +368,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_list_item_new_from_vec_spans() {
|
fn test_list_item_new_from_vec_spans() {
|
||||||
let lines = vec![
|
let lines = vec![
|
||||||
Spans::from(vec![
|
Line::from(vec![
|
||||||
Span::styled("Test ", Style::default().fg(Color::Blue)),
|
Span::styled("Test ", Style::default().fg(Color::Blue)),
|
||||||
Span::styled("item", Style::default().fg(Color::Red)),
|
Span::styled("item", Style::default().fg(Color::Red)),
|
||||||
]),
|
]),
|
||||||
Spans::from(vec![
|
Line::from(vec![
|
||||||
Span::styled("Second ", Style::default().fg(Color::Green)),
|
Span::styled("Second ", Style::default().fg(Color::Green)),
|
||||||
Span::styled("line", Style::default().fg(Color::Yellow)),
|
Span::styled("line", Style::default().fg(Color::Yellow)),
|
||||||
]),
|
]),
|
||||||
|
|
|
@ -24,17 +24,17 @@ fn get_line_offset(line_width: u16, text_area_width: u16, alignment: Alignment)
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use ratatui::text::{Text, Spans, Span};
|
/// # use ratatui::text::{Text, Line, Span};
|
||||||
/// # use ratatui::widgets::{Block, Borders, Paragraph, Wrap};
|
/// # use ratatui::widgets::{Block, Borders, Paragraph, Wrap};
|
||||||
/// # use ratatui::style::{Style, Color, Modifier};
|
/// # use ratatui::style::{Style, Color, Modifier};
|
||||||
/// # use ratatui::layout::{Alignment};
|
/// # use ratatui::layout::{Alignment};
|
||||||
/// let text = vec![
|
/// let text = vec![
|
||||||
/// Spans::from(vec![
|
/// Line::from(vec![
|
||||||
/// Span::raw("First"),
|
/// Span::raw("First"),
|
||||||
/// Span::styled("line",Style::default().add_modifier(Modifier::ITALIC)),
|
/// Span::styled("line",Style::default().add_modifier(Modifier::ITALIC)),
|
||||||
/// Span::raw("."),
|
/// Span::raw("."),
|
||||||
/// ]),
|
/// ]),
|
||||||
/// Spans::from(Span::styled("Second line", Style::default().fg(Color::Red))),
|
/// Line::from(Span::styled("Second line", Style::default().fg(Color::Red))),
|
||||||
/// ];
|
/// ];
|
||||||
/// Paragraph::new(text)
|
/// Paragraph::new(text)
|
||||||
/// .block(Block::default().title("Paragraph").borders(Borders::ALL))
|
/// .block(Block::default().title("Paragraph").borders(Borders::ALL))
|
||||||
|
@ -149,9 +149,8 @@ impl<'a> Widget for Paragraph<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let style = self.style;
|
let style = self.style;
|
||||||
let mut styled = self.text.lines.iter().flat_map(|spans| {
|
let mut styled = self.text.lines.iter().flat_map(|line| {
|
||||||
spans
|
line.spans
|
||||||
.0
|
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|span| span.styled_graphemes(style))
|
.flat_map(|span| span.styled_graphemes(style))
|
||||||
// Required given the way composers work but might be refactored out if we change
|
// Required given the way composers work but might be refactored out if we change
|
||||||
|
@ -205,7 +204,7 @@ mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
style::Color,
|
style::Color,
|
||||||
text::{Span, Spans},
|
text::{Line, Span},
|
||||||
widgets::Borders,
|
widgets::Borders,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -386,7 +385,7 @@ mod test {
|
||||||
fn test_render_paragraph_with_styled_text() {
|
fn test_render_paragraph_with_styled_text() {
|
||||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 13, 1));
|
let mut buffer = Buffer::empty(Rect::new(0, 0, 13, 1));
|
||||||
|
|
||||||
Paragraph::new(Spans::from(vec![
|
Paragraph::new(Line::from(vec![
|
||||||
Span::styled("Hello, ", Style::default().fg(Color::Red)),
|
Span::styled("Hello, ", Style::default().fg(Color::Red)),
|
||||||
Span::styled("world!", Style::default().fg(Color::Blue)),
|
Span::styled("world!", Style::default().fg(Color::Blue)),
|
||||||
]))
|
]))
|
||||||
|
|
|
@ -13,13 +13,13 @@ use unicode_width::UnicodeWidthStr;
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ratatui::widgets::Cell;
|
/// # use ratatui::widgets::Cell;
|
||||||
/// # use ratatui::style::{Style, Modifier};
|
/// # use ratatui::style::{Style, Modifier};
|
||||||
/// # use ratatui::text::{Span, Spans, Text};
|
/// # use ratatui::text::{Span, Line, Text};
|
||||||
/// # use std::borrow::Cow;
|
/// # use std::borrow::Cow;
|
||||||
/// Cell::from("simple string");
|
/// Cell::from("simple string");
|
||||||
///
|
///
|
||||||
/// Cell::from(Span::from("span"));
|
/// Cell::from(Span::from("span"));
|
||||||
///
|
///
|
||||||
/// Cell::from(Spans::from(vec![
|
/// Cell::from(Line::from(vec![
|
||||||
/// Span::raw("a vec of "),
|
/// Span::raw("a vec of "),
|
||||||
/// Span::styled("spans", Style::default().add_modifier(Modifier::BOLD))
|
/// Span::styled("spans", Style::default().add_modifier(Modifier::BOLD))
|
||||||
/// ]));
|
/// ]));
|
||||||
|
@ -142,7 +142,7 @@ impl<'a> Row<'a> {
|
||||||
/// # use ratatui::widgets::{Block, Borders, Table, Row, Cell};
|
/// # use ratatui::widgets::{Block, Borders, Table, Row, Cell};
|
||||||
/// # use ratatui::layout::Constraint;
|
/// # use ratatui::layout::Constraint;
|
||||||
/// # use ratatui::style::{Style, Color, Modifier};
|
/// # use ratatui::style::{Style, Color, Modifier};
|
||||||
/// # use ratatui::text::{Text, Spans, Span};
|
/// # use ratatui::text::{Text, Line, Span};
|
||||||
/// Table::new(vec![
|
/// Table::new(vec![
|
||||||
/// // Row can be created from simple strings.
|
/// // Row can be created from simple strings.
|
||||||
/// Row::new(vec!["Row11", "Row12", "Row13"]),
|
/// Row::new(vec!["Row11", "Row12", "Row13"]),
|
||||||
|
@ -152,7 +152,7 @@ impl<'a> Row<'a> {
|
||||||
/// Row::new(vec![
|
/// Row::new(vec![
|
||||||
/// Cell::from("Row31"),
|
/// Cell::from("Row31"),
|
||||||
/// Cell::from("Row32").style(Style::default().fg(Color::Yellow)),
|
/// Cell::from("Row32").style(Style::default().fg(Color::Yellow)),
|
||||||
/// Cell::from(Spans::from(vec![
|
/// Cell::from(Line::from(vec![
|
||||||
/// Span::raw("Row"),
|
/// Span::raw("Row"),
|
||||||
/// Span::styled("33", Style::default().fg(Color::Green))
|
/// Span::styled("33", Style::default().fg(Color::Green))
|
||||||
/// ])),
|
/// ])),
|
||||||
|
@ -477,11 +477,11 @@ impl<'a> StatefulWidget for Table<'a> {
|
||||||
|
|
||||||
fn render_cell(buf: &mut Buffer, cell: &Cell, area: Rect) {
|
fn render_cell(buf: &mut Buffer, cell: &Cell, area: Rect) {
|
||||||
buf.set_style(area, cell.style);
|
buf.set_style(area, cell.style);
|
||||||
for (i, spans) in cell.content.lines.iter().enumerate() {
|
for (i, line) in cell.content.lines.iter().enumerate() {
|
||||||
if i as u16 >= area.height {
|
if i as u16 >= area.height {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
buf.set_spans(area.x, area.y + i as u16, spans, area.width);
|
buf.set_line(area.x, area.y + i as u16, line, area.width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
style::Style,
|
style::Style,
|
||||||
symbols,
|
symbols,
|
||||||
text::{Span, Spans},
|
text::{Line, Span},
|
||||||
widgets::{Block, Widget},
|
widgets::{Block, Widget},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,9 +14,9 @@ use crate::{
|
||||||
/// ```
|
/// ```
|
||||||
/// # use ratatui::widgets::{Block, Borders, Tabs};
|
/// # use ratatui::widgets::{Block, Borders, Tabs};
|
||||||
/// # use ratatui::style::{Style, Color};
|
/// # use ratatui::style::{Style, Color};
|
||||||
/// # use ratatui::text::{Spans};
|
/// # use ratatui::text::{Line};
|
||||||
/// # use ratatui::symbols::{DOT};
|
/// # use ratatui::symbols::{DOT};
|
||||||
/// let titles = ["Tab1", "Tab2", "Tab3", "Tab4"].iter().cloned().map(Spans::from).collect();
|
/// let titles = ["Tab1", "Tab2", "Tab3", "Tab4"].iter().cloned().map(Line::from).collect();
|
||||||
/// Tabs::new(titles)
|
/// Tabs::new(titles)
|
||||||
/// .block(Block::default().title("Tabs").borders(Borders::ALL))
|
/// .block(Block::default().title("Tabs").borders(Borders::ALL))
|
||||||
/// .style(Style::default().fg(Color::White))
|
/// .style(Style::default().fg(Color::White))
|
||||||
|
@ -28,7 +28,7 @@ pub struct Tabs<'a> {
|
||||||
/// A block to wrap this widget in if necessary
|
/// A block to wrap this widget in if necessary
|
||||||
block: Option<Block<'a>>,
|
block: Option<Block<'a>>,
|
||||||
/// One title for each tab
|
/// One title for each tab
|
||||||
titles: Vec<Spans<'a>>,
|
titles: Vec<Line<'a>>,
|
||||||
/// The index of the selected tabs
|
/// The index of the selected tabs
|
||||||
selected: usize,
|
selected: usize,
|
||||||
/// The style used to draw the text
|
/// The style used to draw the text
|
||||||
|
@ -40,10 +40,13 @@ pub struct Tabs<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Tabs<'a> {
|
impl<'a> Tabs<'a> {
|
||||||
pub fn new(titles: Vec<Spans<'a>>) -> Tabs<'a> {
|
pub fn new<T>(titles: Vec<T>) -> Tabs<'a>
|
||||||
|
where
|
||||||
|
T: Into<Line<'a>>,
|
||||||
|
{
|
||||||
Tabs {
|
Tabs {
|
||||||
block: None,
|
block: None,
|
||||||
titles,
|
titles: titles.into_iter().map(Into::into).collect(),
|
||||||
selected: 0,
|
selected: 0,
|
||||||
style: Default::default(),
|
style: Default::default(),
|
||||||
highlight_style: Default::default(),
|
highlight_style: Default::default(),
|
||||||
|
@ -105,7 +108,7 @@ impl<'a> Widget for Tabs<'a> {
|
||||||
if remaining_width == 0 {
|
if remaining_width == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let pos = buf.set_spans(x, tabs_area.top(), &title, remaining_width);
|
let pos = buf.set_line(x, tabs_area.top(), &title, remaining_width);
|
||||||
if i == self.selected {
|
if i == self.selected {
|
||||||
buf.set_style(
|
buf.set_style(
|
||||||
Rect {
|
Rect {
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
|
#![allow(deprecated)]
|
||||||
|
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
backend::TestBackend,
|
backend::TestBackend,
|
||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
style::{Color, Style},
|
style::{Color, Style},
|
||||||
symbols,
|
symbols,
|
||||||
text::Spans,
|
text::Line,
|
||||||
widgets::{Block, Borders, List, ListItem, ListState},
|
widgets::{Block, Borders, List, ListItem, ListState},
|
||||||
Terminal,
|
Terminal,
|
||||||
};
|
};
|
||||||
|
@ -154,9 +156,9 @@ fn widgets_list_should_display_multiline_items() {
|
||||||
.draw(|f| {
|
.draw(|f| {
|
||||||
let size = f.size();
|
let size = f.size();
|
||||||
let items = vec![
|
let items = vec![
|
||||||
ListItem::new(vec![Spans::from("Item 1"), Spans::from("Item 1a")]),
|
ListItem::new(vec![Line::from("Item 1"), Line::from("Item 1a")]),
|
||||||
ListItem::new(vec![Spans::from("Item 2"), Spans::from("Item 2b")]),
|
ListItem::new(vec![Line::from("Item 2"), Line::from("Item 2b")]),
|
||||||
ListItem::new(vec![Spans::from("Item 3"), Spans::from("Item 3c")]),
|
ListItem::new(vec![Line::from("Item 3"), Line::from("Item 3c")]),
|
||||||
];
|
];
|
||||||
let list = List::new(items)
|
let list = List::new(items)
|
||||||
.highlight_style(Style::default().bg(Color::Yellow))
|
.highlight_style(Style::default().bg(Color::Yellow))
|
||||||
|
@ -189,9 +191,9 @@ fn widgets_list_should_repeat_highlight_symbol() {
|
||||||
.draw(|f| {
|
.draw(|f| {
|
||||||
let size = f.size();
|
let size = f.size();
|
||||||
let items = vec![
|
let items = vec![
|
||||||
ListItem::new(vec![Spans::from("Item 1"), Spans::from("Item 1a")]),
|
ListItem::new(vec![Line::from("Item 1"), Line::from("Item 1a")]),
|
||||||
ListItem::new(vec![Spans::from("Item 2"), Spans::from("Item 2b")]),
|
ListItem::new(vec![Line::from("Item 2"), Line::from("Item 2b")]),
|
||||||
ListItem::new(vec![Spans::from("Item 3"), Spans::from("Item 3c")]),
|
ListItem::new(vec![Line::from("Item 3"), Line::from("Item 3c")]),
|
||||||
];
|
];
|
||||||
let list = List::new(items)
|
let list = List::new(items)
|
||||||
.highlight_style(Style::default().bg(Color::Yellow))
|
.highlight_style(Style::default().bg(Color::Yellow))
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
#![allow(deprecated)]
|
||||||
|
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
backend::TestBackend,
|
backend::TestBackend,
|
||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
layout::Alignment,
|
layout::Alignment,
|
||||||
text::{Span, Spans, Text},
|
text::{Line, Span, Text},
|
||||||
widgets::{Block, Borders, Padding, Paragraph, Wrap},
|
widgets::{Block, Borders, Padding, Paragraph, Wrap},
|
||||||
Terminal,
|
Terminal,
|
||||||
};
|
};
|
||||||
|
@ -21,7 +23,7 @@ fn widgets_paragraph_can_wrap_its_content() {
|
||||||
terminal
|
terminal
|
||||||
.draw(|f| {
|
.draw(|f| {
|
||||||
let size = f.size();
|
let size = f.size();
|
||||||
let text = vec![Spans::from(SAMPLE_STRING)];
|
let text = vec![Line::from(SAMPLE_STRING)];
|
||||||
let paragraph = Paragraph::new(text)
|
let paragraph = Paragraph::new(text)
|
||||||
.block(Block::default().borders(Borders::ALL).padding(Padding {
|
.block(Block::default().borders(Borders::ALL).padding(Padding {
|
||||||
left: 2,
|
left: 2,
|
||||||
|
@ -99,7 +101,7 @@ fn widgets_paragraph_renders_double_width_graphemes() {
|
||||||
terminal
|
terminal
|
||||||
.draw(|f| {
|
.draw(|f| {
|
||||||
let size = f.size();
|
let size = f.size();
|
||||||
let text = vec![Spans::from(s)];
|
let text = vec![Line::from(s)];
|
||||||
let paragraph = Paragraph::new(text)
|
let paragraph = Paragraph::new(text)
|
||||||
.block(Block::default().borders(Borders::ALL))
|
.block(Block::default().borders(Borders::ALL))
|
||||||
.wrap(Wrap { trim: true });
|
.wrap(Wrap { trim: true });
|
||||||
|
@ -131,7 +133,7 @@ fn widgets_paragraph_renders_mixed_width_graphemes() {
|
||||||
terminal
|
terminal
|
||||||
.draw(|f| {
|
.draw(|f| {
|
||||||
let size = f.size();
|
let size = f.size();
|
||||||
let text = vec![Spans::from(s)];
|
let text = vec![Line::from(s)];
|
||||||
let paragraph = Paragraph::new(text)
|
let paragraph = Paragraph::new(text)
|
||||||
.block(Block::default().borders(Borders::ALL))
|
.block(Block::default().borders(Borders::ALL))
|
||||||
.wrap(Wrap { trim: true });
|
.wrap(Wrap { trim: true });
|
||||||
|
@ -155,7 +157,7 @@ fn widgets_paragraph_renders_mixed_width_graphemes() {
|
||||||
#[test]
|
#[test]
|
||||||
fn widgets_paragraph_can_wrap_with_a_trailing_nbsp() {
|
fn widgets_paragraph_can_wrap_with_a_trailing_nbsp() {
|
||||||
let nbsp: &str = "\u{00a0}";
|
let nbsp: &str = "\u{00a0}";
|
||||||
let line = Spans::from(vec![Span::raw("NBSP"), Span::raw(nbsp)]);
|
let line = Line::from(vec![Span::raw("NBSP"), Span::raw(nbsp)]);
|
||||||
let backend = TestBackend::new(20, 3);
|
let backend = TestBackend::new(20, 3);
|
||||||
let mut terminal = Terminal::new(backend).unwrap();
|
let mut terminal = Terminal::new(backend).unwrap();
|
||||||
let expected = Buffer::with_lines(vec![
|
let expected = Buffer::with_lines(vec![
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
#![allow(deprecated)]
|
||||||
|
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
backend::TestBackend,
|
backend::TestBackend,
|
||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
layout::Constraint,
|
layout::Constraint,
|
||||||
style::{Color, Modifier, Style},
|
style::{Color, Modifier, Style},
|
||||||
text::{Span, Spans},
|
text::{Line, Span},
|
||||||
widgets::{Block, Borders, Cell, Row, Table, TableState},
|
widgets::{Block, Borders, Cell, Row, Table, TableState},
|
||||||
Terminal,
|
Terminal,
|
||||||
};
|
};
|
||||||
|
@ -623,7 +625,7 @@ fn widgets_table_can_have_elements_styled_individually() {
|
||||||
Row::new(vec![
|
Row::new(vec![
|
||||||
Cell::from("Row21"),
|
Cell::from("Row21"),
|
||||||
Cell::from("Row22").style(Style::default().fg(Color::Yellow)),
|
Cell::from("Row22").style(Style::default().fg(Color::Yellow)),
|
||||||
Cell::from(Spans::from(vec![
|
Cell::from(Line::from(vec![
|
||||||
Span::raw("Row"),
|
Span::raw("Row"),
|
||||||
Span::styled("23", Style::default().fg(Color::Blue)),
|
Span::styled("23", Style::default().fg(Color::Blue)),
|
||||||
]))
|
]))
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
#![allow(deprecated)]
|
||||||
|
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
backend::TestBackend, buffer::Buffer, layout::Rect, symbols, text::Spans, widgets::Tabs,
|
backend::TestBackend, buffer::Buffer, layout::Rect, symbols, text::Line, widgets::Tabs,
|
||||||
Terminal,
|
Terminal,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,7 +11,7 @@ fn widgets_tabs_should_not_panic_on_narrow_areas() {
|
||||||
let mut terminal = Terminal::new(backend).unwrap();
|
let mut terminal = Terminal::new(backend).unwrap();
|
||||||
terminal
|
terminal
|
||||||
.draw(|f| {
|
.draw(|f| {
|
||||||
let tabs = Tabs::new(["Tab1", "Tab2"].iter().cloned().map(Spans::from).collect());
|
let tabs = Tabs::new(["Tab1", "Tab2"].iter().cloned().map(Line::from).collect());
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
tabs,
|
tabs,
|
||||||
Rect {
|
Rect {
|
||||||
|
@ -31,7 +33,7 @@ fn widgets_tabs_should_truncate_the_last_item() {
|
||||||
let mut terminal = Terminal::new(backend).unwrap();
|
let mut terminal = Terminal::new(backend).unwrap();
|
||||||
terminal
|
terminal
|
||||||
.draw(|f| {
|
.draw(|f| {
|
||||||
let tabs = Tabs::new(["Tab1", "Tab2"].iter().cloned().map(Spans::from).collect());
|
let tabs = Tabs::new(["Tab1", "Tab2"].iter().cloned().map(Line::from).collect());
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
tabs,
|
tabs,
|
||||||
Rect {
|
Rect {
|
||||||
|
|
Loading…
Reference in a new issue