densa_widgets/grid/
selection.rs1use std::ops::Range;
4
5use densa_grid::{CellText, GridModel, Palette};
6use densa_ui::{ActionKind, EventCx};
7
8use super::DataGrid;
9
10impl<M: GridModel> DataGrid<M> {
11 pub fn selected_rows(&self) -> Range<usize> {
14 let rows = self.model.rows();
15 if self.all {
16 return 0..rows;
17 }
18 let Some(focus) = self.selected() else {
19 return 0..0;
20 };
21 let anchor = self.anchor.map_or(focus, |a| a.row);
22 let end = (anchor.max(focus) + 1).min(rows);
24 anchor.min(focus).min(end)..end
25 }
26
27 pub fn select_rows(&mut self, cx: &mut EventCx<'_>, anchor: usize, row: usize) {
31 let Some(last) = self.model.rows().checked_sub(1) else {
32 return;
33 };
34 self.all = false;
35 self.anchor = None;
36 self.select(cx, Some(row.min(last)));
37 self.anchor = Some(self.selection_of(anchor.min(last)));
38 }
39
40 pub fn select_all(&mut self, cx: &mut EventCx<'_>) {
43 self.all = true;
44 cx.request_paint();
45 }
46
47 pub fn copy(&mut self, cx: &mut EventCx<'_>) -> bool {
56 self.catch_up();
57 let runs = self.copied_runs();
58 if runs.is_empty() {
59 return false;
60 }
61 let text = self.text_of(runs, &super::palette(cx.theme()));
62 cx.clipboard().set_text(&text).is_ok()
63 }
64
65 pub fn selected_text(&mut self, palette: &Palette) -> String {
71 self.catch_up();
72 let runs = self.copied_runs();
73 self.text_of(runs, palette)
74 }
75
76 fn copied_runs(&self) -> Vec<Range<usize>> {
79 let selected = self.selected_rows();
80 if selected.is_empty() {
81 return Vec::new();
82 }
83 self.model
84 .copy_rows(selected.clone())
85 .into_iter()
86 .map(|run| run.start.max(selected.start)..run.end.min(selected.end))
87 .filter(|run| !run.is_empty())
88 .collect()
89 }
90
91 fn text_of(&self, runs: Vec<Range<usize>>, palette: &Palette) -> String {
93 let columns = self.model.columns().len();
94 let mut out = String::new();
95 let mut cell = CellText::new();
96 let rows = runs.into_iter().flatten();
97 for (index, row) in rows.enumerate() {
98 if index > 0 {
99 out.push('\n');
100 }
101 for column in 0..columns {
102 if column > 0 {
103 out.push('\t');
104 }
105 cell.clear();
106 self.model.cell(row, column, &mut cell, palette);
107 out.extend(cell.as_str().chars().map(|c| match c {
108 '\t' | '\n' | '\r' => ' ',
109 c => c,
110 }));
111 }
112 }
113 out
114 }
115
116 fn catch_up(&mut self) {
118 self.model.prepare();
119 self.follow_model();
120 self.clamp();
121 }
122
123 pub(super) fn select_row(&mut self, cx: &mut EventCx<'_>, row: usize) {
125 self.anchor = None;
126 self.all = false;
127 self.move_selection(cx, row);
128 }
129
130 pub(super) fn extend_to(&mut self, cx: &mut EventCx<'_>, row: usize) {
133 self.all = false;
134 if self.anchor.is_none() {
135 self.anchor = Some(self.selected.unwrap_or_else(|| self.selection_of(row)));
136 }
137 self.move_selection(cx, row);
138 }
139
140 fn move_selection(&mut self, cx: &mut EventCx<'_>, row: usize) {
142 if self.selected() != Some(row) {
143 self.selected = Some(self.selection_of(row));
144 cx.emit(ActionKind::Selected(row));
145 }
146 self.reveal_row(row);
147 cx.request_paint();
148 }
149}