feat: configure command and search popup anchors
This commit is contained in:
+139
-2
@@ -33,7 +33,7 @@ use unicode_segmentation::UnicodeSegmentation;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use crate::{
|
||||
chrome::{ChromeLine, SegmentConfig},
|
||||
chrome::{ChromeLine, PopupAnchor, SegmentConfig},
|
||||
diff::{ChangedFile, DiffState, DisplayRow, FileStatus, Focus, PaneLayout, Phase, RowKind},
|
||||
editor::{
|
||||
Completion, Cursor, Editor, Hover, Input, Mode, Palette, Selection, StartupDiagnostic,
|
||||
@@ -1840,7 +1840,32 @@ fn draw_floating_input(frame: &mut Frame, editor: &Editor, area: Rect) -> Option
|
||||
}
|
||||
let width = area.width.min(72);
|
||||
let height = area.height.min(3);
|
||||
let panel = Rect::new(area.x, area.bottom() - height, width, height);
|
||||
let anchor = if editor.mode() == Mode::Search {
|
||||
editor.popup_config().search.anchor
|
||||
} else {
|
||||
editor.popup_config().command.anchor
|
||||
};
|
||||
let x = area.x
|
||||
+ match anchor {
|
||||
PopupAnchor::TopLeft | PopupAnchor::CenterLeft | PopupAnchor::BottomLeft => 0,
|
||||
PopupAnchor::TopCenter | PopupAnchor::Center | PopupAnchor::BottomCenter => {
|
||||
(area.width - width) / 2
|
||||
}
|
||||
PopupAnchor::TopRight | PopupAnchor::CenterRight | PopupAnchor::BottomRight => {
|
||||
area.width - width
|
||||
}
|
||||
};
|
||||
let y = area.y
|
||||
+ match anchor {
|
||||
PopupAnchor::TopLeft | PopupAnchor::TopCenter | PopupAnchor::TopRight => 0,
|
||||
PopupAnchor::CenterLeft | PopupAnchor::Center | PopupAnchor::CenterRight => {
|
||||
(area.height - height) / 2
|
||||
}
|
||||
PopupAnchor::BottomLeft | PopupAnchor::BottomCenter | PopupAnchor::BottomRight => {
|
||||
area.height - height
|
||||
}
|
||||
};
|
||||
let panel = Rect::new(x, y, width, height);
|
||||
let bordered = panel.width >= 4 && panel.height >= 3;
|
||||
let padding = u16::from(panel.width.saturating_sub(2 * u16::from(bordered)) >= 3);
|
||||
let mut block = Block::default()
|
||||
@@ -5638,6 +5663,118 @@ mod tests {
|
||||
assert_eq!(panel.bottom(), 12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configured_floating_inputs_paint_each_anchor_independently_inside_the_workspace() {
|
||||
let anchors = [
|
||||
(PopupAnchor::TopLeft, 0, 0),
|
||||
(PopupAnchor::TopCenter, 14, 0),
|
||||
(PopupAnchor::TopRight, 29, 0),
|
||||
(PopupAnchor::CenterLeft, 0, 5),
|
||||
(PopupAnchor::Center, 14, 5),
|
||||
(PopupAnchor::CenterRight, 29, 5),
|
||||
(PopupAnchor::BottomLeft, 0, 10),
|
||||
(PopupAnchor::BottomCenter, 14, 10),
|
||||
(PopupAnchor::BottomRight, 29, 10),
|
||||
];
|
||||
for (index, &(command_anchor, command_x, command_y)) in anchors.iter().enumerate() {
|
||||
let (search_anchor, search_x, search_y) = anchors[(index + 1) % anchors.len()];
|
||||
for (prefix, title, x, y) in [
|
||||
(':', "Command", command_x, command_y),
|
||||
('/', "Search", search_x, search_y),
|
||||
] {
|
||||
let mut editor = Editor::open(None, vec![]).unwrap();
|
||||
let mut popup = *editor.popup_config();
|
||||
popup.command.anchor = command_anchor;
|
||||
popup.search.anchor = search_anchor;
|
||||
editor.set_popup_config(popup);
|
||||
editor.set_message("hidden notification");
|
||||
editor.handle(Input::Char(prefix));
|
||||
for ch in "needle".chars() {
|
||||
editor.handle(Input::Char(ch));
|
||||
}
|
||||
for (width, height) in [
|
||||
(101, 13),
|
||||
(12, 3),
|
||||
(3, 2),
|
||||
(2, 1),
|
||||
(1, 1),
|
||||
(0, 1),
|
||||
(1, 0),
|
||||
(0, 0),
|
||||
] {
|
||||
let workspace = Rect::new(5, 4, width, height);
|
||||
let mut terminal =
|
||||
Terminal::new(ratatui::backend::TestBackend::new(120, 24)).unwrap();
|
||||
let mut panels = WheelPanels::default();
|
||||
terminal
|
||||
.draw(|frame| {
|
||||
frame.render_widget(
|
||||
Paragraph::new(vec![Line::from(".".repeat(120)); 24]),
|
||||
frame.area(),
|
||||
);
|
||||
draw_overlays(frame, &editor, workspace, None, &mut panels);
|
||||
})
|
||||
.unwrap();
|
||||
assert!(panels.notification.is_none());
|
||||
if width == 0 || height == 0 {
|
||||
assert!(panels.input.is_none());
|
||||
assert_eq!(
|
||||
terminal.backend().buffer()[(workspace.x, workspace.y)].symbol(),
|
||||
"."
|
||||
);
|
||||
continue;
|
||||
}
|
||||
let expected = if width == 101 {
|
||||
Rect::new(workspace.x + x, workspace.y + y, 72, 3)
|
||||
} else {
|
||||
workspace
|
||||
};
|
||||
let panel = panels.input.unwrap();
|
||||
assert_eq!(
|
||||
panel, expected,
|
||||
"{prefix} {command_anchor:?} {search_anchor:?}"
|
||||
);
|
||||
assert_eq!(panel.intersection(workspace), panel);
|
||||
let cursor = terminal.get_cursor_position().unwrap();
|
||||
assert!(panel.contains(cursor));
|
||||
let buffer = terminal.backend().buffer();
|
||||
assert_eq!(buffer[(0, 0)].symbol(), ".");
|
||||
assert_eq!(buffer[(cursor.x, cursor.y)].symbol(), " ");
|
||||
if width >= 12 {
|
||||
assert_eq!(buffer[(panel.x, panel.y)].symbol(), "┌");
|
||||
assert_eq!(buffer[(panel.right() - 1, panel.y)].symbol(), "┐");
|
||||
assert_eq!(buffer[(panel.x, panel.bottom() - 1)].symbol(), "└");
|
||||
assert_eq!(
|
||||
buffer[(panel.right() - 1, panel.bottom() - 1)].symbol(),
|
||||
"┘"
|
||||
);
|
||||
assert!(
|
||||
pane_screen(&terminal, Rect::new(panel.x, panel.y, panel.width, 1))
|
||||
.contains(title)
|
||||
);
|
||||
assert_eq!(buffer[(panel.x + 1, panel.y + 1)].symbol(), " ");
|
||||
assert_eq!(buffer[(panel.x + 2, panel.y + 1)].symbol(), "n");
|
||||
assert_eq!(
|
||||
pane_screen(
|
||||
&terminal,
|
||||
Rect::new(panel.x + 1, panel.y + 1, panel.width - 2, 1)
|
||||
)
|
||||
.trim(),
|
||||
"needle"
|
||||
);
|
||||
assert_eq!(cursor, Position::new(panel.x + 8, panel.y + 1));
|
||||
} else {
|
||||
let cursor_x = u16::from(width > 1);
|
||||
assert_eq!(cursor, Position::new(panel.x + cursor_x, panel.y));
|
||||
if width == 2 {
|
||||
assert_eq!(buffer[(panel.x, panel.y)].symbol(), "e");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn floating_inputs_share_workspace_geometry_across_documents_splits_and_diffs() {
|
||||
for context in ["document", "split", "diff", "split-diff", "selector"] {
|
||||
|
||||
Reference in New Issue
Block a user