use crate::core::geometry::Rect;
use crate::core::event::Event;
use crate::core::state::StateFlags;
use crate::core::history::HistoryManager;
use crate::terminal::Terminal;
use super::view::View;
use super::list_viewer::{ListViewer, ListViewerState};
pub struct HistoryViewer {
bounds: Rect,
history_id: u16,
items: Vec<String>,
list_state: ListViewerState,
state: StateFlags,
}
impl HistoryViewer {
pub fn new(bounds: Rect, history_id: u16) -> Self {
let items = HistoryManager::get_list(history_id);
let mut list_state = ListViewerState::new();
list_state.set_range(items.len());
Self {
bounds,
history_id,
items,
list_state,
state: 0,
}
}
pub fn refresh(&mut self) {
self.items = HistoryManager::get_list(self.history_id);
self.list_state.set_range(self.items.len());
}
pub fn get_selected_item(&self) -> Option<&str> {
self.list_state.focused.and_then(|idx| self.items.get(idx).map(|s| s.as_str()))
}
pub fn item_count(&self) -> usize {
self.items.len()
}
}
impl View for HistoryViewer {
fn bounds(&self) -> Rect {
self.bounds
}
fn set_bounds(&mut self, bounds: Rect) {
self.bounds = bounds;
}
fn draw(&mut self, terminal: &mut Terminal) {
use crate::core::palette::colors;
use crate::core::draw::DrawBuffer;
use super::view::write_line_to_terminal;
let width = self.bounds.width() as usize;
let height = self.bounds.height() as usize;
let color_normal = if self.is_focused() {
colors::LISTBOX_FOCUSED
} else {
colors::LISTBOX_NORMAL
};
let color_selected = if self.is_focused() {
colors::LISTBOX_SELECTED_FOCUSED
} else {
colors::LISTBOX_SELECTED
};
for i in 0..height {
let mut buf = DrawBuffer::new(width);
let item_idx = self.list_state.top_item + i;
if item_idx < self.items.len() {
let is_selected = Some(item_idx) == self.list_state.focused;
let color = if is_selected { color_selected } else { color_normal };
let text = &self.items[item_idx];
buf.move_str(0, text, color);
let text_len = text.len();
if text_len < width {
buf.move_char(text_len, ' ', color, width - text_len);
}
} else {
buf.move_char(0, ' ', color_normal, width);
}
write_line_to_terminal(terminal, self.bounds.a.x, self.bounds.a.y + i as i16, &buf);
}
}
fn handle_event(&mut self, event: &mut Event) {
self.handle_list_event(event);
}
fn can_focus(&self) -> bool {
true
}
fn state(&self) -> StateFlags {
self.state
}
fn set_state(&mut self, state: StateFlags) {
self.state = state;
}
fn set_list_selection(&mut self, index: usize) {
if index < self.items.len() {
let visible_rows = self.bounds.height() as usize;
self.list_state.focus_item(index, visible_rows);
}
}
fn get_list_selection(&self) -> usize {
self.list_state.focused.unwrap_or(0)
}
}
impl ListViewer for HistoryViewer {
fn list_state(&self) -> &ListViewerState {
&self.list_state
}
fn list_state_mut(&mut self) -> &mut ListViewerState {
&mut self.list_state
}
fn get_text(&self, item: usize, _max_len: usize) -> String {
self.items.get(item).cloned().unwrap_or_default()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_history_viewer_creation() {
HistoryManager::clear_all();
HistoryManager::add(1, "test1".to_string());
HistoryManager::add(1, "test2".to_string());
let viewer = HistoryViewer::new(Rect::new(0, 0, 20, 10), 1);
assert_eq!(viewer.item_count(), 2);
assert_eq!(viewer.get_selected_item(), Some("test2")); }
#[test]
fn test_history_viewer_refresh() {
HistoryManager::clear_all();
HistoryManager::add(2, "item1".to_string());
let mut viewer = HistoryViewer::new(Rect::new(0, 0, 20, 10), 2);
assert_eq!(viewer.item_count(), 1);
HistoryManager::add(2, "item2".to_string());
HistoryManager::add(2, "item3".to_string());
viewer.refresh();
assert_eq!(viewer.item_count(), 3);
}
#[test]
fn test_history_viewer_empty() {
HistoryManager::clear_all();
let viewer = HistoryViewer::new(Rect::new(0, 0, 20, 10), 99);
assert_eq!(viewer.item_count(), 0);
assert_eq!(viewer.get_selected_item(), None);
}
}