Compare commits
4 Commits
kd/multi-s
...
v0.26.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fadc73d62e | ||
|
|
fcb5d589bb | ||
|
|
4955380932 | ||
|
|
828d17a3f5 |
@@ -1,16 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if !(command cargo-make >/dev/null 2>&1); then # Check if cargo-make is installed
|
||||
echo Attempting to run cargo-make as part of the pre-push hook but it\'s not installed.
|
||||
echo Please install it by running the following command:
|
||||
echo
|
||||
echo " cargo install --force cargo-make"
|
||||
echo
|
||||
echo If you don\'t want to run cargo-make as part of the pre-push hook, you can run
|
||||
echo the following command instead of git push:
|
||||
echo
|
||||
echo " git push --no-verify"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cargo make ci
|
||||
10551
CHANGELOG.md
10551
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -56,11 +56,9 @@ documented.
|
||||
|
||||
### Run CI tests before pushing a PR
|
||||
|
||||
We're using [cargo-husky](https://github.com/rhysd/cargo-husky) to automatically run git hooks,
|
||||
which will run `cargo make ci` before each push. To initialize the hook run `cargo test`. If
|
||||
`cargo-make` is not installed, it will provide instructions to install it for you. This will ensure
|
||||
that your code is formatted, compiles and passes all tests before you push. If you need to skip this
|
||||
check, you can use `git push --no-verify`.
|
||||
Running `cargo make ci` before pushing will perform the same checks that we do in the CI process.
|
||||
It's not mandatory to do this before pushing, however it may save you time to do so instead of
|
||||
waiting for GitHub to run the checks.
|
||||
|
||||
### Sign your commits
|
||||
|
||||
|
||||
11
Cargo.toml
11
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ratatui"
|
||||
version = "0.26.2" # crate version
|
||||
version = "0.26.3" # crate version
|
||||
authors = ["Florian Dehau <work@fdehau.com>", "The Ratatui Developers"]
|
||||
description = "A library that's all about cooking up terminal user interfaces"
|
||||
documentation = "https://docs.rs/ratatui/latest/ratatui/"
|
||||
@@ -47,9 +47,6 @@ unicode-width = "0.1"
|
||||
anyhow = "1.0.71"
|
||||
argh = "0.1.12"
|
||||
better-panic = "0.3.0"
|
||||
cargo-husky = { version = "1.5.0", default-features = false, features = [
|
||||
"user-hooks",
|
||||
] }
|
||||
color-eyre = "0.6.2"
|
||||
criterion = { version = "0.5.1", features = ["html_reports"] }
|
||||
derive_builder = "0.20.0"
|
||||
@@ -274,6 +271,12 @@ name = "list"
|
||||
required-features = ["crossterm"]
|
||||
doc-scrape-examples = true
|
||||
|
||||
[[example]]
|
||||
name = "minimal"
|
||||
required-features = ["crossterm"]
|
||||
# prefer to show the more featureful examples in the docs
|
||||
doc-scrape-examples = false
|
||||
|
||||
[[example]]
|
||||
name = "modifiers"
|
||||
required-features = ["crossterm"]
|
||||
|
||||
44
examples/minimal.rs
Normal file
44
examples/minimal.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
//! # [Ratatui] Minimal example
|
||||
//!
|
||||
//! The latest version of this example is available in the [examples] folder in the repository.
|
||||
//!
|
||||
//! Please note that the examples are designed to be run against the `main` branch of the Github
|
||||
//! repository. This means that you may not be able to compile with the latest release version on
|
||||
//! crates.io, or the one that you have installed locally.
|
||||
//!
|
||||
//! See the [examples readme] for more information on finding examples that match the version of the
|
||||
//! library you are using.
|
||||
//!
|
||||
//! [Ratatui]: https://github.com/ratatui-org/ratatui
|
||||
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
|
||||
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
|
||||
|
||||
use crossterm::{
|
||||
event::{self, Event, KeyCode, KeyEventKind},
|
||||
execute,
|
||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
};
|
||||
use ratatui::{backend::CrosstermBackend, text::Text, Terminal};
|
||||
|
||||
/// This is a bare minimum example. There are many approaches to running an application loop, so
|
||||
/// this is not meant to be prescriptive. See the [examples] folder for more complete examples.
|
||||
/// In particular, the [hello-world] example is a good starting point.
|
||||
///
|
||||
/// [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
|
||||
/// [hello-world]: https://github.com/ratatui-org/ratatui/blob/main/examples/hello_world.rs
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut terminal = Terminal::new(CrosstermBackend::new(std::io::stdout()))?;
|
||||
enable_raw_mode()?;
|
||||
execute!(terminal.backend_mut(), EnterAlternateScreen)?;
|
||||
loop {
|
||||
terminal.draw(|frame| frame.render_widget(Text::raw("Hello World!"), frame.size()))?;
|
||||
if let Event::Key(key) = event::read()? {
|
||||
if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
disable_raw_mode()?;
|
||||
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -83,6 +83,7 @@ impl Frame<'_> {
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "unstable-widget-ref")] {
|
||||
/// # use ratatui::{backend::TestBackend, prelude::*, widgets::Block};
|
||||
/// # let backend = TestBackend::new(5, 5);
|
||||
/// # let mut terminal = Terminal::new(backend).unwrap();
|
||||
@@ -90,6 +91,7 @@ impl Frame<'_> {
|
||||
/// let block = Block::new();
|
||||
/// let area = Rect::new(0, 0, 5, 5);
|
||||
/// frame.render_widget_ref(block, area);
|
||||
/// # }
|
||||
/// ```
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
#[stability::unstable(feature = "widget-ref")]
|
||||
@@ -138,6 +140,7 @@ impl Frame<'_> {
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "unstable-widget-ref")] {
|
||||
/// # use ratatui::{backend::TestBackend, prelude::*, widgets::*};
|
||||
/// # let backend = TestBackend::new(5, 5);
|
||||
/// # let mut terminal = Terminal::new(backend).unwrap();
|
||||
@@ -146,6 +149,7 @@ impl Frame<'_> {
|
||||
/// let list = List::new(vec![ListItem::new("Item 1"), ListItem::new("Item 2")]);
|
||||
/// let area = Rect::new(0, 0, 5, 5);
|
||||
/// frame.render_stateful_widget_ref(list, area, &mut state);
|
||||
/// # }
|
||||
/// ```
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
#[stability::unstable(feature = "widget-ref")]
|
||||
|
||||
362
src/widgets.rs
362
src/widgets.rs
@@ -248,6 +248,7 @@ pub trait StatefulWidget {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "unstable-widget-ref")] {
|
||||
/// use ratatui::{prelude::*, widgets::*};
|
||||
///
|
||||
/// struct Greeting;
|
||||
@@ -294,6 +295,7 @@ pub trait StatefulWidget {
|
||||
/// widget.render_ref(area, buf);
|
||||
/// }
|
||||
/// # }
|
||||
/// # }
|
||||
/// ```
|
||||
#[stability::unstable(feature = "widget-ref")]
|
||||
pub trait WidgetRef {
|
||||
@@ -321,6 +323,7 @@ impl<W: WidgetRef> Widget for &W {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "unstable-widget-ref")] {
|
||||
/// use ratatui::{prelude::*, widgets::*};
|
||||
///
|
||||
/// struct Parent {
|
||||
@@ -340,6 +343,7 @@ impl<W: WidgetRef> Widget for &W {
|
||||
/// self.child.render_ref(area, buf);
|
||||
/// }
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
impl<W: WidgetRef> WidgetRef for Option<W> {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
@@ -368,6 +372,7 @@ impl<W: WidgetRef> WidgetRef for Option<W> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "unstable-widget-ref")] {
|
||||
/// use ratatui::{prelude::*, widgets::*};
|
||||
///
|
||||
/// struct PersonalGreeting;
|
||||
@@ -386,10 +391,11 @@ impl<W: WidgetRef> WidgetRef for Option<W> {
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # fn render(area: Rect, buf: &mut Buffer) {
|
||||
/// let widget = PersonalGreeting;
|
||||
/// let mut state = "world".to_string();
|
||||
/// widget.render(area, buf, &mut state);
|
||||
/// fn render(area: Rect, buf: &mut Buffer) {
|
||||
/// let widget = PersonalGreeting;
|
||||
/// let mut state = "world".to_string();
|
||||
/// widget.render(area, buf, &mut state);
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
#[stability::unstable(feature = "widget-ref")]
|
||||
@@ -470,90 +476,79 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::prelude::*;
|
||||
|
||||
struct Greeting;
|
||||
struct Farewell;
|
||||
struct PersonalGreeting;
|
||||
|
||||
impl Widget for Greeting {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_ref(area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for Greeting {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
Line::from("Hello").render(area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Farewell {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_ref(area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for Farewell {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
Line::from("Goodbye").right_aligned().render(area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl StatefulWidget for PersonalGreeting {
|
||||
type State = String;
|
||||
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||
self.render_ref(area, buf, state);
|
||||
}
|
||||
}
|
||||
|
||||
impl StatefulWidgetRef for PersonalGreeting {
|
||||
type State = String;
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||
Line::from(format!("Hello {state}")).render(area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
#[fixture]
|
||||
fn buf() -> Buffer {
|
||||
Buffer::empty(Rect::new(0, 0, 20, 1))
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn widget_render(mut buf: Buffer) {
|
||||
let widget = Greeting;
|
||||
widget.render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
||||
}
|
||||
mod widget {
|
||||
use super::*;
|
||||
|
||||
#[rstest]
|
||||
fn widget_ref_render(mut buf: Buffer) {
|
||||
let widget = Greeting;
|
||||
widget.render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
||||
}
|
||||
struct Greeting;
|
||||
|
||||
/// This test is to ensure that the blanket implementation of `Widget` for `&W` where `W`
|
||||
/// implements `WidgetRef` works as expected.
|
||||
#[rstest]
|
||||
fn widget_blanket_render(mut buf: Buffer) {
|
||||
let widget = &Greeting;
|
||||
widget.render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn widget_box_render_ref(mut buf: Buffer) {
|
||||
let widget: Box<dyn WidgetRef> = Box::new(Greeting);
|
||||
widget.render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn widget_vec_box_render(mut buf: Buffer) {
|
||||
let widgets: Vec<Box<dyn WidgetRef>> = vec![Box::new(Greeting), Box::new(Farewell)];
|
||||
for widget in widgets {
|
||||
widget.render_ref(buf.area, &mut buf);
|
||||
impl Widget for Greeting {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
Line::from("Hello").render(area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn render(mut buf: Buffer) {
|
||||
let widget = Greeting;
|
||||
widget.render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
||||
}
|
||||
}
|
||||
|
||||
mod widget_ref {
|
||||
use super::*;
|
||||
|
||||
struct Greeting;
|
||||
struct Farewell;
|
||||
|
||||
impl WidgetRef for Greeting {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
Line::from("Hello").render(area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for Farewell {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
Line::from("Goodbye").right_aligned().render(area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn render_ref(mut buf: Buffer) {
|
||||
let widget = Greeting;
|
||||
widget.render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
||||
}
|
||||
|
||||
/// Ensure that the blanket implementation of `Widget` for `&W` where `W` implements
|
||||
/// `WidgetRef` works as expected.
|
||||
#[rstest]
|
||||
fn blanket_render(mut buf: Buffer) {
|
||||
let widget = &Greeting;
|
||||
widget.render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn box_render_ref(mut buf: Buffer) {
|
||||
let widget: Box<dyn WidgetRef> = Box::new(Greeting);
|
||||
widget.render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn vec_box_render(mut buf: Buffer) {
|
||||
let widgets: Vec<Box<dyn WidgetRef>> = vec![Box::new(Greeting), Box::new(Farewell)];
|
||||
for widget in widgets {
|
||||
widget.render_ref(buf.area, &mut buf);
|
||||
}
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello Goodbye"]));
|
||||
}
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello Goodbye"]));
|
||||
}
|
||||
|
||||
#[fixture]
|
||||
@@ -561,98 +556,143 @@ mod tests {
|
||||
"world".to_string()
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn stateful_widget_render(mut buf: Buffer, mut state: String) {
|
||||
let widget = PersonalGreeting;
|
||||
widget.render(buf.area, &mut buf, &mut state);
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello world "]));
|
||||
mod stateful_widget {
|
||||
use super::*;
|
||||
|
||||
struct PersonalGreeting;
|
||||
|
||||
impl StatefulWidget for PersonalGreeting {
|
||||
type State = String;
|
||||
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||
Line::from(format!("Hello {state}")).render(area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn render(mut buf: Buffer, mut state: String) {
|
||||
let widget = PersonalGreeting;
|
||||
widget.render(buf.area, &mut buf, &mut state);
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello world "]));
|
||||
}
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn stateful_widget_ref_render(mut buf: Buffer, mut state: String) {
|
||||
let widget = PersonalGreeting;
|
||||
widget.render_ref(buf.area, &mut buf, &mut state);
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello world "]));
|
||||
mod stateful_widget_ref {
|
||||
use super::*;
|
||||
|
||||
struct PersonalGreeting;
|
||||
|
||||
impl StatefulWidgetRef for PersonalGreeting {
|
||||
type State = String;
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||
Line::from(format!("Hello {state}")).render(area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn render_ref(mut buf: Buffer, mut state: String) {
|
||||
let widget = PersonalGreeting;
|
||||
widget.render_ref(buf.area, &mut buf, &mut state);
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello world "]));
|
||||
}
|
||||
|
||||
// Note this cannot be tested until the blanket implementation of StatefulWidget for &W
|
||||
// where W implements StatefulWidgetRef is added. (see the comment in the blanket
|
||||
// implementation for more).
|
||||
// /// This test is to ensure that the blanket implementation of `StatefulWidget` for `&W`
|
||||
// where /// `W` implements `StatefulWidgetRef` works as expected.
|
||||
// #[rstest]
|
||||
// fn stateful_widget_blanket_render(mut buf: Buffer, mut state: String) {
|
||||
// let widget = &PersonalGreeting;
|
||||
// widget.render(buf.area, &mut buf, &mut state);
|
||||
// assert_eq!(buf, Buffer::with_lines(["Hello world "]));
|
||||
// }
|
||||
|
||||
#[rstest]
|
||||
fn box_render_render(mut buf: Buffer, mut state: String) {
|
||||
let widget = Box::new(PersonalGreeting);
|
||||
widget.render_ref(buf.area, &mut buf, &mut state);
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello world "]));
|
||||
}
|
||||
}
|
||||
|
||||
// Note this cannot be tested until the blanket implementation of StatefulWidget for &W where W
|
||||
// implements StatefulWidgetRef is added. (see the comment in the blanket implementation for
|
||||
// more).
|
||||
// /// This test is to ensure that the blanket implementation of `StatefulWidget` for `&W` where
|
||||
// /// `W` implements `StatefulWidgetRef` works as expected.
|
||||
// #[rstest]
|
||||
// fn stateful_widget_blanket_render(mut buf: Buffer, mut state: String) {
|
||||
// let widget = &PersonalGreeting;
|
||||
// widget.render(buf.area, &mut buf, &mut state);
|
||||
// assert_eq!(buf, Buffer::with_lines(["Hello world "]));
|
||||
// }
|
||||
mod option_widget_ref {
|
||||
use super::*;
|
||||
|
||||
#[rstest]
|
||||
fn stateful_widget_box_render(mut buf: Buffer, mut state: String) {
|
||||
let widget = Box::new(PersonalGreeting);
|
||||
widget.render(buf.area, &mut buf, &mut state);
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello world "]));
|
||||
struct Greeting;
|
||||
|
||||
impl WidgetRef for Greeting {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
Line::from("Hello").render(area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn render_ref_some(mut buf: Buffer) {
|
||||
let widget = Some(Greeting);
|
||||
widget.render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn render_ref_none(mut buf: Buffer) {
|
||||
let widget: Option<Greeting> = None;
|
||||
widget.render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines([" "]));
|
||||
}
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn widget_option_render_ref_some(mut buf: Buffer) {
|
||||
let widget = Some(Greeting);
|
||||
widget.render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
||||
mod str {
|
||||
use super::*;
|
||||
|
||||
#[rstest]
|
||||
fn render(mut buf: Buffer) {
|
||||
"hello world".render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn render_ref(mut buf: Buffer) {
|
||||
"hello world".render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn option_render(mut buf: Buffer) {
|
||||
Some("hello world").render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn option_render_ref(mut buf: Buffer) {
|
||||
Some("hello world").render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||
}
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn widget_option_render_ref_none(mut buf: Buffer) {
|
||||
let widget: Option<Greeting> = None;
|
||||
widget.render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines([" "]));
|
||||
}
|
||||
mod string {
|
||||
use super::*;
|
||||
#[rstest]
|
||||
fn render(mut buf: Buffer) {
|
||||
String::from("hello world").render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn str_render(mut buf: Buffer) {
|
||||
"hello world".render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||
}
|
||||
#[rstest]
|
||||
fn render_ref(mut buf: Buffer) {
|
||||
String::from("hello world").render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn str_render_ref(mut buf: Buffer) {
|
||||
"hello world".render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||
}
|
||||
#[rstest]
|
||||
fn option_render(mut buf: Buffer) {
|
||||
Some(String::from("hello world")).render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn str_option_render(mut buf: Buffer) {
|
||||
Some("hello world").render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn str_option_render_ref(mut buf: Buffer) {
|
||||
Some("hello world").render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn string_render(mut buf: Buffer) {
|
||||
String::from("hello world").render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn string_render_ref(mut buf: Buffer) {
|
||||
String::from("hello world").render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn string_option_render(mut buf: Buffer) {
|
||||
Some(String::from("hello world")).render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn string_option_render_ref(mut buf: Buffer) {
|
||||
Some(String::from("hello world")).render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||
#[rstest]
|
||||
fn option_render_ref(mut buf: Buffer) {
|
||||
Some(String::from("hello world")).render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,15 +213,6 @@ pub struct Table<'a> {
|
||||
/// Symbol in front of the selected row
|
||||
highlight_symbol: Text<'a>,
|
||||
|
||||
/// Symbol in front of the marked row
|
||||
mark_symbol: Text<'a>,
|
||||
|
||||
/// Symbol in front of the unmarked row
|
||||
unmark_symbol: Text<'a>,
|
||||
|
||||
/// Symbol in front of the marked and selected row
|
||||
mark_highlight_symbol: Text<'a>,
|
||||
|
||||
/// Decides when to allocate spacing for the row selection
|
||||
highlight_spacing: HighlightSpacing,
|
||||
|
||||
@@ -241,9 +232,6 @@ impl<'a> Default for Table<'a> {
|
||||
style: Style::new(),
|
||||
highlight_style: Style::new(),
|
||||
highlight_symbol: Text::default(),
|
||||
mark_symbol: Text::default(),
|
||||
unmark_symbol: Text::default(),
|
||||
mark_highlight_symbol: Text::default(),
|
||||
highlight_spacing: HighlightSpacing::default(),
|
||||
flex: Flex::Start,
|
||||
}
|
||||
@@ -515,60 +503,6 @@ impl<'a> Table<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the symbol to be displayed in front of the marked row
|
||||
///
|
||||
/// This is a fluent setter method which must be chained or used as it consumes self
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ratatui::{prelude::*, widgets::*};
|
||||
/// # let rows = [Row::new(vec!["Cell1", "Cell2"])];
|
||||
/// # let widths = [Constraint::Length(5), Constraint::Length(5)];
|
||||
/// let table = Table::new(rows, widths).mark_symbol("\u{2714}");
|
||||
/// ```
|
||||
#[must_use = "method moves the value of self and returns the modified value"]
|
||||
pub fn mark_symbol<T: Into<Text<'a>>>(mut self, mark_symbol: T) -> Self {
|
||||
self.mark_symbol = mark_symbol.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the symbol to be displayed in front of the unmarked row
|
||||
///
|
||||
/// This is a fluent setter method which must be chained or used as it consumes self
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ratatui::{prelude::*, widgets::*};
|
||||
/// # let rows = [Row::new(vec!["Cell1", "Cell2"])];
|
||||
/// # let widths = [Constraint::Length(5), Constraint::Length(5)];
|
||||
/// let table = Table::new(rows, widths).unmark_symbol(" ");
|
||||
/// ```
|
||||
#[must_use = "method moves the value of self and returns the modified value"]
|
||||
pub fn unmark_symbol<T: Into<Text<'a>>>(mut self, unmark_symbol: T) -> Self {
|
||||
self.unmark_symbol = unmark_symbol.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the symbol to be displayed in front of the marked and selected row
|
||||
///
|
||||
/// This is a fluent setter method which must be chained or used as it consumes self
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ratatui::{prelude::*, widgets::*};
|
||||
/// # let rows = [Row::new(vec!["Cell1", "Cell2"])];
|
||||
/// # let widths = [Constraint::Length(5), Constraint::Length(5)];
|
||||
/// let table = Table::new(rows, widths).mark_highlight_symbol("\u{29bf}");
|
||||
/// ```
|
||||
#[must_use = "method moves the value of self and returns the modified value"]
|
||||
pub fn mark_highlight_symbol<T: Into<Text<'a>>>(mut self, mark_highlight_symbol: T) -> Self {
|
||||
self.mark_highlight_symbol = mark_highlight_symbol.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set when to show the highlight spacing
|
||||
///
|
||||
/// The highlight spacing is the spacing that is allocated for the selection symbol column (if
|
||||
@@ -670,8 +604,8 @@ impl StatefulWidgetRef for Table<'_> {
|
||||
return;
|
||||
}
|
||||
|
||||
let highlight_column_width = self.highlight_column_width(state);
|
||||
let columns_widths = self.get_columns_widths(table_area.width, highlight_column_width);
|
||||
let selection_width = self.selection_width(state);
|
||||
let columns_widths = self.get_columns_widths(table_area.width, selection_width);
|
||||
let (header_area, rows_area, footer_area) = self.layout(table_area);
|
||||
|
||||
self.render_header(header_area, buf, &columns_widths);
|
||||
@@ -680,13 +614,8 @@ impl StatefulWidgetRef for Table<'_> {
|
||||
rows_area,
|
||||
buf,
|
||||
state,
|
||||
highlight_column_width,
|
||||
(
|
||||
&self.highlight_symbol,
|
||||
&self.mark_symbol,
|
||||
&self.unmark_symbol,
|
||||
&self.mark_highlight_symbol,
|
||||
),
|
||||
selection_width,
|
||||
&self.highlight_symbol,
|
||||
&columns_widths,
|
||||
);
|
||||
|
||||
@@ -741,16 +670,14 @@ impl Table<'_> {
|
||||
area: Rect,
|
||||
buf: &mut Buffer,
|
||||
state: &mut TableState,
|
||||
highlight_column_width: u16,
|
||||
symbols: (&Text<'_>, &Text<'_>, &Text<'_>, &Text<'_>),
|
||||
selection_width: u16,
|
||||
highlight_symbol: &Text<'_>,
|
||||
columns_widths: &[(u16, u16)],
|
||||
) {
|
||||
if self.rows.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let (highlight_symbol, mark_symbol, unmark_symbol, mark_highlight_symbol) = symbols;
|
||||
|
||||
let (start_index, end_index) =
|
||||
self.get_row_bounds(state.selected, state.offset, area.height);
|
||||
state.offset = start_index;
|
||||
@@ -771,37 +698,22 @@ impl Table<'_> {
|
||||
);
|
||||
buf.set_style(row_area, row.style);
|
||||
|
||||
let is_marked = state.marked().contains(&(i + state.offset));
|
||||
let is_highlighted = state.selected().is_some_and(|index| index == i);
|
||||
if highlight_column_width > 0 {
|
||||
let area = Rect {
|
||||
width: highlight_column_width,
|
||||
let is_selected = state.selected().is_some_and(|index| index == i);
|
||||
if selection_width > 0 && is_selected {
|
||||
let selection_area = Rect {
|
||||
width: selection_width,
|
||||
..row_area
|
||||
};
|
||||
buf.set_style(area, row.style);
|
||||
|
||||
match (is_marked, is_highlighted) {
|
||||
(true, true) => {
|
||||
mark_highlight_symbol.render(area, buf);
|
||||
}
|
||||
(true, false) => {
|
||||
mark_symbol.render(area, buf);
|
||||
}
|
||||
(false, true) => {
|
||||
highlight_symbol.render(area, buf);
|
||||
}
|
||||
(false, false) => {
|
||||
unmark_symbol.render(area, buf);
|
||||
}
|
||||
};
|
||||
}
|
||||
buf.set_style(selection_area, row.style);
|
||||
highlight_symbol.clone().render(selection_area, buf);
|
||||
};
|
||||
for ((x, width), cell) in columns_widths.iter().zip(row.cells.iter()) {
|
||||
cell.render(
|
||||
Rect::new(row_area.x + x, row_area.y, *width, row_area.height),
|
||||
buf,
|
||||
);
|
||||
}
|
||||
if is_highlighted {
|
||||
if is_selected {
|
||||
buf.set_style(row_area, self.highlight_style);
|
||||
}
|
||||
y_offset += row.height_with_margin();
|
||||
@@ -876,35 +788,15 @@ impl Table<'_> {
|
||||
(start, end)
|
||||
}
|
||||
|
||||
/// Returns the width of the indicator column if a row is selected, rows are marked,
|
||||
/// or the `highlight_spacing` is set to show the column always, otherwise 0.
|
||||
fn highlight_column_width(&self, state: &TableState) -> u16 {
|
||||
let has_highlight = state.selected().is_some() || state.marked().len() > 0;
|
||||
let highlight_column_width = if self.highlight_spacing.should_add(has_highlight) {
|
||||
/// Returns the width of the selection column if a row is selected, or the `highlight_spacing`
|
||||
/// is set to show the column always, otherwise 0.
|
||||
fn selection_width(&self, state: &TableState) -> u16 {
|
||||
let has_selection = state.selected().is_some();
|
||||
if self.highlight_spacing.should_add(has_selection) {
|
||||
self.highlight_symbol.width() as u16
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let mark_column_width = if self.highlight_spacing.should_add(has_highlight) {
|
||||
self.mark_symbol.width() as u16
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let mark_highlight_column_width = if self.highlight_spacing.should_add(has_highlight) {
|
||||
self.mark_highlight_symbol.width() as u16
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let unmark_column_width = if self.highlight_spacing.should_add(has_highlight) {
|
||||
self.unmark_symbol.width() as u16
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
highlight_column_width
|
||||
.max(mark_column_width)
|
||||
.max(mark_highlight_column_width)
|
||||
.max(unmark_column_width)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1298,100 +1190,6 @@ mod tests {
|
||||
]);
|
||||
assert_eq!(buf, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_with_selected_marked_unmarked() {
|
||||
let rows = vec![
|
||||
Row::new(vec!["Cell", "Cell"]),
|
||||
Row::new(vec!["Cell", "Cell"]),
|
||||
Row::new(vec!["Cell", "Cell"]),
|
||||
Row::new(vec!["Cell", "Cell"]),
|
||||
Row::new(vec!["Cell", "Cell"]),
|
||||
Row::new(vec!["Cell", "Cell"]),
|
||||
Row::new(vec!["Cell", "Cell"]),
|
||||
];
|
||||
let table = Table::new(rows, [Constraint::Length(5); 2])
|
||||
.highlight_symbol("• ")
|
||||
.mark_symbol("⦾")
|
||||
.unmark_symbol(" ")
|
||||
.mark_highlight_symbol("⦿");
|
||||
|
||||
let mut state = TableState::new().with_selected(0);
|
||||
|
||||
state.mark(1);
|
||||
state.mark(3);
|
||||
state.mark(5);
|
||||
|
||||
let mut buf = Buffer::empty(Rect::new(0, 0, 15, 10));
|
||||
StatefulWidget::render(table.clone(), Rect::new(0, 0, 15, 10), &mut buf, &mut state);
|
||||
let expected = Buffer::with_lines(Text::from(vec![
|
||||
"• Cell Cell ".into(),
|
||||
"⦾ Cell Cell ".into(),
|
||||
" Cell Cell ".into(),
|
||||
"⦾ Cell Cell ".into(),
|
||||
" Cell Cell ".into(),
|
||||
"⦾ Cell Cell ".into(),
|
||||
" Cell Cell ".into(),
|
||||
" ".into(),
|
||||
" ".into(),
|
||||
" ".into(),
|
||||
]));
|
||||
assert_eq!(buf, expected);
|
||||
|
||||
state.mark(0);
|
||||
|
||||
let mut buf = Buffer::empty(Rect::new(0, 0, 15, 10));
|
||||
StatefulWidget::render(table.clone(), Rect::new(0, 0, 15, 10), &mut buf, &mut state);
|
||||
let expected = Buffer::with_lines(Text::from(vec![
|
||||
"⦿ Cell Cell ".into(),
|
||||
"⦾ Cell Cell ".into(),
|
||||
" Cell Cell ".into(),
|
||||
"⦾ Cell Cell ".into(),
|
||||
" Cell Cell ".into(),
|
||||
"⦾ Cell Cell ".into(),
|
||||
" Cell Cell ".into(),
|
||||
" ".into(),
|
||||
" ".into(),
|
||||
" ".into(),
|
||||
]));
|
||||
assert_eq!(buf, expected);
|
||||
|
||||
state.select(Some(1));
|
||||
|
||||
let mut buf = Buffer::empty(Rect::new(0, 0, 15, 10));
|
||||
StatefulWidget::render(table.clone(), Rect::new(0, 0, 15, 10), &mut buf, &mut state);
|
||||
let expected = Buffer::with_lines(Text::from(vec![
|
||||
"⦾ Cell Cell ".into(),
|
||||
"⦿ Cell Cell ".into(),
|
||||
" Cell Cell ".into(),
|
||||
"⦾ Cell Cell ".into(),
|
||||
" Cell Cell ".into(),
|
||||
"⦾ Cell Cell ".into(),
|
||||
" Cell Cell ".into(),
|
||||
" ".into(),
|
||||
" ".into(),
|
||||
" ".into(),
|
||||
]));
|
||||
assert_eq!(buf, expected);
|
||||
|
||||
state.unmark(0);
|
||||
|
||||
let mut buf = Buffer::empty(Rect::new(0, 0, 15, 10));
|
||||
StatefulWidget::render(table.clone(), Rect::new(0, 0, 15, 10), &mut buf, &mut state);
|
||||
let expected = Buffer::with_lines(Text::from(vec![
|
||||
" Cell Cell ".into(),
|
||||
"⦿ Cell Cell ".into(),
|
||||
" Cell Cell ".into(),
|
||||
"⦾ Cell Cell ".into(),
|
||||
" Cell Cell ".into(),
|
||||
"⦾ Cell Cell ".into(),
|
||||
" Cell Cell ".into(),
|
||||
" ".into(),
|
||||
" ".into(),
|
||||
" ".into(),
|
||||
]));
|
||||
assert_eq!(buf, expected);
|
||||
}
|
||||
}
|
||||
|
||||
// test how constraints interact with table column width allocation
|
||||
@@ -1590,214 +1388,6 @@ mod tests {
|
||||
assert_eq!(table.get_columns_widths(10, 0), [(0, 5), (5, 5)]);
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn test_table_with_selection_and_marks<'line, Lines, Marks>(
|
||||
highlight_spacing: HighlightSpacing,
|
||||
columns: u16,
|
||||
spacing: u16,
|
||||
selection: Option<usize>,
|
||||
marks: Marks,
|
||||
expected: Lines,
|
||||
) where
|
||||
Lines: IntoIterator,
|
||||
Lines::Item: Into<Line<'line>>,
|
||||
Marks: IntoIterator<Item = usize>,
|
||||
{
|
||||
let table = Table::default()
|
||||
.rows(vec![Row::new(vec!["ABCDE", "12345"])])
|
||||
.highlight_spacing(highlight_spacing)
|
||||
.highlight_symbol(">>>")
|
||||
.mark_symbol(" MMM ")
|
||||
.mark_highlight_symbol(" >M> ")
|
||||
.column_spacing(spacing);
|
||||
let area = Rect::new(0, 0, columns, 3);
|
||||
let mut buf = Buffer::empty(area);
|
||||
let mut state = TableState::default().with_selected(selection);
|
||||
for mark in marks {
|
||||
state.mark(mark);
|
||||
}
|
||||
StatefulWidget::render(table, area, &mut buf, &mut state);
|
||||
assert_eq!(buf, Buffer::with_lines(expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn highlight_symbol_mark_symbol_and_column_spacing_with_highlight_spacing() {
|
||||
// no highlight_symbol or mark_symbol rendered ever
|
||||
test_table_with_selection_and_marks(
|
||||
HighlightSpacing::Never,
|
||||
15, // width
|
||||
0, // spacing
|
||||
None, // selection
|
||||
[], // marks
|
||||
[
|
||||
"ABCDE 12345 ", /* default layout is Flex::Start but columns length
|
||||
* constraints are calculated as `max_area / n_columns`,
|
||||
* i.e. they are distributed amongst available space */
|
||||
" ", // row 2
|
||||
" ", // row 3
|
||||
],
|
||||
);
|
||||
// no highlight_symbol or mark_symbol rendered ever
|
||||
test_table_with_selection_and_marks(
|
||||
HighlightSpacing::Never,
|
||||
15, // width
|
||||
0, // spacing
|
||||
None, // selection
|
||||
[0], // marks
|
||||
[
|
||||
"ABCDE 12345 ", /* default layout is Flex::Start but columns length
|
||||
* constraints are calculated as `max_area / n_columns`,
|
||||
* i.e. they are distributed amongst available space */
|
||||
" ", // row 2
|
||||
" ", // row 3
|
||||
],
|
||||
);
|
||||
// no highlight_symbol or mark_symbol rendered ever
|
||||
test_table_with_selection_and_marks(
|
||||
HighlightSpacing::Never,
|
||||
15, // width
|
||||
0, // spacing
|
||||
None, // selection
|
||||
[], // marks
|
||||
[
|
||||
"ABCDE 12345 ", /* default layout is Flex::Start but columns length
|
||||
* constraints are calculated as `max_area / n_columns`,
|
||||
* i.e. they are distributed amongst available space */
|
||||
" ", // row 2
|
||||
" ", // row 3
|
||||
],
|
||||
);
|
||||
|
||||
// no highlight_symbol or mark_symbol rendered
|
||||
test_table_with_selection_and_marks(
|
||||
HighlightSpacing::WhenSelected,
|
||||
15, // width
|
||||
0, // spacing
|
||||
None, // selection
|
||||
[], // marks
|
||||
[
|
||||
"ABCDE 12345 ", /* default layout is Flex::Start but columns length
|
||||
* constraints are calculated as `max_area / n_columns`,
|
||||
* i.e. they are distributed amongst available space */
|
||||
" ", // row 2
|
||||
" ", // row 3
|
||||
],
|
||||
);
|
||||
|
||||
// mark_symbol rendered
|
||||
test_table_with_selection_and_marks(
|
||||
HighlightSpacing::WhenSelected,
|
||||
15, // width
|
||||
0, // spacing
|
||||
None, // selection
|
||||
[0], // marks
|
||||
[
|
||||
" MMM ABCDE12345", /* default layout is Flex::Start but columns length
|
||||
* constraints are calculated as `max_area / n_columns`,
|
||||
* i.e. they are distributed amongst available space */
|
||||
" ", // row 2
|
||||
" ", // row 3
|
||||
],
|
||||
);
|
||||
|
||||
// highlight symbol rendered with mark symbol width
|
||||
test_table_with_selection_and_marks(
|
||||
HighlightSpacing::WhenSelected,
|
||||
15, // width
|
||||
0, // spacing
|
||||
Some(0), // selection
|
||||
[], // marks
|
||||
[
|
||||
">>> ABCDE12345", /* default layout is Flex::Start but columns length
|
||||
* constraints are calculated as `max_area / n_columns`,
|
||||
* i.e. they are distributed amongst available space */
|
||||
" ", // row 2
|
||||
" ", // row 3
|
||||
],
|
||||
);
|
||||
|
||||
// mark highlight symbol rendered
|
||||
test_table_with_selection_and_marks(
|
||||
HighlightSpacing::WhenSelected,
|
||||
15, // width
|
||||
0, // spacing
|
||||
Some(0), // selection
|
||||
[0], // marks
|
||||
[
|
||||
" >M> ABCDE12345", /* default layout is Flex::Start but columns length
|
||||
* constraints are calculated as `max_area / n_columns`,
|
||||
* i.e. they are distributed amongst available space */
|
||||
" ", // row 2
|
||||
" ", // row 3
|
||||
],
|
||||
);
|
||||
|
||||
// no highlight_symbol or mark_symbol rendered
|
||||
test_table_with_selection_and_marks(
|
||||
HighlightSpacing::Always,
|
||||
15, // width
|
||||
0, // spacing
|
||||
None, // selection
|
||||
[], // marks
|
||||
[
|
||||
" ABCDE12345", /* default layout is Flex::Start but columns length
|
||||
* constraints are calculated as `max_area / n_columns`,
|
||||
* i.e. they are distributed amongst available space */
|
||||
" ", // row 2
|
||||
" ", // row 3
|
||||
],
|
||||
);
|
||||
|
||||
// mark_symbol rendered
|
||||
test_table_with_selection_and_marks(
|
||||
HighlightSpacing::Always,
|
||||
15, // width
|
||||
0, // spacing
|
||||
None, // selection
|
||||
[0], // marks
|
||||
[
|
||||
" MMM ABCDE12345", /* default layout is Flex::Start but columns length
|
||||
* constraints are calculated as `max_area / n_columns`,
|
||||
* i.e. they are distributed amongst available space */
|
||||
" ", // row 2
|
||||
" ", // row 3
|
||||
],
|
||||
);
|
||||
|
||||
// highlight symbol rendered with mark symbol width
|
||||
test_table_with_selection_and_marks(
|
||||
HighlightSpacing::Always,
|
||||
15, // width
|
||||
0, // spacing
|
||||
Some(0), // selection
|
||||
[], // marks
|
||||
[
|
||||
">>> ABCDE12345", /* default layout is Flex::Start but columns length
|
||||
* constraints are calculated as `max_area / n_columns`,
|
||||
* i.e. they are distributed amongst available space */
|
||||
" ", // row 2
|
||||
" ", // row 3
|
||||
],
|
||||
);
|
||||
|
||||
// mark highlight symbol rendered
|
||||
test_table_with_selection_and_marks(
|
||||
HighlightSpacing::Always,
|
||||
15, // width
|
||||
0, // spacing
|
||||
Some(0), // selection
|
||||
[0], // marks
|
||||
[
|
||||
" >M> ABCDE12345", /* default layout is Flex::Start but columns length
|
||||
* constraints are calculated as `max_area / n_columns`,
|
||||
* i.e. they are distributed amongst available space */
|
||||
" ", // row 2
|
||||
" ", // row 3
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn test_table_with_selection<'line, Lines>(
|
||||
highlight_spacing: HighlightSpacing,
|
||||
|
||||
@@ -49,7 +49,6 @@
|
||||
pub struct TableState {
|
||||
pub(crate) offset: usize,
|
||||
pub(crate) selected: Option<usize>,
|
||||
pub(crate) marked: Vec<usize>,
|
||||
}
|
||||
|
||||
impl TableState {
|
||||
@@ -65,7 +64,6 @@ impl TableState {
|
||||
Self {
|
||||
offset: 0,
|
||||
selected: None,
|
||||
marked: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,78 +175,6 @@ impl TableState {
|
||||
self.offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the index of the row as marked
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ratatui::{prelude::*, widgets::*};
|
||||
/// let mut state = TableState::default();
|
||||
/// state.mark(1);
|
||||
/// ```
|
||||
pub fn mark(&mut self, index: usize) {
|
||||
if !self.marked.contains(&index) {
|
||||
self.marked.push(index);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the index of the row as unmarked
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ratatui::{prelude::*, widgets::*};
|
||||
/// let mut state = TableState::default();
|
||||
/// state.unmark(1);
|
||||
/// ```
|
||||
pub fn unmark(&mut self, index: usize) {
|
||||
self.marked.retain(|i| *i != index);
|
||||
}
|
||||
|
||||
/// Toggles the index of the row as marked or unmarked
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ratatui::{prelude::*, widgets::*};
|
||||
/// let mut state = TableState::default();
|
||||
/// state.toggle_mark(1);
|
||||
/// ```
|
||||
pub fn toggle_mark(&mut self, index: usize) {
|
||||
if self.marked.contains(&index) {
|
||||
self.unmark(index);
|
||||
} else {
|
||||
self.mark(index);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a iterator of all marked rows
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ratatui::{prelude::*, widgets::*};
|
||||
/// # use itertools::Itertools;
|
||||
/// let mut state = TableState::default();
|
||||
/// state.marked().contains(&1);
|
||||
/// ```
|
||||
pub fn marked(&self) -> std::slice::Iter<'_, usize> {
|
||||
self.marked.iter()
|
||||
}
|
||||
|
||||
/// Clears all marks from all rows
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ratatui::{prelude::*, widgets::*};
|
||||
/// let mut state = TableState::default();
|
||||
/// state.clear_marks();
|
||||
/// ```
|
||||
pub fn clear_marks(&mut self) {
|
||||
self.marked.drain(..);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
// not too happy about the redundancy in these tests,
|
||||
// but if that helps readability then it's ok i guess /shrug
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use ratatui::{backend::TestBackend, prelude::*, widgets::*};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
@@ -99,8 +98,7 @@ const DEFAULT_STATE_REPR: &str = r#"{
|
||||
},
|
||||
"table": {
|
||||
"offset": 0,
|
||||
"selected": null,
|
||||
"marked": []
|
||||
"selected": null
|
||||
},
|
||||
"scrollbar": {
|
||||
"content_length": 10,
|
||||
@@ -137,8 +135,7 @@ const SELECTED_STATE_REPR: &str = r#"{
|
||||
},
|
||||
"table": {
|
||||
"offset": 0,
|
||||
"selected": 1,
|
||||
"marked": []
|
||||
"selected": 1
|
||||
},
|
||||
"scrollbar": {
|
||||
"content_length": 10,
|
||||
@@ -177,8 +174,7 @@ const SCROLLED_STATE_REPR: &str = r#"{
|
||||
},
|
||||
"table": {
|
||||
"offset": 4,
|
||||
"selected": 8,
|
||||
"marked": []
|
||||
"selected": 8
|
||||
},
|
||||
"scrollbar": {
|
||||
"content_length": 10,
|
||||
|
||||
Reference in New Issue
Block a user