Skip to content

Commit 1bbfafa

Browse files
committed
fix: limit row and column selection borders
1 parent be36d5a commit 1bbfafa

3 files changed

Lines changed: 162 additions & 27 deletions

File tree

src/widget/table/draw.rs

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use ratatui::{buffer::Buffer, layout::Rect, style::Style};
22

3-
use super::primitives::{BorderStroke, CellRect, RegionRect};
3+
use super::primitives::{BorderSides, BorderStroke, CellRect, RegionRect};
44

55
pub fn draw_cell_border(buffer: &mut Buffer, cell_rect: CellRect, style: Style) {
66
let CellRect {
@@ -24,11 +24,12 @@ pub fn draw_cell_border(buffer: &mut Buffer, cell_rect: CellRect, style: Style)
2424
buffer[(right, content)].set_symbol("┃").set_style(style);
2525
}
2626

27-
pub fn draw_region_border(
27+
pub fn draw_region_border_sides(
2828
buffer: &mut Buffer,
2929
region: RegionRect,
3030
style: Style,
3131
stroke: BorderStroke,
32+
sides: BorderSides,
3233
) {
3334
let glyphs = stroke.glyphs();
3435
let RegionRect {
@@ -38,33 +39,53 @@ pub fn draw_region_border(
3839
bottom_y: bottom,
3940
} = region;
4041

41-
buffer[(left, top)]
42-
.set_symbol(glyphs.top_left)
43-
.set_style(style);
44-
buffer[(right, top)]
45-
.set_symbol(glyphs.top_right)
46-
.set_style(style);
47-
buffer[(left, bottom)]
48-
.set_symbol(glyphs.bottom_left)
49-
.set_style(style);
50-
buffer[(right, bottom)]
51-
.set_symbol(glyphs.bottom_right)
52-
.set_style(style);
42+
if sides.top {
43+
for x in left..=right {
44+
buffer[(x, top)]
45+
.set_symbol(glyphs.horizontal)
46+
.set_style(style);
47+
}
48+
}
49+
if sides.bottom {
50+
for x in left..=right {
51+
buffer[(x, bottom)]
52+
.set_symbol(glyphs.horizontal)
53+
.set_style(style);
54+
}
55+
}
56+
if sides.left {
57+
for y in top..=bottom {
58+
buffer[(left, y)]
59+
.set_symbol(glyphs.vertical)
60+
.set_style(style);
61+
}
62+
}
63+
if sides.right {
64+
for y in top..=bottom {
65+
buffer[(right, y)]
66+
.set_symbol(glyphs.vertical)
67+
.set_style(style);
68+
}
69+
}
5370

54-
for x in left + 1..right {
55-
buffer[(x, top)]
56-
.set_symbol(glyphs.horizontal)
71+
if sides.top && sides.left {
72+
buffer[(left, top)]
73+
.set_symbol(glyphs.top_left)
5774
.set_style(style);
58-
buffer[(x, bottom)]
59-
.set_symbol(glyphs.horizontal)
75+
}
76+
if sides.top && sides.right {
77+
buffer[(right, top)]
78+
.set_symbol(glyphs.top_right)
6079
.set_style(style);
6180
}
62-
for y in top + 1..bottom {
63-
buffer[(left, y)]
64-
.set_symbol(glyphs.vertical)
81+
if sides.bottom && sides.left {
82+
buffer[(left, bottom)]
83+
.set_symbol(glyphs.bottom_left)
6584
.set_style(style);
66-
buffer[(right, y)]
67-
.set_symbol(glyphs.vertical)
85+
}
86+
if sides.bottom && sides.right {
87+
buffer[(right, bottom)]
88+
.set_symbol(glyphs.bottom_right)
6889
.set_style(style);
6990
}
7091
}

src/widget/table/mod.rs

Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::{model::cell::col_name, theme::Theme};
1515

1616
use self::{
1717
layout::GridLayout,
18-
primitives::{BorderStroke, CellRect, RegionRect},
18+
primitives::{BorderSides, BorderStroke, CellRect, RegionRect},
1919
view::{GridScroll, GridSelection},
2020
};
2121

@@ -130,11 +130,12 @@ impl TableGrid {
130130
&& let Some(region) =
131131
selection_to_region_rect(&cfg.layout, &sc, &sel, rendered_rows, rendered_cols)
132132
{
133-
draw::draw_region_border(
133+
draw::draw_region_border_sides(
134134
buffer,
135135
region,
136136
Style::default().fg(cfg.theme.accent),
137137
BorderStroke::Solid,
138+
selection_border_sides(&sel),
138139
);
139140
}
140141

@@ -143,11 +144,12 @@ impl TableGrid {
143144
&& let Some(rect) =
144145
selection_to_region_rect(&cfg.layout, &sc, &region, rendered_rows, rendered_cols)
145146
{
146-
draw::draw_region_border(
147+
draw::draw_region_border_sides(
147148
buffer,
148149
rect,
149150
Style::default().fg(cfg.theme.accent),
150151
BorderStroke::Dashed,
152+
selection_border_sides(&region),
151153
);
152154
}
153155

@@ -179,6 +181,14 @@ fn cursor_cell_rect(
179181
layout.cell_rect(vis_row, vis_col)
180182
}
181183

184+
fn selection_border_sides(selection: &GridSelection) -> BorderSides {
185+
match selection {
186+
GridSelection::Row(_) => BorderSides::HORIZONTAL,
187+
GridSelection::Column(_) => BorderSides::VERTICAL,
188+
GridSelection::Range { .. } => BorderSides::ALL,
189+
}
190+
}
191+
182192
fn selection_to_region_rect(
183193
layout: &GridLayout,
184194
scroll: &GridScroll,
@@ -225,3 +235,76 @@ fn selection_to_region_rect(
225235
}
226236
}
227237
}
238+
239+
#[cfg(test)]
240+
mod tests {
241+
use ratatui::{Terminal, backend::TestBackend, layout::Rect};
242+
243+
use super::{
244+
TableGrid, TableGridConfig,
245+
layout::GridMetrics,
246+
view::{GridScroll, GridSelection},
247+
};
248+
use crate::{model::cell::CellAddress, theme::Theme};
249+
250+
fn render_selection(selection: GridSelection) -> ratatui::buffer::Buffer {
251+
let backend = TestBackend::new(40, 10);
252+
let mut terminal = Terminal::new(backend).unwrap();
253+
let theme = Theme::dark();
254+
let cell_text = |_: usize, _: usize| String::new();
255+
256+
let frame = terminal
257+
.draw(|frame| {
258+
let area = Rect::new(0, 0, 40, 10);
259+
let layout = GridMetrics::new(8, 4, 2).layout(area);
260+
TableGrid::render(
261+
frame,
262+
frame.area(),
263+
TableGridConfig {
264+
scroll: GridScroll {
265+
scroll_row: 0,
266+
scroll_col: 0,
267+
visible_rows: 3,
268+
visible_cols: 3,
269+
cursor: CellAddress { row: 1, col: 1 },
270+
total_rows: 3,
271+
total_cols: 3,
272+
},
273+
layout,
274+
theme,
275+
blink_visible: true,
276+
edit_buffer: None,
277+
selection: Some(selection),
278+
copied_region: None,
279+
cell_text: &cell_text,
280+
},
281+
);
282+
})
283+
.unwrap();
284+
285+
frame.buffer.clone()
286+
}
287+
288+
#[test]
289+
fn column_selection_draws_only_left_and_right_edges() {
290+
let buffer = render_selection(GridSelection::Column(1));
291+
let accent = Theme::dark().accent;
292+
293+
assert_eq!(buffer[(13, 1)].fg, accent);
294+
assert_eq!(buffer[(22, 1)].fg, accent);
295+
assert_ne!(buffer[(14, 0)].symbol(), "━");
296+
assert_ne!(buffer[(14, 7)].symbol(), "━");
297+
assert_ne!(buffer[(14, 7)].fg, accent);
298+
}
299+
300+
#[test]
301+
fn row_selection_draws_only_top_and_bottom_edges() {
302+
let buffer = render_selection(GridSelection::Row(1));
303+
let accent = Theme::dark().accent;
304+
305+
assert_eq!(buffer[(14, 3)].fg, accent);
306+
assert_eq!(buffer[(14, 5)].fg, accent);
307+
assert_ne!(buffer[(4, 4)].fg, accent);
308+
assert_ne!(buffer[(31, 4)].fg, accent);
309+
}
310+
}

src/widget/table/primitives.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,37 @@ pub enum BorderStroke {
2727
Dashed,
2828
}
2929

30+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31+
pub struct BorderSides {
32+
pub top: bool,
33+
pub right: bool,
34+
pub bottom: bool,
35+
pub left: bool,
36+
}
37+
38+
impl BorderSides {
39+
pub const ALL: Self = Self {
40+
top: true,
41+
right: true,
42+
bottom: true,
43+
left: true,
44+
};
45+
46+
pub const HORIZONTAL: Self = Self {
47+
top: true,
48+
right: false,
49+
bottom: true,
50+
left: false,
51+
};
52+
53+
pub const VERTICAL: Self = Self {
54+
top: false,
55+
right: true,
56+
bottom: false,
57+
left: true,
58+
};
59+
}
60+
3061
#[derive(Debug, Clone, Copy)]
3162
pub(crate) struct BorderGlyphs {
3263
pub horizontal: &'static str,

0 commit comments

Comments
 (0)