style: title and pad floating input windows

This commit is contained in:
Valdemar Lorenzen
2026-10-12 12:55:47 +02:00
parent 52915ffacf
commit 684ba46a2c
3 changed files with 84 additions and 30 deletions
+80 -28
View File
@@ -1848,27 +1848,31 @@ fn draw_floating_input(frame: &mut Frame, editor: &Editor, area: Rect) -> Option
height,
);
let bordered = panel.width >= 4 && panel.height >= 3;
let block = Block::default()
let padding = u16::from(panel.width.saturating_sub(2 * u16::from(bordered)) >= 3);
let mut block = Block::default()
.borders(if bordered {
Borders::ALL
} else {
Borders::NONE
})
.border_style(Style::default().fg(Color::Cyan));
.border_style(Style::default().fg(Color::Cyan))
.padding(ratatui::widgets::Padding::horizontal(padding));
if bordered {
block = block.title(if editor.mode() == Mode::Search {
" Search "
} else {
" Command "
});
}
let content = block.inner(panel);
frame.render_widget(Clear, panel);
frame.render_widget(block, panel);
let prefix = if editor.mode() == Mode::Search {
'/'
} else {
':'
};
let command = format!("{prefix}{}", editor.command_line());
let column = display_column(&command, usize::MAX);
let command = editor.command_line();
let column = display_column(command, usize::MAX);
let width = usize::from(content.width);
let offset = column.saturating_sub(width.saturating_sub(1));
frame.render_widget(
Paragraph::new(visible_line(&command, offset, width)),
Paragraph::new(visible_line(command, offset, width)),
content,
);
frame.set_cursor_position((content.x + (column - offset) as u16, content.y));
@@ -1898,13 +1902,19 @@ fn draw_notification(frame: &mut Frame, editor: &Editor, area: Rect) -> Option<R
} else {
message
};
let width = (UnicodeWidthStr::width(message.as_ref()).saturating_add(2))
.min(60)
.min(usize::from(area.width)) as u16;
let width = (UnicodeWidthStr::width(message.as_ref()).saturating_add(if area.height >= 3 {
4
} else {
2
}))
.min(60)
.min(usize::from(area.width)) as u16;
let bordered = width >= 4 && area.height >= 3;
let border = 2 * u16::from(bordered);
let padding = u16::from(width.saturating_sub(border) >= 3);
let paragraph = Paragraph::new(message).wrap(Wrap { trim: false });
let height = (paragraph.line_count(width - border).min(6) as u16 + border).min(area.height);
let height = (paragraph.line_count(width - border - 2 * padding).min(6) as u16 + border)
.min(area.height);
let panel = Rect::new(area.right() - width, area.bottom() - height, width, height);
let block = Block::default()
.borders(if bordered {
@@ -1912,7 +1922,8 @@ fn draw_notification(frame: &mut Frame, editor: &Editor, area: Rect) -> Option<R
} else {
Borders::NONE
})
.border_style(Style::default().fg(Color::DarkGray));
.border_style(Style::default().fg(Color::DarkGray))
.padding(ratatui::widgets::Padding::horizontal(padding));
let content = block.inner(panel);
frame.render_widget(Clear, panel);
frame.render_widget(block, panel);
@@ -5582,6 +5593,58 @@ mod tests {
}
}
#[test]
fn floating_fields_use_border_titles_and_inner_horizontal_padding() {
for (prefix, title) in [(':', "Command"), ('/', "Search")] {
let mut editor = Editor::open(None, vec![]).unwrap();
editor.handle(Input::Char(prefix));
for ch in "needle".chars() {
editor.handle(Input::Char(ch));
}
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(80, 12)).unwrap();
let mut panel = Rect::default();
terminal
.draw(|frame| {
panel = draw_floating_input(frame, &editor, frame.area()).unwrap();
})
.unwrap();
assert!(
pane_screen(&terminal, Rect::new(panel.x, panel.y, panel.width, 1)).contains(title)
);
let buffer = terminal.backend().buffer();
assert_eq!(buffer[(panel.x + 1, panel.y + 1)].symbol(), " ");
assert_eq!(buffer[(panel.x + 2, panel.y + 1)].symbol(), "n");
assert_eq!(buffer[(panel.right() - 2, panel.y + 1)].symbol(), " ");
assert_eq!(
terminal.get_cursor_position().unwrap(),
Position::new(panel.x + 8, panel.y + 1)
);
assert_eq!(
pane_screen(
&terminal,
Rect::new(panel.x + 1, panel.y + 1, panel.width - 2, 1)
)
.trim(),
"needle"
);
}
let mut editor = Editor::open(None, vec![]).unwrap();
editor.set_message("operation complete");
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(80, 12)).unwrap();
let mut panel = Rect::default();
terminal
.draw(|frame| {
panel = draw_notification(frame, &editor, frame.area()).unwrap();
})
.unwrap();
let buffer = terminal.backend().buffer();
assert_eq!(buffer[(panel.x + 1, panel.y + 1)].symbol(), " ");
assert_eq!(buffer[(panel.x + 2, panel.y + 1)].symbol(), "o");
assert_eq!(buffer[(panel.right() - 2, panel.y + 1)].symbol(), " ");
assert_eq!(panel.right(), 80);
assert_eq!(panel.bottom(), 12);
}
#[test]
fn floating_inputs_share_workspace_geometry_across_documents_splits_and_diffs() {
for context in ["document", "split", "diff", "split-diff", "selector"] {
@@ -6357,7 +6420,6 @@ mod tests {
if prompting {
let input = views.panels.input.unwrap();
assert!(input.contains(cursor));
assert!(pane_screen(&terminal, input).contains(':'));
} else {
assert!(viewport.contains(cursor));
assert!(pane_screen(&terminal, viewport).contains("DOCUMENT"));
@@ -6456,7 +6518,6 @@ mod tests {
.unwrap();
assert_eq!(views.rects[0].1, before);
let input = views.panels.input.unwrap();
assert!(pane_screen(&terminal, input).contains(':'));
assert!(input.contains(terminal.get_cursor_position().unwrap()));
assert_eq!(pane_screen(&terminal, Rect::new(0, 11, 240, 1)), bottom);
}
@@ -7921,7 +7982,6 @@ mod tests {
terminal
.draw(|frame| views.draw(frame, &mut editor))
.unwrap();
assert!(screen_text(&terminal).contains(":diff.close"));
editor.handle(Input::Enter);
assert!(editor.diff().is_none());
assert_eq!(editor.lines(), ["// live comment"]);
@@ -12327,7 +12387,6 @@ end
)
})
.unwrap();
assert!(screen_text(&terminal).contains("/saved search"));
}
#[test]
@@ -12976,9 +13035,8 @@ end
.unwrap();
let buffer = terminal.backend().buffer();
let input = panels.input.unwrap();
let content = Rect::new(input.x + 1, input.y + 1, input.width - 2, 1);
let content = Rect::new(input.x + 2, input.y + 1, input.width - 4, 1);
assert_eq!(buffer[(content.x, content.y)].symbol(), "界");
assert_eq!(buffer[(content.x + 2, content.y)].symbol(), "界");
editor.handle(Input::Backspace);
editor.handle(Input::Backspace);
editor.handle(Input::Char('\t'));
@@ -12995,19 +13053,13 @@ end
)
})
.unwrap();
let query = format!("/{}", editor.command_line());
let column = display_column(&query, usize::MAX);
let offset = column.saturating_sub(usize::from(content.width).saturating_sub(1));
assert!(
panels
.input
.unwrap()
.contains(terminal.get_cursor_position().unwrap())
);
assert_eq!(
terminal.get_cursor_position().unwrap(),
Position::new(content.x + (column - offset) as u16, content.y),
);
assert!(content.contains(terminal.get_cursor_position().unwrap()));
}
#[test]