diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 6fb88bec4..fcb0cd623 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -15,6 +15,7 @@ This is a quick summary of the sections below: - Removed deprecated `Block::title_on_bottom` - `Line` now has an extra `style` field which applies the style to the entire line - `Block` style methods cannot be created in a const context + - `Tabs::new()` now accepts `IntoIterator>>` - [v0.25.0](#v0250) - Removed `Axis::title_style` and `Buffer::set_background` - `List::new()` now accepts `IntoIterator>>` @@ -46,6 +47,22 @@ This is a quick summary of the sections below: ## v0.26.0 (unreleased) +### `Tabs::new()` now accepts `IntoIterator>>` ([#776]) + +[#776]: https://github.com/ratatui-org/ratatui/pull/776 + +Previously, `Tabs::new()` accepted `Vec` where `T: Into>`. This allows more flexible +types from calling scopes, though it can break type inference when the calling scope. + +This typically occurs when collecting an iterator prior to calling `Tabs::new`, and can be resolved +by removing the call to `.collect()`. + +```diff +- let table = Tabs::new((0.3).map(|i| format!("{i}")).collect()); +// becomes ++ let table = Tabs::new((0.3).map(|i| format!("{i}"))); +``` + ### Table::default() now sets segment_size to None and column_spacing to ([#751]) [#751]: https://github.com/ratatui-org/ratatui/pull/751 diff --git a/examples/demo/ui.rs b/examples/demo/ui.rs index 81bdb7331..c623ec5bc 100644 --- a/examples/demo/ui.rs +++ b/examples/demo/ui.rs @@ -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::() .block(Block::default().borders(Borders::ALL).title(app.title)) .highlight_style(Style::default().fg(Color::Yellow)) .select(app.tabs.index); diff --git a/examples/tabs.rs b/examples/tabs.rs index 60f42191c..5a7aed207 100644 --- a/examples/tabs.rs +++ b/examples/tabs.rs @@ -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::() .block(Block::default().borders(Borders::ALL).title("Tabs")) .select(app.index) .style(Style::default().cyan().on_gray()) diff --git a/src/widgets/tabs.rs b/src/widgets/tabs.rs old mode 100644 new mode 100755 index e9c1aadaf..4546a1123 --- a/src/widgets/tabs.rs +++ b/src/widgets/tabs.rs @@ -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::(); +/// ``` #[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] pub struct Tabs<'a> { /// A block to wrap this widget in if necessary @@ -79,9 +88,10 @@ impl<'a> Tabs<'a> { /// # use ratatui::{prelude::*, widgets::Tabs}; /// let tabs = Tabs::new(vec!["Tab 1".red(), "Tab 2".blue()]); /// ``` - pub fn new(titles: Vec) -> Tabs<'a> + pub fn new(titles: Iter) -> Tabs<'a> where - T: Into>, + Iter: IntoIterator, + Iter::Item: Into>, { Tabs { block: None, @@ -306,6 +316,15 @@ impl<'a> Widget for Tabs<'a> { } } +impl<'a, Item> FromIterator for Tabs<'a> +where + Item: Into>, +{ + fn from_iter>(iter: Iter) -> Self { + Self::new(iter) + } +} + #[cfg(test)] mod tests { use super::*; @@ -335,6 +354,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); diff --git a/tests/widgets_tabs.rs b/tests/widgets_tabs.rs index 0ee646d32..9941239d2 100644 --- a/tests/widgets_tabs.rs +++ b/tests/widgets_tabs.rs @@ -4,7 +4,6 @@ use ratatui::{ layout::Rect, style::{Style, Stylize}, symbols, - text::Line, widgets::Tabs, Terminal, }; @@ -15,7 +14,7 @@ fn widgets_tabs_should_not_panic_on_narrow_areas() { let mut terminal = Terminal::new(backend).unwrap(); terminal .draw(|f| { - let tabs = Tabs::new(["Tab1", "Tab2"].iter().cloned().map(Line::from).collect()); + let tabs = Tabs::new(["Tab1", "Tab2"]); f.render_widget( tabs, Rect { @@ -37,7 +36,7 @@ fn widgets_tabs_should_truncate_the_last_item() { let mut terminal = Terminal::new(backend).unwrap(); terminal .draw(|f| { - let tabs = Tabs::new(["Tab1", "Tab2"].iter().cloned().map(Line::from).collect()); + let tabs = Tabs::new(["Tab1", "Tab2"]); f.render_widget( tabs, Rect {