diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-25 23:08:01 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-25 23:08:01 +0300 |
| commit | 692327f38b752e868988e789d27e9098e4b7e34e (patch) | |
| tree | 401255da700f823104f917ab25b886c32ff360d2 | |
| parent | d15fa7aae4e6da3d30ea000c4ea9d0934fbee2e5 (diff) | |
fix: reserve pane scrollbar column
| -rw-r--r-- | src/ui.rs | 10 | ||||
| -rw-r--r-- | src/ui/panes.rs | 43 |
2 files changed, 44 insertions, 9 deletions
@@ -492,11 +492,11 @@ mod tests { } #[test] - fn pane_scrollbar_rect_overlays_rightmost_inner_column() { + fn pane_scrollbar_rect_uses_reserved_rightmost_column() { let info = PaneInfo { id: crate::layout::PaneId::from_raw(1), rect: Rect::new(0, 0, 12, 8), - inner_rect: Rect::new(1, 1, 10, 6), + inner_rect: Rect::new(1, 1, 9, 6), scrollbar_rect: Some(Rect::new(10, 1, 1, 6)), is_focused: true, }; @@ -505,7 +505,7 @@ mod tests { } #[tokio::test] - async fn compute_view_keeps_terminal_width_when_pane_scrollbar_is_visible() { + async fn compute_view_reserves_terminal_column_when_pane_scrollbar_is_visible() { let mut app = crate::app::state::AppState::test_new(); let mut ws = Workspace::test_new("test"); let pane_id = ws.tabs[0].root_pane; @@ -526,11 +526,11 @@ mod tests { compute_view(&mut app, Rect::new(0, 0, 40, 12)); let info = app.view.pane_infos.first().expect("pane info"); - assert_eq!(info.inner_rect.width, app.view.terminal_area.width); + assert_eq!(info.inner_rect.width + 1, app.view.terminal_area.width); assert_eq!( info.scrollbar_rect, Some(Rect::new( - info.inner_rect.x + info.inner_rect.width.saturating_sub(1), + info.inner_rect.x + info.inner_rect.width, info.inner_rect.y, 1, info.inner_rect.height, diff --git a/src/ui/panes.rs b/src/ui/panes.rs index 7e1490db..7a4a2c74 100644 --- a/src/ui/panes.rs +++ b/src/ui/panes.rs @@ -26,13 +26,14 @@ pub(super) fn compute_pane_infos(app: &AppState, area: Rect, resize_panes: bool) if ws.zoomed { let focused_id = ws.layout.focused(); - let inner_rect = area; + let mut inner_rect = area; let mut scrollbar_rect = None; if let Some(rt) = ws.runtimes.get(&focused_id) { if rt .scroll_metrics() - .is_some_and(|metrics| should_show_scrollbar(metrics) && area.width > 0) + .is_some_and(|metrics| should_show_scrollbar(metrics) && area.width > 1) { + inner_rect.width = inner_rect.width.saturating_sub(1); scrollbar_rect = Some(Rect::new( area.x + area.width.saturating_sub(1), area.y, @@ -70,13 +71,14 @@ pub(super) fn compute_pane_infos(app: &AppState, area: Rect, resize_panes: bool) area }; - let inner_rect = pane_inner; + let mut inner_rect = pane_inner; let mut scrollbar_rect = None; if let Some(rt) = ws.runtimes.get(&info.id) { if rt .scroll_metrics() - .is_some_and(|metrics| should_show_scrollbar(metrics) && pane_inner.width > 0) + .is_some_and(|metrics| should_show_scrollbar(metrics) && pane_inner.width > 1) { + inner_rect.width = inner_rect.width.saturating_sub(1); scrollbar_rect = Some(Rect::new( pane_inner.x + pane_inner.width.saturating_sub(1), pane_inner.y, @@ -213,6 +215,39 @@ fn render_selection_highlight( } } +#[cfg(test)] +mod tests { + use super::*; + use crate::pane::PaneRuntime; + use crate::workspace::Workspace; + + #[tokio::test] + async fn pane_scrollbar_reserves_last_column_from_terminal_area() { + let mut app = AppState::test_new(); + let mut workspace = Workspace::test_new("test"); + let root_pane = workspace.tabs[0].root_pane; + workspace.tabs[0].runtimes.insert( + root_pane, + PaneRuntime::test_with_scrollback_bytes( + 40, + 8, + 1024, + b"one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten\n", + ), + ); + app.workspaces = vec![workspace]; + app.active = Some(0); + + let area = Rect::new(10, 3, 40, 8); + let infos = compute_pane_infos(&app, area, false); + let info = &infos[0]; + + assert_eq!(info.rect, area); + assert_eq!(info.scrollbar_rect, Some(Rect::new(49, 3, 1, 8))); + assert_eq!(info.inner_rect, Rect::new(10, 3, 39, 8)); + } +} + fn render_empty(app: &AppState, frame: &mut Frame, area: Rect) { let p = &app.palette; let lines = vec