fix: render floating windows and explorer outside workspace panes
This commit is contained in:
+205
-75
@@ -378,7 +378,19 @@ impl BufferViewports {
|
||||
} else {
|
||||
Some(document_layout(area, editor))
|
||||
};
|
||||
let workspace = chrome.map_or(area, |areas| areas[0]);
|
||||
let full_workspace = chrome.map_or(area, |areas| areas[0]);
|
||||
let (workspace, dock) = match editor.file_picker() {
|
||||
Some(picker) if picker.presentation == FilePresentation::Docked => dock_layout(
|
||||
full_workspace,
|
||||
if editor.diff().is_some() {
|
||||
DockSide::Right
|
||||
} else {
|
||||
editor.file_explorer_side()
|
||||
},
|
||||
picker.focused,
|
||||
),
|
||||
_ => (full_workspace, None),
|
||||
};
|
||||
editor.set_pane_bounds(pane_rect(workspace));
|
||||
self.rects.clear();
|
||||
editor
|
||||
@@ -394,6 +406,7 @@ impl BufferViewports {
|
||||
continue;
|
||||
}
|
||||
self.activate(id, editor);
|
||||
self.panels.workspace_managed = true;
|
||||
if multiple {
|
||||
let block =
|
||||
Block::default()
|
||||
@@ -444,6 +457,43 @@ impl BufferViewports {
|
||||
}
|
||||
}
|
||||
self.activate(focused, editor);
|
||||
self.panels.workspace_managed = false;
|
||||
if let Some(panel) = dock
|
||||
&& let Some(picker) = editor.file_picker()
|
||||
{
|
||||
draw_file_list(
|
||||
frame,
|
||||
picker,
|
||||
panel,
|
||||
editor.file_navigation_hint(),
|
||||
&mut self.panels,
|
||||
editor
|
||||
.diff()
|
||||
.and_then(|diff| diff.comparison.as_ref())
|
||||
.map(|comparison| comparison.files.as_slice()),
|
||||
);
|
||||
if let Some(geometry) = &mut self.panels.diff {
|
||||
geometry.regions[0] = (panel, Focus::Files);
|
||||
editor.set_diff_pane_bounds(
|
||||
geometry
|
||||
.regions
|
||||
.map(|(area, focus)| (focus, pane_rect(area))),
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(diff) = editor.diff().filter(|diff| diff.phase != Phase::View) {
|
||||
draw_diff(
|
||||
frame,
|
||||
diff,
|
||||
area,
|
||||
editor.selection_navigation_hint(),
|
||||
editor.file_navigation_hint(),
|
||||
&mut self.panels,
|
||||
Some(editor),
|
||||
);
|
||||
} else {
|
||||
draw_overlays(frame, editor, area, self.panels.caret, &mut self.panels);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_session(&mut self, frame: &mut Frame, session: &mut EditorSession) {
|
||||
@@ -901,6 +951,9 @@ fn draw_document(
|
||||
let focused = editor.render_pane_focused();
|
||||
if let Some(diff) = editor.diff() {
|
||||
draw_status(frame, editor, syntax, status_area);
|
||||
if panels.workspace_managed && diff.phase != Phase::View {
|
||||
return;
|
||||
}
|
||||
draw_diff(
|
||||
frame,
|
||||
diff,
|
||||
@@ -918,7 +971,11 @@ fn draw_document(
|
||||
}
|
||||
let prompting = matches!(editor.mode(), Mode::Command | Mode::Search);
|
||||
let (viewport, dock) = match editor.file_picker() {
|
||||
Some(picker) if focused && picker.presentation == FilePresentation::Docked => {
|
||||
Some(picker)
|
||||
if !panels.workspace_managed
|
||||
&& focused
|
||||
&& picker.presentation == FilePresentation::Docked =>
|
||||
{
|
||||
dock_layout(workspace, editor.file_explorer_side(), picker.focused)
|
||||
}
|
||||
_ => (workspace, None),
|
||||
@@ -1294,24 +1351,11 @@ fn draw_document(
|
||||
if !focused {
|
||||
return;
|
||||
}
|
||||
if let Some(completion) = editor.completion()
|
||||
&& let Some(caret) = document_caret
|
||||
{
|
||||
panels.completion = draw_completion(frame, completion, viewport, caret);
|
||||
}
|
||||
if let Some(picker) = editor.file_picker() {
|
||||
if picker.presentation == FilePresentation::Floating {
|
||||
let panel = floating_panel(overlay_area);
|
||||
frame.render_widget(Clear, panel);
|
||||
draw_file_list(
|
||||
frame,
|
||||
picker,
|
||||
panel,
|
||||
editor.file_navigation_hint(),
|
||||
panels,
|
||||
None,
|
||||
);
|
||||
} else if let Some(panel) = dock {
|
||||
panels.caret = document_caret.map(|caret| (viewport, caret));
|
||||
if !panels.workspace_managed {
|
||||
if let Some(panel) = dock
|
||||
&& let Some(picker) = editor.file_picker()
|
||||
{
|
||||
draw_file_list(
|
||||
frame,
|
||||
picker,
|
||||
@@ -1321,6 +1365,38 @@ fn draw_document(
|
||||
None,
|
||||
);
|
||||
}
|
||||
draw_overlays(frame, editor, overlay_area, panels.caret, panels);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_overlays(
|
||||
frame: &mut Frame,
|
||||
editor: &Editor,
|
||||
area: Rect,
|
||||
caret: Option<(Rect, Position)>,
|
||||
panels: &mut WheelPanels,
|
||||
) {
|
||||
if let Some(completion) = editor.completion()
|
||||
&& let Some((_, caret)) = caret
|
||||
{
|
||||
panels.completion = draw_completion(frame, completion, area, caret);
|
||||
}
|
||||
if let Some(picker) = editor.file_picker()
|
||||
&& picker.presentation == FilePresentation::Floating
|
||||
{
|
||||
let panel = floating_panel(area);
|
||||
frame.render_widget(Clear, panel);
|
||||
draw_file_list(
|
||||
frame,
|
||||
picker,
|
||||
panel,
|
||||
editor.file_navigation_hint(),
|
||||
panels,
|
||||
editor
|
||||
.diff()
|
||||
.and_then(|diff| diff.comparison.as_ref())
|
||||
.map(|comparison| comparison.files.as_slice()),
|
||||
);
|
||||
}
|
||||
if let Some(palette) = editor.palette() {
|
||||
draw_palette(
|
||||
@@ -1331,10 +1407,10 @@ fn draw_document(
|
||||
);
|
||||
}
|
||||
if let Some(hover) = editor.hover() {
|
||||
draw_hover(frame, hover, viewport, panels);
|
||||
draw_hover(frame, hover, area, panels);
|
||||
}
|
||||
if let Some(diagnostic) = editor.startup_diagnostic() {
|
||||
draw_startup_diagnostic(frame, diagnostic, overlay_area);
|
||||
draw_startup_diagnostic(frame, diagnostic, area);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1680,11 +1756,6 @@ fn draw_diff_popups(
|
||||
if !editor.render_pane_focused() {
|
||||
return;
|
||||
}
|
||||
if let Some((text_area, caret)) = caret
|
||||
&& let Some(completion) = editor.completion()
|
||||
{
|
||||
panels.completion = draw_completion(frame, completion, text_area, caret);
|
||||
}
|
||||
if area.width > 0 && area.height > 0 {
|
||||
let footer = Rect::new(area.x, area.bottom() - 1, area.width, 1);
|
||||
if matches!(editor.mode(), Mode::Command | Mode::Search) {
|
||||
@@ -1707,19 +1778,9 @@ fn draw_diff_popups(
|
||||
frame.render_widget(Paragraph::new(display_text(editor.message())), footer);
|
||||
}
|
||||
}
|
||||
if let Some(palette) = editor.palette() {
|
||||
draw_palette(
|
||||
frame,
|
||||
palette,
|
||||
editor.mode(),
|
||||
editor.selection_navigation_hint(),
|
||||
);
|
||||
}
|
||||
if let Some(hover) = editor.hover() {
|
||||
draw_hover(frame, hover, caret.map_or(area, |(area, _)| area), panels);
|
||||
}
|
||||
if let Some(diagnostic) = editor.startup_diagnostic() {
|
||||
draw_startup_diagnostic(frame, diagnostic, area);
|
||||
panels.caret = caret;
|
||||
if !panels.workspace_managed {
|
||||
draw_overlays(frame, editor, area, caret, panels);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1740,9 +1801,9 @@ fn draw_diff(
|
||||
if area.width == 0 || area.height == 0 {
|
||||
return;
|
||||
}
|
||||
frame.render_widget(Clear, area);
|
||||
if diff.phase != Phase::View {
|
||||
let panel = floating_panel(area);
|
||||
frame.render_widget(Clear, panel);
|
||||
let stage = if diff.phase == Phase::Before {
|
||||
"Before"
|
||||
} else {
|
||||
@@ -1933,6 +1994,7 @@ fn draw_diff(
|
||||
}
|
||||
return;
|
||||
}
|
||||
frame.render_widget(Clear, area);
|
||||
|
||||
let syntax_error = panels
|
||||
.diff_syntax
|
||||
@@ -1998,7 +2060,8 @@ fn draw_diff(
|
||||
notice_area,
|
||||
);
|
||||
}
|
||||
let (panes, navigator) = if diff.files_visible
|
||||
let (panes, navigator) = if !panels.workspace_managed
|
||||
&& diff.files_visible
|
||||
&& diff
|
||||
.files
|
||||
.as_ref()
|
||||
@@ -2254,22 +2317,6 @@ fn draw_diff(
|
||||
text_widths,
|
||||
wrapping,
|
||||
});
|
||||
if let Some(picker) = &diff.files
|
||||
&& diff.files_visible
|
||||
&& picker.presentation == FilePresentation::Floating
|
||||
&& editor.is_none_or(|editor| editor.render_pane_focused())
|
||||
{
|
||||
let panel = floating_panel(area);
|
||||
frame.render_widget(Clear, panel);
|
||||
draw_file_list(
|
||||
frame,
|
||||
picker,
|
||||
panel,
|
||||
file_navigation_hint,
|
||||
panels,
|
||||
comparison.map(|comparison| comparison.files.as_slice()),
|
||||
);
|
||||
}
|
||||
if let Some(editor) = editor {
|
||||
draw_diff_popups(frame, editor, area, document_caret, panels);
|
||||
}
|
||||
@@ -2862,6 +2909,8 @@ fn diff_visible_rows(
|
||||
|
||||
#[derive(Default)]
|
||||
struct WheelPanels {
|
||||
workspace_managed: bool,
|
||||
caret: Option<(Rect, Position)>,
|
||||
completion: Option<Rect>,
|
||||
hover: Option<(Rect, usize)>,
|
||||
hover_syntax: SyntaxCache,
|
||||
@@ -2903,6 +2952,7 @@ impl WheelPanels {
|
||||
self.manual_files = None;
|
||||
}
|
||||
self.hover = None;
|
||||
self.caret = None;
|
||||
self.completion = None;
|
||||
self.files = None;
|
||||
self.previous_diff = self.diff.take();
|
||||
@@ -2959,10 +3009,6 @@ impl WheelPanels {
|
||||
{
|
||||
return true;
|
||||
}
|
||||
let pane = session.editor().pane_id();
|
||||
if self.diff.is_some() && self.scroll_comparison(session, mouse, pane) {
|
||||
return true;
|
||||
}
|
||||
let editor = session.editor();
|
||||
if editor.palette().is_some() || editor.startup_diagnostic().is_some() {
|
||||
return true;
|
||||
@@ -2994,6 +3040,17 @@ impl WheelPanels {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
let pane = session.editor().pane_id();
|
||||
let floating_files = editor.file_picker().is_some_and(|picker| {
|
||||
picker.presentation == FilePresentation::Floating
|
||||
&& self
|
||||
.files
|
||||
.is_some_and(|(area, _, _)| area.contains(position))
|
||||
});
|
||||
if !floating_files && self.diff.is_some() && self.scroll_comparison(session, mouse, pane) {
|
||||
return true;
|
||||
}
|
||||
let editor = session.editor();
|
||||
if let Some(picker) = editor.file_picker()
|
||||
&& let Some((panel, height, drawn_top)) = self.files
|
||||
&& panel.contains(position)
|
||||
@@ -4823,6 +4880,66 @@ mod tests {
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explorer_dock_reserves_space_outside_all_workspace_panes() {
|
||||
let directory = Directory::new();
|
||||
let mut editor =
|
||||
Editor::open(Some(directory.file("shared.txt", "first\nsecond")), vec![]).unwrap();
|
||||
editor.set_project_root(directory.0.clone());
|
||||
editor.handle(Input::Char('|'));
|
||||
editor.handle(Input::Char('_'));
|
||||
editor.handle(Input::Char(' '));
|
||||
editor.handle(Input::Char('e'));
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(120, 30)).unwrap();
|
||||
let mut views = BufferViewports::default();
|
||||
terminal
|
||||
.draw(|frame| views.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
let dock = views.panels.files.unwrap().0;
|
||||
assert!(views.rects.iter().all(|(_, pane)| pane.right() <= dock.x));
|
||||
assert_eq!(dock.right(), 120);
|
||||
assert_eq!(dock.y, 0);
|
||||
assert_eq!(dock.height, 28);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn revision_selector_overlays_and_clears_workspace_pane_borders() {
|
||||
let mut editor = Editor::open(None, vec![]).unwrap();
|
||||
editor.handle(Input::Char('|'));
|
||||
editor.execute("diff");
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(120, 30)).unwrap();
|
||||
let mut views = BufferViewports::default();
|
||||
terminal
|
||||
.draw(|frame| views.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
assert!(screen_text(&terminal).contains("Choose Before"));
|
||||
let results = views.panels.diff.as_ref().unwrap().regions[0].0;
|
||||
assert!(results.x < 60 && results.right() > 60);
|
||||
for x in [59, 60] {
|
||||
assert_eq!(terminal.backend().buffer()[(x, 20)].symbol(), " ");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_floating_picker_overlays_the_whole_workspace() {
|
||||
let directory = Directory::new();
|
||||
let mut editor =
|
||||
Editor::open(Some(directory.file("source.txt", "workspace")), vec![]).unwrap();
|
||||
editor.handle(Input::Char('|'));
|
||||
install_readonly_diff(&mut editor, syntax_diff_file("history.txt", "old", "new"));
|
||||
editor.handle(Input::Char(' '));
|
||||
editor.handle(Input::Char('o'));
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(120, 30)).unwrap();
|
||||
let mut views = BufferViewports::default();
|
||||
terminal
|
||||
.draw(|frame| views.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
views.panels.files.unwrap().0,
|
||||
floating_panel(Rect::new(0, 0, 120, 30))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_workspace_panes_share_edits_and_undo_but_not_carets() {
|
||||
let directory = Directory::new();
|
||||
@@ -6184,14 +6301,10 @@ mod tests {
|
||||
#[test]
|
||||
fn hover_scroll_renders_whole_graphemes_in_tiny_workspace() {
|
||||
let mut editor = Editor::open(None, vec![]).unwrap();
|
||||
editor.show_hover("x界e\u{301}👩🚀z\nlast".into()).unwrap();
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(6, 1)).unwrap();
|
||||
let mut syntax = SyntaxCache::new();
|
||||
let (mut top, mut left) = (0, 0);
|
||||
for _ in 0..2 {
|
||||
editor.handle(Input::Right);
|
||||
}
|
||||
let rendered = terminal
|
||||
terminal
|
||||
.draw(|frame| {
|
||||
draw(
|
||||
frame,
|
||||
@@ -6201,16 +6314,33 @@ mod tests {
|
||||
&mut syntax,
|
||||
&mut DocumentViewport::default(),
|
||||
&mut WheelPanels::default(),
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
let document_cursor = terminal.get_cursor_position().unwrap();
|
||||
editor.show_hover("x界e\u{301}👩🚀z\nlast".into()).unwrap();
|
||||
for _ in 0..2 {
|
||||
editor.handle(Input::Right);
|
||||
}
|
||||
let mut panels = WheelPanels::default();
|
||||
let rendered = terminal
|
||||
.draw(|frame| {
|
||||
draw(
|
||||
frame,
|
||||
&editor,
|
||||
&mut top,
|
||||
&mut left,
|
||||
&mut syntax,
|
||||
&mut DocumentViewport::default(),
|
||||
&mut panels,
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(rendered.buffer[(2, 0)].symbol(), "e\u{301}");
|
||||
assert_eq!(rendered.buffer[(3, 0)].symbol(), "👩🚀");
|
||||
assert_eq!(rendered.buffer[(5, 0)].symbol(), "z");
|
||||
assert_eq!(
|
||||
terminal.get_cursor_position().unwrap(),
|
||||
ratatui::layout::Position::new(2, 0)
|
||||
);
|
||||
let origin = panels.hover.unwrap().0;
|
||||
assert_eq!(rendered.buffer[(origin.x, origin.y)].symbol(), "e\u{301}");
|
||||
assert_eq!(rendered.buffer[(origin.x + 1, origin.y)].symbol(), "👩🚀");
|
||||
assert_eq!(rendered.buffer[(origin.x + 3, origin.y)].symbol(), "z");
|
||||
assert_eq!(terminal.get_cursor_position().unwrap(), document_cursor);
|
||||
assert_eq!((top, left), (0, 0));
|
||||
editor.handle(Input::Down);
|
||||
editor.handle(Input::Home);
|
||||
@@ -6227,7 +6357,7 @@ mod tests {
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
assert!(screen_text(&terminal).ends_with("last"));
|
||||
assert!(screen_text(&terminal).trim().ends_with("last"));
|
||||
for (width, height) in [(1, 1), (2, 2), (3, 3), (4, 4)] {
|
||||
let mut terminal =
|
||||
Terminal::new(ratatui::backend::TestBackend::new(width, height)).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user