feat(tui): responsive breakpoints for detail views (bd-a6yb)
Apply breakpoint-aware layout to issue_detail and mr_detail views: - Issue detail: hide labels on Xs, hide assignees on Xs/Sm, skip milestone row on Xs - MR detail: hide branch names and merge status on Xs/Sm - Issue detail allocate_sections gives description 60% on wide (Lg+) vs 40% narrow - Add responsive tests for both detail views - Close bd-a6yb: all TUI screens now adapt to terminal width 760 lib tests pass, clippy clean.
This commit is contained in:
@@ -20,6 +20,7 @@ use ftui::render::cell::Cell;
|
||||
use ftui::render::drawing::Draw;
|
||||
use ftui::render::frame::Frame;
|
||||
|
||||
use crate::layout::{classify_width, search_show_project};
|
||||
use crate::message::EntityKind;
|
||||
use crate::state::search::SearchState;
|
||||
use crate::text_width::cursor_cell_offset;
|
||||
@@ -39,6 +40,8 @@ pub fn render_search(frame: &mut Frame<'_>, state: &SearchState, area: Rect) {
|
||||
return;
|
||||
}
|
||||
|
||||
let bp = classify_width(area.width);
|
||||
let show_project = search_show_project(bp);
|
||||
let mut y = area.y;
|
||||
let max_x = area.right();
|
||||
|
||||
@@ -99,7 +102,7 @@ pub fn render_search(frame: &mut Frame<'_>, state: &SearchState, area: Rect) {
|
||||
if state.results.is_empty() {
|
||||
render_empty_state(frame, state, area.x + 1, y, max_x);
|
||||
} else {
|
||||
render_result_list(frame, state, area.x, y, area.width, list_height);
|
||||
render_result_list(frame, state, area.x, y, area.width, list_height, show_project);
|
||||
}
|
||||
|
||||
// -- Bottom hint bar -----------------------------------------------------
|
||||
@@ -228,6 +231,7 @@ fn render_result_list(
|
||||
start_y: u16,
|
||||
width: u16,
|
||||
list_height: usize,
|
||||
show_project: bool,
|
||||
) {
|
||||
let max_x = x + width;
|
||||
|
||||
@@ -294,11 +298,13 @@ fn render_result_list(
|
||||
let after_title =
|
||||
frame.print_text_clipped(after_iid + 1, y, &result.title, label_style, max_x);
|
||||
|
||||
// Project path (right-aligned).
|
||||
let path_width = result.project_path.len() as u16 + 2;
|
||||
let path_x = max_x.saturating_sub(path_width);
|
||||
if path_x > after_title + 1 {
|
||||
frame.print_text_clipped(path_x, y, &result.project_path, detail_style, max_x);
|
||||
// Project path (right-aligned, hidden on narrow terminals).
|
||||
if show_project {
|
||||
let path_width = result.project_path.len() as u16 + 2;
|
||||
let path_x = max_x.saturating_sub(path_width);
|
||||
if path_x > after_title + 1 {
|
||||
frame.print_text_clipped(path_x, y, &result.project_path, detail_style, max_x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,4 +482,23 @@ mod tests {
|
||||
render_search(&mut frame, &state, Rect::new(0, 0, 80, 10));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_render_search_responsive_breakpoints() {
|
||||
// Narrow (Xs=50): project path hidden.
|
||||
with_frame!(50, 24, |frame| {
|
||||
let mut state = SearchState::default();
|
||||
state.enter(fts_caps());
|
||||
state.results = sample_results(3);
|
||||
render_search(&mut frame, &state, Rect::new(0, 0, 50, 24));
|
||||
});
|
||||
|
||||
// Standard (Md=100): project path shown.
|
||||
with_frame!(100, 24, |frame| {
|
||||
let mut state = SearchState::default();
|
||||
state.enter(fts_caps());
|
||||
state.results = sample_results(3);
|
||||
render_search(&mut frame, &state, Rect::new(0, 0, 100, 24));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user