Data structures and logic was changing very fast. Don't have the time to break downt the individual sections ATM. Sorry future reader (me).
128 lines
3.5 KiB
Rust
128 lines
3.5 KiB
Rust
// This file is part of Deja-vu.
|
|
//
|
|
// Deja-vu is free software: you can redistribute it and/or modify it
|
|
// under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Deja-vu is distributed in the hope that it will be useful, but
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
// See the GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
|
|
//
|
|
use std::collections::{hash_map, HashMap};
|
|
|
|
use ratatui::widgets::ListState;
|
|
|
|
#[derive(Default, Debug, Clone, PartialEq)]
|
|
pub struct StatefulList<T> {
|
|
pub state: ListState,
|
|
pub items: Vec<T>,
|
|
pub last_selected: Option<usize>,
|
|
pub selected: HashMap<usize, bool>,
|
|
}
|
|
|
|
impl<T: Clone> StatefulList<T> {
|
|
#[must_use]
|
|
pub fn new() -> StatefulList<T> {
|
|
StatefulList {
|
|
state: ListState::default(),
|
|
items: Vec::new(),
|
|
last_selected: None,
|
|
selected: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
pub fn clear_items(&mut self) {
|
|
// Clear list and rebuild with provided items
|
|
self.items.clear();
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn get_selected(&self) -> Option<&T> {
|
|
if let Some(i) = self.state.selected() {
|
|
self.items.get(i)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn pop_selected(&mut self) -> Option<T> {
|
|
if let Some(i) = self.state.selected() {
|
|
Some(self.items[i].clone())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn select_first_item(&mut self) {
|
|
if self.items.is_empty() {
|
|
self.state.select(None);
|
|
} else {
|
|
self.state.select(Some(0));
|
|
}
|
|
self.last_selected = None;
|
|
}
|
|
|
|
pub fn set_items(&mut self, items: Vec<T>) {
|
|
// Clear list and rebuild with provided items
|
|
self.clear_items();
|
|
for item in items {
|
|
self.items.push(item);
|
|
}
|
|
|
|
// Reset state and select first item (if available)
|
|
self.state = ListState::default();
|
|
self.select_first_item();
|
|
}
|
|
|
|
pub fn next(&mut self) {
|
|
if self.items.is_empty() {
|
|
return;
|
|
}
|
|
let i = match self.state.selected() {
|
|
Some(i) => {
|
|
if i >= self.items.len() - 1 {
|
|
0
|
|
} else {
|
|
i + 1
|
|
}
|
|
}
|
|
None => self.last_selected.unwrap_or(0),
|
|
};
|
|
self.state.select(Some(i));
|
|
}
|
|
|
|
pub fn previous(&mut self) {
|
|
if self.items.is_empty() {
|
|
return;
|
|
}
|
|
let i = match self.state.selected() {
|
|
Some(i) => {
|
|
if i == 0 {
|
|
self.items.len() - 1
|
|
} else {
|
|
i - 1
|
|
}
|
|
}
|
|
None => self.last_selected.unwrap_or(0),
|
|
};
|
|
self.state.select(Some(i));
|
|
}
|
|
pub fn toggle_selected(&mut self) {
|
|
if let Some(i) = self.state.selected() {
|
|
if let hash_map::Entry::Vacant(e) = self.selected.entry(i) {
|
|
// Was NOT selected, WILL be shortly
|
|
self.last_selected = Some(i);
|
|
e.insert(true);
|
|
} else {
|
|
// WAS selected, will NOT be shortly
|
|
self.last_selected = None;
|
|
self.selected.remove(&i);
|
|
}
|
|
}
|
|
}
|
|
}
|