aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-03-31 16:03:34 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-03-31 16:03:34 +0300
commitf826fb78dcac89b4ba23e399ac5d141459c14ae9 (patch)
treecce037483769961855ba98612789a3c533b03d2d /src
parent79f5f3c2c838af976e1e3823fdeb1aa1d0617ec1 (diff)
fix: reserve pane width for visible scrollbars
Diffstat (limited to 'src')
-rw-r--r--src/layout.rs3
-rw-r--r--src/ui.rs60
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,
});
}
diff --git a/src/ui.rs b/src/ui.rs
index f5c806a6..64564487 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -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,
};