Skip to content

Commit

Permalink
feat(widgets): Collect iterator of Line into Tabs
Browse files Browse the repository at this point in the history
A follow-up from ratatui#755,
allowing any iterator whose item is convertible into `Line` to be
collected into `Tabs`.
  • Loading branch information
Lunderberg committed Jan 9, 2024
1 parent 388aa46 commit 4beb7ea
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 6 deletions.
5 changes: 2 additions & 3 deletions examples/demo/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ use crate::app::App;

pub fn draw(f: &mut Frame, app: &mut App) {
let chunks = Layout::vertical([Constraint::Length(3), Constraint::Min(0)]).split(f.size());
let titles = app
let tabs = app
.tabs
.titles
.iter()
.map(|t| text::Line::from(Span::styled(*t, Style::default().fg(Color::Green))))
.collect();
let tabs = Tabs::new(titles)
.collect::<Tabs>()
.block(Block::default().borders(Borders::ALL).title(app.title))
.highlight_style(Style::default().fg(Color::Yellow))
.select(app.tabs.index);
Expand Down
5 changes: 2 additions & 3 deletions examples/tabs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,14 @@ fn ui(f: &mut Frame, app: &App) {

let block = Block::default().on_white().black();
f.render_widget(block, area);
let titles = app
let tabs = app
.titles
.iter()
.map(|t| {
let (first, rest) = t.split_at(1);
Line::from(vec![first.yellow(), rest.green()])
})
.collect();
let tabs = Tabs::new(titles)
.collect::<Tabs>()
.block(Block::default().borders(Borders::ALL).title("Tabs"))
.select(app.index)
.style(Style::default().cyan().on_gray())
Expand Down
44 changes: 44 additions & 0 deletions src/widgets/tabs.rs
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ const DEFAULT_HIGHLIGHT_STYLE: Style = Style::new().add_modifier(Modifier::REVER
/// .divider(symbols::DOT)
/// .padding("->", "<-");
/// ```
///
/// In addition to `Tabs::new`, any iterator whose element is convertible to `Line` can be collected
/// into `Tabs`.
///
/// ```
/// use ratatui::widgets::Tabs;
///
/// (0..5).map(|i| format!("Tab{i}")).collect::<Tabs>();
/// ```
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
pub struct Tabs<'a> {
/// A block to wrap this widget in if necessary
Expand Down Expand Up @@ -82,6 +91,12 @@ impl<'a> Tabs<'a> {
pub fn new<T>(titles: Vec<T>) -> Tabs<'a>
where
T: Into<Line<'a>>,
// The `Tabs::new` function could be made more flexible by accepting `Iter: IntoIterator`,
// with `Iter::Item: Into<Line<'a>>`. This may be desirable at some point, but would be a
// breaking change, as type inference could no longer use `Tabs::new` to infer the type
// into which an iterator is collected. For example, `Tabs::new((0..4).map(|i|
// format("Tab{i}")).collect())` would not compile, because the return type of `.collect()`
// could no longer be inferred to be `Vec<_>`.
{
Tabs {
block: None,
Expand Down Expand Up @@ -306,6 +321,15 @@ impl<'a> Widget for Tabs<'a> {
}
}

impl<'a, Item> FromIterator<Item> for Tabs<'a>
where
Item: Into<Line<'a>>,
{
fn from_iter<Iter: IntoIterator<Item = Item>>(iter: Iter) -> Self {
Self::new(iter.into_iter().map(Into::into).collect())
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -335,6 +359,26 @@ mod tests {
);
}

#[test]
fn new_from_vec_of_str() {
Tabs::new(vec!["a", "b"]);
}

#[test]
fn collect() {
let tabs: Tabs = (0..5).map(|i| format!("Tab{i}")).collect();
assert_eq!(
tabs.titles,
vec![
Line::from("Tab0"),
Line::from("Tab1"),
Line::from("Tab2"),
Line::from("Tab3"),
Line::from("Tab4"),
],
);
}

fn render(tabs: Tabs, area: Rect) -> Buffer {
let mut buffer = Buffer::empty(area);
tabs.render(area, &mut buffer);
Expand Down

0 comments on commit 4beb7ea

Please sign in to comment.