ane/frontend/tui/
list_dialog.rs1use ratatui::{
2 Frame,
3 layout::Rect,
4 style::{Color, Modifier, Style},
5 text::{Line, Span},
6 widgets::{Block, Borders, Clear, List, ListItem, ListState},
7};
8
9use crate::commands::chord_engine::types::ListItem as ChordListItem;
10
11#[derive(Debug, Clone)]
12pub struct ListDialog {
13 pub items: Vec<ChordListItem>,
14 pub selected: usize,
15 pub scroll_offset: usize,
16}
17
18impl ListDialog {
19 pub fn new(items: Vec<ChordListItem>) -> Self {
20 Self {
21 items,
22 selected: 0,
23 scroll_offset: 0,
24 }
25 }
26
27 pub fn move_up(&mut self) {
28 if self.selected > 0 {
29 self.selected -= 1;
30 }
31 }
32
33 pub fn move_down(&mut self) {
34 if !self.items.is_empty() && self.selected < self.items.len() - 1 {
35 self.selected += 1;
36 }
37 }
38}
39
40pub fn render(frame: &mut Frame, dialog: &ListDialog) {
41 let area = frame.area();
42 let width = (area.width * 3 / 4).max(30).min(area.width);
43 let height = (area.height * 3 / 4).max(10).min(area.height);
44 let x = (area.width.saturating_sub(width)) / 2;
45 let y = (area.height.saturating_sub(height)) / 2;
46 let dialog_area = Rect::new(x, y, width, height);
47
48 frame.render_widget(Clear, dialog_area);
49
50 if dialog.items.is_empty() {
51 let block = Block::default()
52 .title(" List Results ")
53 .borders(Borders::ALL)
54 .style(Style::default().fg(Color::White).bg(Color::DarkGray));
55 let inner = block.inner(dialog_area);
56 frame.render_widget(block, dialog_area);
57 let msg = List::new(vec![ListItem::new("No results")]);
58 frame.render_widget(msg, inner);
59 return;
60 }
61
62 let block = Block::default()
63 .title(" List Results ")
64 .borders(Borders::ALL)
65 .style(Style::default().fg(Color::White).bg(Color::DarkGray));
66 let inner = block.inner(dialog_area);
67 frame.render_widget(block, dialog_area);
68
69 let visible_height = inner.height as usize;
70 let items: Vec<ListItem> = dialog
71 .items
72 .iter()
73 .enumerate()
74 .map(|(i, item)| {
75 let line_num = format!("{:>4}:{:<3}", item.line + 1, item.col + 1);
76 let style = if i == dialog.selected {
77 Style::default()
78 .fg(Color::Black)
79 .bg(Color::Cyan)
80 .add_modifier(Modifier::BOLD)
81 } else {
82 Style::default().fg(Color::White)
83 };
84 ListItem::new(Line::from(vec![
85 Span::styled(
86 line_num,
87 Style::default().fg(if i == dialog.selected {
88 Color::Black
89 } else {
90 Color::DarkGray
91 }),
92 ),
93 Span::raw(" "),
94 Span::styled(&item.val, style),
95 ]))
96 .style(style)
97 })
98 .collect();
99
100 let mut list_state = ListState::default();
101 list_state.select(Some(dialog.selected));
102
103 let list = List::new(items).highlight_style(
104 Style::default()
105 .fg(Color::Black)
106 .bg(Color::Cyan)
107 .add_modifier(Modifier::BOLD),
108 );
109
110 let _ = visible_height;
112 frame.render_stateful_widget(list, inner, &mut list_state);
113}