rat_ftable/
rowselection.rs1use crate::event::TableOutcome;
2use crate::{TableSelection, TableState};
3use rat_event::{ct_event, ConsumedEvent, HandleEvent, MouseOnly, Regular};
4use rat_focus::HasFocus;
5use rat_scrolled::event::ScrollOutcome;
6use rat_scrolled::ScrollAreaState;
7use std::cmp::{max, min};
8
9#[derive(Debug, Default, Clone)]
16pub struct RowSelection {
17 pub lead_row: Option<usize>,
19 pub scroll_selected: bool,
21}
22
23impl TableSelection for RowSelection {
24 fn count(&self) -> usize {
25 if self.lead_row.is_some() {
26 1
27 } else {
28 0
29 }
30 }
31
32 fn is_selected_row(&self, row: usize) -> bool {
33 self.lead_row == Some(row)
34 }
35
36 fn is_selected_column(&self, _column: usize) -> bool {
37 false
38 }
39
40 fn is_selected_cell(&self, _column: usize, _row: usize) -> bool {
41 false
42 }
43
44 fn lead_selection(&self) -> Option<(usize, usize)> {
45 self.lead_row.map(|v| (0, v))
46 }
47}
48
49impl RowSelection {
50 pub fn new() -> RowSelection {
52 Self::default()
53 }
54
55 pub fn clear(&mut self) {
57 self.lead_row = None;
58 }
59
60 pub fn scroll_selected(&self) -> bool {
62 self.scroll_selected
63 }
64
65 pub fn set_scroll_selected(&mut self, scroll: bool) {
67 self.scroll_selected = scroll;
68 }
69
70 pub fn selected(&self) -> Option<usize> {
72 self.lead_row
73 }
74
75 pub fn has_selection(&self) -> bool {
77 self.lead_row.is_some()
78 }
79
80 pub fn select(&mut self, select: Option<usize>) -> bool {
83 let old_row = self.lead_row;
84 self.lead_row = select;
85 old_row != self.lead_row
86 }
87
88 pub fn items_added(&mut self, pos: usize, n: usize) {
90 if let Some(lead_row) = self.lead_row {
91 if lead_row > pos {
92 self.lead_row = Some(lead_row + n);
93 }
94 }
95 }
96
97 pub fn items_removed(&mut self, pos: usize, n: usize, maximum: usize) {
102 if let Some(lead_row) = self.lead_row {
103 if lead_row > pos {
104 self.lead_row = Some(lead_row.saturating_sub(n));
105 } else if lead_row == pos && lead_row == maximum {
106 self.lead_row = Some(lead_row.saturating_sub(1));
107 }
108 }
109 }
110
111 pub fn move_to(&mut self, select: usize, maximum: usize) -> bool {
113 let old_row = self.lead_row;
114 self.lead_row = Some(min(select, maximum));
115 old_row != self.lead_row
116 }
117
118 pub fn move_down(&mut self, n: usize, maximum: usize) -> bool {
120 let old_row = self.lead_row;
121 self.lead_row = Some(self.lead_row.map_or(0, |v| min(v + n, maximum)));
122 old_row != self.lead_row
123 }
124
125 pub fn move_up(&mut self, n: usize, maximum: usize) -> bool {
127 let old_row = self.lead_row;
128 self.lead_row = Some(self.lead_row.map_or(maximum, |v| v.saturating_sub(n)));
129 old_row != self.lead_row
130 }
131}
132
133impl HandleEvent<crossterm::event::Event, Regular, TableOutcome> for TableState<RowSelection> {
134 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TableOutcome {
135 let res = if self.is_focused() {
136 match event {
137 ct_event!(keycode press Up) => {
138 if self.move_up(1) {
139 TableOutcome::Selected
140 } else {
141 TableOutcome::Unchanged
142 }
143 }
144 ct_event!(keycode press Down) => {
145 if self.move_down(1) {
146 TableOutcome::Selected
147 } else {
148 TableOutcome::Unchanged
149 }
150 }
151 ct_event!(keycode press CONTROL-Up)
152 | ct_event!(keycode press CONTROL-Home)
153 | ct_event!(keycode press Home) => {
154 if self.move_to(0) {
155 TableOutcome::Selected
156 } else {
157 TableOutcome::Unchanged
158 }
159 }
160 ct_event!(keycode press CONTROL-Down)
161 | ct_event!(keycode press CONTROL-End)
162 | ct_event!(keycode press End) => {
163 if self.move_to(self.rows.saturating_sub(1)) {
164 TableOutcome::Selected
165 } else {
166 TableOutcome::Unchanged
167 }
168 }
169 ct_event!(keycode press PageUp) => {
170 if self.move_up(max(1, self.page_len().saturating_sub(1))) {
171 TableOutcome::Selected
172 } else {
173 TableOutcome::Unchanged
174 }
175 }
176 ct_event!(keycode press PageDown) => {
177 if self.move_down(max(1, self.page_len().saturating_sub(1))) {
178 TableOutcome::Selected
179 } else {
180 TableOutcome::Unchanged
181 }
182 }
183 ct_event!(keycode press Left) => {
184 if self.scroll_left(1) {
185 TableOutcome::Changed
186 } else {
187 TableOutcome::Unchanged
188 }
189 }
190 ct_event!(keycode press Right) => {
191 if self.scroll_right(1) {
192 TableOutcome::Changed
193 } else {
194 TableOutcome::Unchanged
195 }
196 }
197 ct_event!(keycode press CONTROL-Left) => {
198 if self.scroll_to_x(0) {
199 TableOutcome::Changed
200 } else {
201 TableOutcome::Unchanged
202 }
203 }
204 ct_event!(keycode press CONTROL-Right) => {
205 if self.scroll_to_x(self.x_max_offset()) {
206 TableOutcome::Changed
207 } else {
208 TableOutcome::Unchanged
209 }
210 }
211 _ => TableOutcome::Continue,
212 }
213 } else {
214 TableOutcome::Continue
215 };
216
217 if res == TableOutcome::Continue {
218 self.handle(event, MouseOnly)
219 } else {
220 res
221 }
222 }
223}
224
225impl HandleEvent<crossterm::event::Event, MouseOnly, TableOutcome> for TableState<RowSelection> {
226 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TableOutcome {
227 let mut r = match event {
228 ct_event!(mouse any for m) if self.mouse.drag(self.table_area, m) => {
229 if self.move_to(self.row_at_drag((m.column, m.row))) {
230 TableOutcome::Selected
231 } else {
232 TableOutcome::Unchanged
233 }
234 }
235 ct_event!(mouse down Left for column, row) => {
236 if self.table_area.contains((*column, *row).into()) {
237 if let Some(new_row) = self.row_at_clicked((*column, *row)) {
238 if self.move_to(new_row) {
239 TableOutcome::Selected
240 } else {
241 TableOutcome::Unchanged
242 }
243 } else {
244 TableOutcome::Continue
245 }
246 } else {
247 TableOutcome::Continue
248 }
249 }
250
251 _ => TableOutcome::Continue,
252 };
253
254 r = r.or_else(|| {
255 let mut sas = ScrollAreaState::new()
256 .area(self.inner)
257 .h_scroll(&mut self.hscroll)
258 .v_scroll(&mut self.vscroll);
259 match sas.handle(event, MouseOnly) {
260 ScrollOutcome::Up(v) => {
261 if self.selection.scroll_selected() {
262 if self.move_up(1) {
263 TableOutcome::Selected
264 } else {
265 TableOutcome::Unchanged
266 }
267 } else {
268 if self.scroll_up(v) {
269 TableOutcome::Changed
270 } else {
271 TableOutcome::Unchanged
272 }
273 }
274 }
275 ScrollOutcome::Down(v) => {
276 if self.selection.scroll_selected() {
277 if self.move_down(1) {
278 TableOutcome::Selected
279 } else {
280 TableOutcome::Unchanged
281 }
282 } else {
283 if self.scroll_down(v) {
284 TableOutcome::Changed
285 } else {
286 TableOutcome::Unchanged
287 }
288 }
289 }
290 ScrollOutcome::VPos(v) => {
291 if self.selection.scroll_selected {
292 if self.move_to(self.remap_offset_selection(v)) {
293 TableOutcome::Selected
294 } else {
295 TableOutcome::Unchanged
296 }
297 } else {
298 if self.set_row_offset(self.vscroll.limited_offset(v)) {
299 TableOutcome::Changed
300 } else {
301 TableOutcome::Unchanged
302 }
303 }
304 }
305 ScrollOutcome::Left(v) => {
306 if self.scroll_left(v) {
307 TableOutcome::Changed
308 } else {
309 TableOutcome::Unchanged
310 }
311 }
312 ScrollOutcome::Right(v) => {
313 if self.scroll_right(v) {
314 TableOutcome::Changed
315 } else {
316 TableOutcome::Unchanged
317 }
318 }
319 ScrollOutcome::HPos(v) => {
320 if self.set_x_offset(self.hscroll.limited_offset(v)) {
321 TableOutcome::Changed
322 } else {
323 TableOutcome::Unchanged
324 }
325 }
326
327 ScrollOutcome::Continue => TableOutcome::Continue,
328 ScrollOutcome::Unchanged => TableOutcome::Unchanged,
329 ScrollOutcome::Changed => TableOutcome::Changed,
330 }
331 });
332
333 r
334 }
335}
336
337pub fn handle_events(
341 state: &mut TableState<RowSelection>,
342 focus: bool,
343 event: &crossterm::event::Event,
344) -> TableOutcome {
345 state.focus.set(focus);
346 state.handle(event, Regular)
347}
348
349pub fn handle_mouse_events(
351 state: &mut TableState<RowSelection>,
352 event: &crossterm::event::Event,
353) -> TableOutcome {
354 state.handle(event, MouseOnly)
355}