fix(tui): fix incorrect height calculation when there is not enough space (#611)

## 📺 PR Description

Solves #609

- The calculation of available_height for the Inline and Fixed mode were
updated to correctly account for the remaining space and the necessary
height needed for the TUI
- QoL: Refactored the viewport calculation logic for both Inline and
Fixed modes to use a shared scrolling function
(handle_viewport_scrolling).
- Docs: Added note about default minimum height

## Checklist

- [x] my commits **and PR title** follow the [conventional
commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) format
- [x] if this is a new feature, I have added tests to consolidate the
feature and prevent regressions
- [ ] if this is a bug fix, I have added a test that reproduces the bug
(if applicable)
- [x] I have added a reasonable amount of documentation to the code
where appropriate
This commit is contained in:
LM 2025-07-09 16:12:19 +02:00 committed by GitHub
parent 427ca3619c
commit 20a55cf142
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 66 additions and 30 deletions

View File

@ -360,7 +360,8 @@ Television's options are organized by functionality. Each option behaves differe
**Purpose**: Uses all available empty space at the bottom of the terminal
- **Both Modes**: Same behavior
- **Behavior**: Automatically uses all available space below the cursor, minimum height is ensured
- **Behavior**: Automatically uses all available space below the cursor,
minimum height is ensured (set by default at 15 lines)
- **Conflicts**: Cannot be used with `--height`
- **Use Case**: Use of all available space without entering fullscreen mode

View File

@ -320,7 +320,7 @@ This flag works identically in both channel mode and ad\-hoc mode.
When enabled, the picker will be displayed as an inline interface that uses
all available empty space at the bottom of the terminal. If there is insufficient
space to meet the minimum height the terminal will scroll.
space to meet the minimum height (set by default at 15 lines) the terminal will scroll.
.TP
\fB\-h\fR, \fB\-\-help\fR
Print help (see a summary with \*(Aq\-h\*(Aq)

View File

@ -82,23 +82,20 @@ where
let viewport = match mode {
TuiMode::Fullscreen => Viewport::Fullscreen,
TuiMode::Inline => {
let mut cursor_position = Self::get_cursor_position();
// take all available height and max width with a minimum of 15 for the height
let available_height =
terminal_size.height.saturating_sub(cursor_position.y);
debug!(
"Total height: {}, Available height: {}, cursor position: {:?}",
terminal_size.height, available_height, cursor_position.y
);
if available_height < MIN_VIEWPORT_HEIGHT {
execute!(
backend,
ScrollUp(MIN_VIEWPORT_HEIGHT - available_height)
)?;
cursor_position.y = cursor_position.y.saturating_sub(
MIN_VIEWPORT_HEIGHT - available_height + 1,
);
}
let cursor_position = Self::get_cursor_position();
let cursor_position = Self::handle_viewport_scrolling(
&mut backend,
cursor_position,
terminal_size,
MIN_VIEWPORT_HEIGHT,
)?;
// Calculate final available height after potential scrolling
let available_height = terminal_size
.height
.saturating_sub(cursor_position.y)
.max(MIN_VIEWPORT_HEIGHT);
Viewport::Fixed(ratatui::layout::Rect::new(
0,
cursor_position.y,
@ -107,16 +104,15 @@ where
))
}
TuiMode::Fixed { width, height } => {
let mut cursor_position = Self::get_cursor_position();
let cursor_position = Self::get_cursor_position();
let cursor_position = Self::handle_viewport_scrolling(
&mut backend,
cursor_position,
terminal_size,
*height,
)?;
let w = width.unwrap_or(terminal_size.width);
let available_height =
terminal_size.height.saturating_sub(cursor_position.y);
if available_height < *height {
execute!(backend, ScrollUp(height - available_height))?;
cursor_position.y = cursor_position
.y
.saturating_sub(*height - available_height + 1);
}
Viewport::Fixed(ratatui::layout::Rect::new(
0,
cursor_position.y,
@ -131,8 +127,47 @@ where
Ok(Self { terminal, viewport })
}
pub fn size(&self) -> Result<Size> {
Ok(self.terminal.size()?)
/// Handles scrolling logic when there's insufficient space for the requested height.
/// Returns the updated cursor position after scrolling.
fn handle_viewport_scrolling(
backend: &mut CrosstermBackend<W>,
mut cursor_position: Position,
terminal_size: Size,
required_height: u16,
) -> Result<Position> {
let available_height =
terminal_size.height.saturating_sub(cursor_position.y);
debug!(
"Terminal height: {}, Available height: {}, cursor position: {:?}",
terminal_size.height, available_height, cursor_position.y
);
// If we don't have enough space for the required height we need to scroll up.
if available_height < required_height {
// Minus one to account for the cursor position.
let scroll_amount = required_height - available_height - 1;
// Special case: when we're at the very bottom (available_height == 1),
// we need to scroll one less line to avoid creating an empty line
// between the TUI and the prompt due to terminal cursor positioning.
let actual_scroll = if available_height == 1 {
scroll_amount - 1
} else {
scroll_amount
};
// Scroll up by as needed to reach the required height.
debug!("Scrolling up by: {}", actual_scroll);
execute!(backend, ScrollUp(actual_scroll))?;
// Update cursor position to account for the scroll.
debug!("New cursor position: {}", cursor_position.y);
cursor_position.y =
cursor_position.y.saturating_sub(scroll_amount);
}
Ok(cursor_position)
}
const DSR: &'static str = "\x1b[6n";