diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-03-31 16:03:34 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-03-31 16:03:34 +0300 |
| commit | f826fb78dcac89b4ba23e399ac5d141459c14ae9 (patch) | |
| tree | cce037483769961855ba98612789a3c533b03d2d /src | |
| parent | 79f5f3c2c838af976e1e3823fdeb1aa1d0617ec1 (diff) | |
fix: reserve pane width for visible scrollbars
Diffstat (limited to 'src')
| -rw-r--r-- | src/layout.rs | 3 | ||||
| -rw-r--r-- | src/ui.rs | 60 |
2 files changed, 45 insertions, 18 deletions
diff --git a/src/layout.rs b/src/layout.rs index c72456b9..56254f04 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -32,6 +32,8 @@ pub struct PaneInfo { pub rect: Rect, /// Inner rect (content area, excluding borders). Used for selection. pub inner_rect: Rect, + /// Reserved scrollbar lane, when scrollback is visible. + pub scrollbar_rect: Option<Rect>, pub is_focused: bool, } @@ -315,6 +317,7 @@ fn collect_panes(node: &Node, area: Rect, focus: PaneId, result: &mut Vec<PaneIn rect: area, // inner_rect is set during render when we know if borders are shown inner_rect: area, + scrollbar_rect: None, is_focused: *id == focus, }); } @@ -116,25 +116,41 @@ fn compute_pane_infos(app: &AppState, area: Rect) -> Vec<PaneInfo> { return Vec::new(); }; + let multi_pane = ws.layout.pane_count() > 1; + let terminal_active = app.mode == Mode::Terminal; + if ws.zoomed { let focused_id = ws.layout.focused(); + let mut inner_rect = area; + let mut scrollbar_rect = None; if let Some(rt) = ws.runtimes.get(&focused_id) { - rt.resize(area.height, area.width); + if rt + .scroll_metrics() + .is_some_and(|metrics| metrics.max_offset_from_bottom > 0 && 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, + 1, + area.height, + )); + } + rt.resize(inner_rect.height, inner_rect.width); } return vec![PaneInfo { id: focused_id, rect: area, - inner_rect: area, + inner_rect, + scrollbar_rect, is_focused: true, }]; } - let multi_pane = ws.layout.pane_count() > 1; - let terminal_active = app.mode == Mode::Terminal; let mut pane_infos = ws.layout.panes(area); for info in &mut pane_infos { - let inner = if multi_pane { + let pane_inner = if multi_pane { let border_set = if info.is_focused && terminal_active { ratatui::symbols::border::THICK } else { @@ -147,11 +163,27 @@ fn compute_pane_infos(app: &AppState, area: Rect) -> Vec<PaneInfo> { } else { area }; - info.inner_rect = inner; + let mut inner_rect = pane_inner; + let mut scrollbar_rect = None; if let Some(rt) = ws.runtimes.get(&info.id) { - rt.resize(inner.height, inner.width); + if rt + .scroll_metrics() + .is_some_and(|metrics| metrics.max_offset_from_bottom > 0 && 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, + 1, + pane_inner.height, + )); + } + rt.resize(inner_rect.height, inner_rect.width); } + + info.inner_rect = inner_rect; + info.scrollbar_rect = scrollbar_rect; } pane_infos @@ -660,16 +692,7 @@ fn dim_color(color: Color) -> Color { } pub(crate) fn pane_scrollbar_rect(info: &PaneInfo) -> Option<Rect> { - let inner = info.inner_rect; - if inner.width == 0 || inner.height == 0 { - return None; - } - Some(Rect::new( - inner.x + inner.width.saturating_sub(1), - inner.y, - 1, - inner.height, - )) + info.scrollbar_rect } pub(crate) fn scrollbar_offset_from_row( @@ -1716,7 +1739,8 @@ mod tests { 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, }; |
