ratatui-org / ratatui

Rust library that's all about cooking up terminal user interfaces (TUIs) 👨‍🍳🐀
https://ratatui.rs
MIT License
8.86k stars 269 forks source link

feat(state): allow conversion between `ListState` and `TableState` #1155

Closed orhun closed 1 month ago

orhun commented 1 month ago

In my TUI apps I often use Table instead of List since a Table with a single column is effectively a List. This also gives me the flexibility of adding columns at any time without changing List to Table. That's why I mostly have a helper struct called SelectableList.

However, you see, I need to use TableState for storing the state. I would like to extend this to support ListState but it needs manual conversion and I thought it would be better to do that in Ratatui itself.

So,

This PR adds a From implementation for ListState and TableState for using them interchangeably. For example:

let list_state = ListState::default();
let table_state: TableState = list_state.into();
codecov[bot] commented 1 month ago

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Project coverage is 94.2%. Comparing base (8061813) to head (c38aea8).

Additional details and impacted files ```diff @@ Coverage Diff @@ ## main #1155 +/- ## ===================================== Coverage 94.2% 94.2% ===================================== Files 60 60 Lines 14509 14533 +24 ===================================== + Hits 13670 13694 +24 Misses 839 839 ```

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

joshka commented 1 month ago

I'm not sure that I fully understand the need for this properly. Can you expand on the use case a bit from the perspective of the app writer? This could do with some simple example code, or perhaps an integration test that helps show the need for this.

orhun commented 1 month ago

Yes, sure. I understand that it is a bit specific, but take the following code for example:

#[derive(Debug)]
pub struct SelectableList<T> {
    pub items: Vec<T>,
    pub state: TableState,
}

terminal.draw(|frame| {
    let mut list = SelectableList {
        items: vec!["data1", "data2", "data3"],
        state: TableState::default(),
    };
    list.state.select(Some(1));

    frame.render_stateful_widget(Table::default(), frame.size(), &mut list.state);
    frame.render_stateful_widget(List::default(), frame.size(), &mut list.state.into()); // into
})?;

As you can see I can use the state for both cases. Not sure if that is enough to clarify things + this is too much of an edge case.

joshka commented 1 month ago

As you can see I can use the state for both cases. Not sure if that is enough to clarify things + this is too much of an edge case.

I see that you're using the functionality, but what I'm not seeing is the reason that you're doing so. Can you take one or two steps back from the implementation of this, and explain it from the perspective of where you need(ed) this in your app?

orhun commented 1 month ago

To be honest I don't quite remember :D I usually take notes while I'm writing code (to not forget ideas) and this was one of the entries under "things that I need while building Ratatui apps". I need to time travel into my coding history and find out where exactly I needed this.

Anyways, I can come back to this with a more concrete example when I need it again. Feel free to close.

joshka commented 1 month ago

To be honest I don't quite remember :D I usually take notes while I'm writing code (to not forget ideas) and this was one of the entries under "things that I need while building Ratatui apps". I need to time travel into my coding history and find out where exactly I needed this.

Anyways, I can come back to this with a more concrete example when I need it again. Feel free to close.

Sounds fine.