excel_cli/app/
navigation.rs

1use crate::app::AppState;
2use crate::utils::find_non_empty_cell;
3use crate::utils::Direction;
4
5impl AppState<'_> {
6    pub fn move_cursor(&mut self, delta_row: isize, delta_col: isize) {
7        // Calculate new position
8        let new_row = (self.selected_cell.0 as isize + delta_row).max(1) as usize;
9        let new_col = (self.selected_cell.1 as isize + delta_col).max(1) as usize;
10
11        // Update selected position
12        self.selected_cell = (new_row, new_col);
13
14        // Handle scrolling
15        self.handle_scrolling();
16    }
17
18    pub fn handle_scrolling(&mut self) {
19        if self.selected_cell.0 < self.start_row {
20            self.start_row = self.selected_cell.0;
21        } else if self.selected_cell.0 >= self.start_row + self.visible_rows {
22            self.start_row = self.selected_cell.0 - self.visible_rows + 1;
23        }
24
25        self.handle_column_scrolling();
26    }
27
28    pub fn jump_to_first_row(&mut self) {
29        let current_col = self.selected_cell.1;
30        self.selected_cell = (1, current_col);
31        self.handle_scrolling();
32        self.add_notification("Jumped to first row".to_string());
33    }
34
35    pub fn jump_to_last_row(&mut self) {
36        let sheet = self.workbook.get_current_sheet();
37        let current_col = self.selected_cell.1;
38
39        let max_row = sheet.max_rows;
40
41        self.selected_cell = (max_row, current_col);
42        self.handle_scrolling();
43        self.add_notification("Jumped to last row".to_string());
44    }
45
46    pub fn jump_to_first_column(&mut self) {
47        let current_row = self.selected_cell.0;
48        self.selected_cell = (current_row, 1);
49        self.handle_scrolling();
50        self.add_notification("Jumped to first column".to_string());
51    }
52
53    pub fn jump_to_first_non_empty_column(&mut self) {
54        let sheet = self.workbook.get_current_sheet();
55        let current_row = self.selected_cell.0;
56
57        let mut first_non_empty_col = 1; // Default to first column
58
59        if current_row < sheet.data.len() {
60            for col in 1..=sheet.max_cols {
61                if col < sheet.data[current_row].len()
62                    && !sheet.data[current_row][col].value.is_empty()
63                {
64                    first_non_empty_col = col;
65                    break;
66                }
67            }
68        }
69
70        self.selected_cell = (current_row, first_non_empty_col);
71        self.handle_scrolling();
72        self.add_notification("Jumped to first non-empty column".to_string());
73    }
74
75    pub fn jump_to_last_column(&mut self) {
76        let sheet = self.workbook.get_current_sheet();
77        let current_row = self.selected_cell.0;
78
79        let max_col = sheet.max_cols;
80
81        self.selected_cell = (current_row, max_col);
82        self.handle_scrolling();
83        self.add_notification("Jumped to last column".to_string());
84    }
85
86    fn jump_to_non_empty_cell(&mut self, direction: Direction) {
87        let sheet = self.workbook.get_current_sheet();
88        let max_bounds = (sheet.max_rows, sheet.max_cols);
89        let current_pos = self.selected_cell;
90
91        if let Some(new_pos) = find_non_empty_cell(sheet, current_pos, direction, max_bounds) {
92            self.selected_cell = new_pos;
93            self.handle_scrolling();
94
95            let dir_name = match direction {
96                Direction::Left => "left",
97                Direction::Right => "right",
98                Direction::Up => "up",
99                Direction::Down => "down",
100            };
101
102            // Re-fetch sheet to avoid borrow conflict
103            let sheet = self.workbook.get_current_sheet();
104
105            let (row, col) = self.selected_cell;
106            let is_cell_empty = row >= sheet.data.len()
107                || col >= sheet.data[row].len()
108                || sheet.data[row][col].value.is_empty();
109
110            let message = if is_cell_empty {
111                format!("Jumped to first non-empty cell ({dir_name})")
112            } else {
113                format!("Jumped to last non-empty cell ({dir_name})")
114            };
115
116            self.add_notification(message);
117        }
118    }
119
120    pub fn jump_to_prev_non_empty_cell_left(&mut self) {
121        self.jump_to_non_empty_cell(Direction::Left);
122    }
123
124    pub fn jump_to_prev_non_empty_cell_right(&mut self) {
125        self.jump_to_non_empty_cell(Direction::Right);
126    }
127
128    pub fn jump_to_prev_non_empty_cell_up(&mut self) {
129        self.jump_to_non_empty_cell(Direction::Up);
130    }
131
132    pub fn jump_to_prev_non_empty_cell_down(&mut self) {
133        self.jump_to_non_empty_cell(Direction::Down);
134    }
135
136    fn handle_column_scrolling(&mut self) {
137        self.ensure_column_visible(self.selected_cell.1);
138    }
139
140    pub fn ensure_column_visible(&mut self, column: usize) {
141        // If column is to the left of visible area, adjust start_col
142        if column < self.start_col {
143            self.start_col = column;
144            return;
145        }
146
147        let last_visible_col = self.start_col + self.visible_cols - 1;
148
149        // If column is to the right of visible area, adjust start_col to make it visible
150        if column > last_visible_col {
151            self.start_col = (column - self.visible_cols + 1).max(1);
152            return;
153        }
154
155        // If the column is already visible but at the right edge, try to add a margin
156        let sheet = self.workbook.get_current_sheet();
157        let max_col = sheet.max_cols;
158
159        // Only apply margin logic if not at the max column
160        if column < max_col && column == last_visible_col && self.visible_cols > 1 {
161            // Adjust start column to show more columns to the left
162            // This creates a margin on the right
163            self.start_col = (column - (self.visible_cols - 2)).max(1);
164        }
165    }
166}