diff --git a/Cargo.toml b/Cargo.toml index c7a017b1..bc51f3f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,6 +78,10 @@ missing_panics_doc = "allow" module_name_repetitions = "allow" must_use_candidate = "allow" +# we often split up a module into multiple files with the main type in a file named after the +# module, so we want to allow this pattern +module_inception = "allow" + # nursery or restricted as_underscore = "warn" deref_by_slicing = "warn" diff --git a/src/buffer.rs b/src/buffer.rs index a560e364..e7ac4d74 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -2,7 +2,6 @@ //! A module for the [`Buffer`] and [`Cell`] types. mod assert; -#[allow(clippy::module_inception)] mod buffer; mod cell; diff --git a/src/layout.rs b/src/layout.rs index dbae27f8..a4eef996 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -4,7 +4,6 @@ mod alignment; mod constraint; mod direction; mod flex; -#[allow(clippy::module_inception)] mod layout; mod margin; mod position; diff --git a/src/terminal.rs b/src/terminal.rs index a9b397b1..00e22f50 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -32,7 +32,6 @@ //! [`Buffer`]: crate::buffer::Buffer mod frame; -#[allow(clippy::module_inception)] mod terminal; mod viewport; diff --git a/src/text.rs b/src/text.rs index a3dd163e..8ae719a4 100644 --- a/src/text.rs +++ b/src/text.rs @@ -56,6 +56,5 @@ pub use masked::Masked; mod span; pub use span::{Span, ToSpan}; -#[allow(clippy::module_inception)] mod text; pub use text::{Text, ToText}; diff --git a/src/widgets/list.rs b/src/widgets/list.rs index c95af0a2..c167e9b2 100755 --- a/src/widgets/list.rs +++ b/src/widgets/list.rs @@ -1,2293 +1,10 @@ -use strum::{Display, EnumString}; -use unicode_width::UnicodeWidthStr; - -use crate::{ - prelude::*, - style::Styled, - widgets::{Block, HighlightSpacing}, +pub use self::{ + item::ListItem, + list::{List, ListDirection}, + state::ListState, }; -/// State of the [`List`] widget -/// -/// This state can be used to scroll through items and select one. When the list is rendered as a -/// stateful widget, the selected item will be highlighted and the list will be shifted to ensure -/// that the selected item is visible. This will modify the [`ListState`] object passed to the -/// [`Frame::render_stateful_widget`](crate::terminal::Frame::render_stateful_widget) method. -/// -/// The state consists of two fields: -/// - [`offset`]: the index of the first item to be displayed -/// - [`selected`]: the index of the selected item, which can be `None` if no item is selected -/// -/// [`offset`]: ListState::offset() -/// [`selected`]: ListState::selected() -/// -/// See the list in the [Examples] directory for a more in depth example of the various -/// configuration options and for how to handle state. -/// -/// [Examples]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md -/// -/// # Example -/// -/// ```rust -/// # use ratatui::{prelude::*, widgets::*}; -/// # fn ui(frame: &mut Frame) { -/// # let area = Rect::default(); -/// # let items = ["Item 1"]; -/// let list = List::new(items); -/// -/// // This should be stored outside of the function in your application state. -/// let mut state = ListState::default(); -/// -/// *state.offset_mut() = 1; // display the second item and onwards -/// state.select(Some(3)); // select the forth item (0-indexed) -/// -/// frame.render_stateful_widget(list, area, &mut state); -/// # } -/// ``` -#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct ListState { - offset: usize, - selected: Option, -} - -impl ListState { - /// Sets the index of the first item to be displayed - /// - /// This is a fluent setter method which must be chained or used as it consumes self - /// - /// # Examples - /// - /// ```rust - /// # use ratatui::{prelude::*, widgets::*}; - /// let state = ListState::default().with_offset(1); - /// ``` - #[must_use = "method moves the value of self and returns the modified value"] - pub const fn with_offset(mut self, offset: usize) -> Self { - self.offset = offset; - self - } - - /// Sets the index of the selected item - /// - /// This is a fluent setter method which must be chained or used as it consumes self - /// - /// # Examples - /// - /// ```rust - /// # use ratatui::{prelude::*, widgets::*}; - /// let state = ListState::default().with_selected(Some(1)); - /// ``` - #[must_use = "method moves the value of self and returns the modified value"] - pub const fn with_selected(mut self, selected: Option) -> Self { - self.selected = selected; - self - } - - /// Index of the first item to be displayed - /// - /// # Examples - /// - /// ```rust - /// # use ratatui::{prelude::*, widgets::*}; - /// let state = ListState::default(); - /// assert_eq!(state.offset(), 0); - /// ``` - pub const fn offset(&self) -> usize { - self.offset - } - - /// Mutable reference to the index of the first item to be displayed - /// - /// # Examples - /// - /// ```rust - /// # use ratatui::{prelude::*, widgets::*}; - /// let mut state = ListState::default(); - /// *state.offset_mut() = 1; - /// ``` - pub fn offset_mut(&mut self) -> &mut usize { - &mut self.offset - } - - /// Index of the selected item - /// - /// Returns `None` if no item is selected - /// - /// # Examples - /// - /// ```rust - /// # use ratatui::{prelude::*, widgets::*}; - /// let state = TableState::default(); - /// assert_eq!(state.selected(), None); - /// ``` - pub const fn selected(&self) -> Option { - self.selected - } - - /// Mutable reference to the index of the selected item - /// - /// Returns `None` if no item is selected - /// - /// # Examples - /// - /// ```rust - /// # use ratatui::{prelude::*, widgets::*}; - /// let mut state = ListState::default(); - /// *state.selected_mut() = Some(1); - /// ``` - pub fn selected_mut(&mut self) -> &mut Option { - &mut self.selected - } - - /// Sets the index of the selected item - /// - /// Set to `None` if no item is selected. This will also reset the offset to `0`. - /// - /// # Examples - /// - /// ```rust - /// # use ratatui::{prelude::*, widgets::*}; - /// let mut state = ListState::default(); - /// state.select(Some(1)); - /// ``` - pub fn select(&mut self, index: Option) { - self.selected = index; - if index.is_none() { - self.offset = 0; - } - } - - /// Selects the next item or the first one if no item is selected - /// - /// Note: until the list is rendered, the number of items is not known, so the index is set to - /// `0` and will be corrected when the list is rendered - /// - /// # Examples - /// - /// ```rust - /// # use ratatui::{prelude::*, widgets::*}; - /// let mut state = ListState::default(); - /// state.select_next(); - /// ``` - pub fn select_next(&mut self) { - let next = self.selected.map_or(0, |i| i.saturating_add(1)); - self.select(Some(next)); - } - - /// Selects the previous item or the last one if no item is selected - /// - /// Note: until the list is rendered, the number of items is not known, so the index is set to - /// `usize::MAX` and will be corrected when the list is rendered - /// - /// # Examples - /// - /// ```rust - /// # use ratatui::{prelude::*, widgets::*}; - /// let mut state = ListState::default(); - /// state.select_previous(); - /// ``` - pub fn select_previous(&mut self) { - let previous = self.selected.map_or(usize::MAX, |i| i.saturating_sub(1)); - self.select(Some(previous)); - } - - /// Selects the first item - /// - /// Note: until the list is rendered, the number of items is not known, so the index is set to - /// `0` and will be corrected when the list is rendered - /// - /// # Examples - /// - /// ```rust - /// # use ratatui::{prelude::*, widgets::*}; - /// let mut state = ListState::default(); - /// state.select_first(); - /// ``` - pub fn select_first(&mut self) { - self.select(Some(0)); - } - - /// Selects the last item - /// - /// Note: until the list is rendered, the number of items is not known, so the index is set to - /// `usize::MAX` and will be corrected when the list is rendered - /// - /// # Examples - /// - /// ```rust - /// # use ratatui::{prelude::*, widgets::*}; - /// let mut state = ListState::default(); - /// state.select_last(); - /// ``` - pub fn select_last(&mut self) { - self.select(Some(usize::MAX)); - } -} - -/// A single item in a [`List`] -/// -/// The item's height is defined by the number of lines it contains. This can be queried using -/// [`ListItem::height`]. Similarly, [`ListItem::width`] will return the maximum width of all -/// lines. -/// -/// You can set the style of an item with [`ListItem::style`] or using the [`Stylize`] trait. -/// This [`Style`] will be combined with the [`Style`] of the inner [`Text`]. The [`Style`] -/// of the [`Text`] will be added to the [`Style`] of the [`ListItem`]. -/// -/// You can also align a `ListItem` by aligning its underlying [`Text`] and [`Line`]s. For that, -/// see [`Text::alignment`] and [`Line::alignment`]. On a multiline `Text`, one `Line` can override -/// the alignment by setting it explicitly. -/// -/// # Examples -/// -/// You can create [`ListItem`]s from simple `&str` -/// -/// ```rust -/// # use ratatui::{prelude::*, widgets::*}; -/// let item = ListItem::new("Item 1"); -/// ``` -/// -/// Anything that can be converted to [`Text`] can be a [`ListItem`]. -/// -/// ```rust -/// # use ratatui::{prelude::*, widgets::*}; -/// let item1: ListItem = "Item 1".into(); -/// let item2: ListItem = Line::raw("Item 2").into(); -/// ``` -/// -/// A [`ListItem`] styled with [`Stylize`] -/// -/// ```rust -/// # use ratatui::{prelude::*, widgets::*}; -/// let item = ListItem::new("Item 1").red().on_white(); -/// ``` -/// -/// If you need more control over the item's style, you can explicitly style the underlying -/// [`Text`] -/// -/// ```rust -/// # use ratatui::{prelude::*, widgets::*}; -/// let mut text = Text::default(); -/// text.extend(["Item".blue(), Span::raw(" "), "1".bold().red()]); -/// let item = ListItem::new(text); -/// ``` -/// -/// A right-aligned `ListItem` -/// -/// ```rust -/// # use ratatui::{prelude::*, widgets::*}; -/// ListItem::new(Text::from("foo").alignment(Alignment::Right)); -/// ``` -/// -/// [`Stylize`]: crate::style::Stylize -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct ListItem<'a> { - content: Text<'a>, - style: Style, -} - -impl<'a> ListItem<'a> { - /// Creates a new [`ListItem`] - /// - /// The `content` parameter accepts any value that can be converted into [`Text`]. - /// - /// # Examples - /// - /// You can create [`ListItem`]s from simple `&str` - /// - /// ```rust - /// # use ratatui::{prelude::*, widgets::*}; - /// let item = ListItem::new("Item 1"); - /// ``` - /// - /// Anything that can be converted to [`Text`] can be a [`ListItem`]. - /// - /// ```rust - /// # use ratatui::{prelude::*, widgets::*}; - /// let item1: ListItem = "Item 1".into(); - /// let item2: ListItem = Line::raw("Item 2").into(); - /// ``` - /// - /// You can also create multilines item - /// - /// ```rust - /// # use ratatui::{prelude::*, widgets::*}; - /// let item = ListItem::new("Multi-line\nitem"); - /// ``` - /// - /// # See also - /// - /// - [`List::new`] to create a list of items that can be converted to [`ListItem`] - pub fn new(content: T) -> Self - where - T: Into>, - { - Self { - content: content.into(), - style: Style::default(), - } - } - - /// Sets the item style - /// - /// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or - /// your own type that implements [`Into