Skip to main content

vtcode_tui/core_tui/session/file_palette/
navigation.rs

1use super::{FileEntry, FilePalette, PAGE_SIZE};
2
3impl FilePalette {
4    pub fn move_selection_up(&mut self) {
5        if self.filtered_files.is_empty() {
6            return;
7        }
8        if self.selected_index > 0 {
9            self.selected_index -= 1;
10        } else {
11            self.selected_index = self.filtered_files.len().saturating_sub(1);
12        }
13        self.update_page_from_selection();
14    }
15
16    pub fn move_selection_down(&mut self) {
17        if self.filtered_files.is_empty() {
18            return;
19        }
20        if self.selected_index + 1 < self.filtered_files.len() {
21            self.selected_index += 1;
22        } else {
23            self.selected_index = 0;
24        }
25        self.update_page_from_selection();
26    }
27
28    pub fn move_to_first(&mut self) {
29        if !self.filtered_files.is_empty() {
30            self.selected_index = 0;
31            self.current_page = 0;
32        }
33    }
34
35    pub fn move_to_last(&mut self) {
36        if !self.filtered_files.is_empty() {
37            self.selected_index = self.filtered_files.len().saturating_sub(1);
38            self.update_page_from_selection();
39        }
40    }
41
42    pub fn page_up(&mut self) {
43        if self.current_page > 0 {
44            self.current_page -= 1;
45            self.selected_index = self.current_page * PAGE_SIZE;
46        }
47    }
48
49    pub fn page_down(&mut self) {
50        let total_pages = self.total_pages();
51        if self.current_page + 1 < total_pages {
52            self.current_page += 1;
53            self.selected_index = self.current_page * PAGE_SIZE;
54        }
55    }
56
57    fn update_page_from_selection(&mut self) {
58        self.current_page = self.selected_index / PAGE_SIZE;
59    }
60
61    pub fn get_selected(&self) -> Option<&FileEntry> {
62        self.filtered_files.get(self.selected_index)
63    }
64
65    /// Get the best matching file entry based on current filter query
66    /// Used for Tab autocomplete - returns the first filtered file if any exist
67    #[allow(dead_code)]
68    pub fn get_best_match(&self) -> Option<&FileEntry> {
69        // Return the first file in filtered results (already sorted by score)
70        self.filtered_files.first()
71    }
72
73    /// Set selection to the best match
74    pub fn select_best_match(&mut self) {
75        if !self.filtered_files.is_empty() {
76            self.selected_index = 0;
77            self.current_page = 0;
78        }
79    }
80
81    pub fn current_page_items(&self) -> Vec<(usize, &FileEntry, bool)> {
82        let start = self.current_page * PAGE_SIZE;
83        let end = (start + PAGE_SIZE).min(self.filtered_files.len());
84
85        self.filtered_files[start..end]
86            .iter()
87            .enumerate()
88            .map(|(idx, entry)| {
89                let global_idx = start + idx;
90                let is_selected = global_idx == self.selected_index;
91                (global_idx, entry, is_selected)
92            })
93            .collect()
94    }
95
96    pub fn total_pages(&self) -> usize {
97        if self.filtered_files.is_empty() {
98            1
99        } else {
100            self.filtered_files.len().div_ceil(PAGE_SIZE)
101        }
102    }
103
104    pub fn current_page_number(&self) -> usize {
105        self.current_page + 1
106    }
107
108    pub fn total_items(&self) -> usize {
109        self.filtered_files.len()
110    }
111
112    pub fn is_empty(&self) -> bool {
113        self.filtered_files.is_empty()
114    }
115
116    pub fn filter_query(&self) -> &str {
117        &self.filter_query
118    }
119
120    pub fn has_files(&self) -> bool {
121        !self.all_files.is_empty()
122    }
123
124    pub fn has_more_items(&self) -> bool {
125        let end = ((self.current_page + 1) * PAGE_SIZE).min(self.filtered_files.len());
126        end < self.filtered_files.len()
127    }
128}