Skip to main content

rmux_core/screen/
selection.rs

1use crate::grid::GridCellFlags;
2use crate::input::GridAttr;
3use crate::style::{style_parse, Style, StyleCell};
4
5use super::Screen;
6
7impl Screen {
8    /// Returns whether any visible cell is currently marked as selected.
9    #[must_use]
10    pub fn has_selected_cells(&self) -> bool {
11        self.has_selected_cells
12    }
13
14    /// Marks one visible row range as selected.
15    pub fn mark_selected_row_range(&mut self, row: u32, start_x: u32, end_x: u32) {
16        let line_width = self.grid.sx();
17        if row >= self.grid.sy() || line_width == 0 {
18            return;
19        }
20
21        let Some(line) = self.grid.visible_line_mut(row) else {
22            return;
23        };
24        let start_x = start_x.min(line_width.saturating_sub(1));
25        let end_x = end_x.min(line_width.saturating_sub(1));
26        if start_x > end_x {
27            return;
28        }
29
30        let start_x = line.owning_cell_x(start_x).unwrap_or(start_x);
31        let end_x = line.owning_cell_x(end_x).unwrap_or(end_x);
32        let mut touched = false;
33        let mut x = start_x;
34        while x <= end_x {
35            let owner_x = line.owning_cell_x(x).unwrap_or(x);
36            let width = line
37                .cell(owner_x)
38                .map(|cell| u32::from(cell.width().max(1)))
39                .unwrap_or(1);
40
41            for offset in 0..width {
42                let cell_x = owner_x.saturating_add(offset);
43                if let Some(cell) = line.cell_mut(cell_x) {
44                    let mut flags = cell.flags();
45                    flags.insert(GridCellFlags::SELECTED);
46                    cell.set_flags(flags);
47                    self.has_selected_cells = true;
48                    touched = true;
49                }
50            }
51
52            x = owner_x.saturating_add(width.max(1));
53        }
54        if touched {
55            line.touch();
56        }
57    }
58
59    /// Clears all visible selected-cell markers.
60    pub fn clear_selected_cells(&mut self) {
61        if !self.has_selected_cells {
62            return;
63        }
64
65        let width = self.grid.sx();
66        for row in 0..self.grid.sy() {
67            let Some(line) = self.grid.visible_line_mut(row) else {
68                continue;
69            };
70            let mut touched = false;
71            for x in 0..width {
72                let Some(cell) = line.cell_mut(x) else {
73                    continue;
74                };
75                if !cell.flags().contains(GridCellFlags::SELECTED) {
76                    continue;
77                }
78                let mut flags = cell.flags();
79                flags.remove(GridCellFlags::SELECTED);
80                cell.set_flags(flags);
81                touched = true;
82            }
83            if touched {
84                line.touch();
85            }
86        }
87        self.has_selected_cells = false;
88    }
89
90    /// Overlays `style_input` onto all selected visible cells.
91    pub fn overlay_style_on_selected(&mut self, style_input: &str) {
92        if style_input.is_empty() {
93            self.clear_selected_cells();
94            return;
95        }
96
97        let width = self.grid.sx();
98        for row in 0..self.grid.sy() {
99            let Some(line) = self.grid.visible_line_mut(row) else {
100                continue;
101            };
102            let mut touched = false;
103            for x in 0..width {
104                let Some(cell) = line.cell_mut(x) else {
105                    continue;
106                };
107                if !cell.flags().contains(GridCellFlags::SELECTED) || cell.is_padding() {
108                    continue;
109                }
110
111                let base = StyleCell {
112                    fg: cell.fg(),
113                    bg: cell.bg(),
114                    us: cell.us(),
115                    attr: cell.attr(),
116                };
117                let mut style = Style::with_cell(base);
118                if style_parse(&mut style, &base, style_input).is_err() {
119                    return;
120                }
121
122                let attr = if style.cell.attr & GridAttr::NOATTR != 0 {
123                    style.cell.attr & !GridAttr::NOATTR
124                } else {
125                    style.cell.attr
126                };
127                cell.set_attr(attr);
128                cell.set_fg(style.cell.fg);
129                cell.set_bg(style.cell.bg);
130                cell.set_us(style.cell.us);
131                touched = true;
132            }
133            if touched {
134                line.touch();
135            }
136        }
137        self.clear_selected_cells();
138    }
139}