fix: only show cursor in focused pane
This commit is contained in:
parent
c62599de00
commit
7245aa6c12
59
src/pane.rs
59
src/pane.rs
|
|
@ -354,8 +354,8 @@ impl PaneTerminal {
|
|||
self.ghostty.extract_selection(selection)
|
||||
}
|
||||
|
||||
fn render(&self, frame: &mut Frame, area: Rect) {
|
||||
self.ghostty.render(frame, area);
|
||||
fn render(&self, frame: &mut Frame, area: Rect, show_cursor: bool) {
|
||||
self.ghostty.render(frame, area, show_cursor);
|
||||
}
|
||||
|
||||
fn apply_host_terminal_theme(&self, theme: crate::terminal_theme::TerminalTheme) {
|
||||
|
|
@ -669,7 +669,7 @@ impl GhosttyPaneTerminal {
|
|||
.and_then(|mut core| ghostty_extract_selection(&mut core, selection).ok())
|
||||
}
|
||||
|
||||
fn render(&self, frame: &mut Frame, area: Rect) {
|
||||
fn render(&self, frame: &mut Frame, area: Rect, show_cursor: bool) {
|
||||
let Ok(mut core) = self.core.lock() else {
|
||||
return;
|
||||
};
|
||||
|
|
@ -732,7 +732,7 @@ impl GhosttyPaneTerminal {
|
|||
}
|
||||
}
|
||||
|
||||
if render_state.cursor_visible().ok() == Some(true) {
|
||||
if show_cursor && render_state.cursor_visible().ok() == Some(true) {
|
||||
if let Ok(Some(cursor)) = render_state.cursor_viewport() {
|
||||
if cursor.x < area.width && cursor.y < area.height {
|
||||
frame.set_cursor_position((area.x + cursor.x, area.y + cursor.y));
|
||||
|
|
@ -1722,8 +1722,8 @@ impl PaneRuntime {
|
|||
self.terminal.extract_selection(selection)
|
||||
}
|
||||
|
||||
pub fn render(&self, frame: &mut Frame, area: Rect) {
|
||||
self.terminal.render(frame, area);
|
||||
pub fn render(&self, frame: &mut Frame, area: Rect, show_cursor: bool) {
|
||||
self.terminal.render(frame, area, show_cursor);
|
||||
}
|
||||
|
||||
pub fn keyboard_protocol(&self) -> crate::input::KeyboardProtocol {
|
||||
|
|
@ -1842,6 +1842,30 @@ impl PaneRuntime {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl PaneRuntime {
|
||||
pub(crate) fn test_with_screen_bytes(cols: u16, rows: u16, bytes: &[u8]) -> Self {
|
||||
let (tx, _rx) = mpsc::channel(4);
|
||||
let (resize_tx, _resize_rx) = mpsc::channel(1);
|
||||
let mut terminal = crate::ghostty::Terminal::new(cols, rows, 0).unwrap();
|
||||
terminal.write(bytes);
|
||||
|
||||
Self {
|
||||
terminal: Arc::new(PaneTerminal {
|
||||
ghostty: GhosttyPaneTerminal::new(terminal, tx.clone()).unwrap(),
|
||||
}),
|
||||
sender: tx,
|
||||
resize_tx,
|
||||
current_size: Cell::new((rows, cols)),
|
||||
child_pid: Arc::new(AtomicU32::new(0)),
|
||||
kitty_keyboard_flags: Arc::new(AtomicU16::new(0)),
|
||||
detect_reset_notify: Arc::new(Notify::new()),
|
||||
pending_release: Arc::new(Mutex::new(None)),
|
||||
detect_handle: tokio::spawn(async {}).abort_handle(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
@ -1852,6 +1876,29 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ghostty_render_can_suppress_cursor_position() {
|
||||
let (tx, _rx) = mpsc::channel(4);
|
||||
let mut first_terminal = crate::ghostty::Terminal::new(20, 5, 0).unwrap();
|
||||
first_terminal.write(b"left");
|
||||
let first = GhosttyPaneTerminal::new(first_terminal, tx.clone()).unwrap();
|
||||
|
||||
let mut second_terminal = crate::ghostty::Terminal::new(20, 5, 0).unwrap();
|
||||
second_terminal.write(b"r\r\nb");
|
||||
let second = GhosttyPaneTerminal::new(second_terminal, tx).unwrap();
|
||||
|
||||
let backend = ratatui::backend::TestBackend::new(40, 5);
|
||||
let mut terminal = ratatui::Terminal::new(backend).unwrap();
|
||||
terminal
|
||||
.draw(|frame| {
|
||||
first.render(frame, Rect::new(0, 0, 20, 5), true);
|
||||
second.render(frame, Rect::new(20, 0, 20, 5), false);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
terminal.backend_mut().assert_cursor_position((4, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ghostty_keyboard_protocol_tracks_live_terminal_flags() {
|
||||
let (tx, _rx) = mpsc::channel(4);
|
||||
|
|
|
|||
44
src/ui.rs
44
src/ui.rs
|
|
@ -1207,8 +1207,8 @@ fn render_panes(app: &AppState, frame: &mut Frame, area: Rect) {
|
|||
frame.render_widget(block, info.rect);
|
||||
}
|
||||
|
||||
// Draw terminal content
|
||||
rt.render(frame, info.inner_rect);
|
||||
// Draw terminal content. Only the focused pane should own the cursor.
|
||||
rt.render(frame, info.inner_rect, info.is_focused && terminal_active);
|
||||
render_pane_scrollbar(app, frame, info, rt);
|
||||
|
||||
// Dim unfocused panes only in navigate mode
|
||||
|
|
@ -3029,6 +3029,46 @@ fn _build_hints(items: &[(&str, &str)], key_style: Style, dim_style: Style) -> V
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::{detect::Agent, workspace::Workspace};
|
||||
use ratatui::{backend::TestBackend, Terminal};
|
||||
|
||||
#[tokio::test]
|
||||
async fn focused_pane_cursor_wins_during_terminal_render() {
|
||||
let mut app = crate::app::state::AppState::test_new();
|
||||
let mut ws = Workspace::test_new("test");
|
||||
let first_pane = ws.tabs[0].root_pane;
|
||||
let second_pane = ws.test_split(ratatui::layout::Direction::Horizontal);
|
||||
|
||||
ws.tabs[0].runtimes.insert(
|
||||
first_pane,
|
||||
crate::pane::PaneRuntime::test_with_screen_bytes(20, 5, b"left"),
|
||||
);
|
||||
ws.tabs[0].runtimes.insert(
|
||||
second_pane,
|
||||
crate::pane::PaneRuntime::test_with_screen_bytes(20, 5, b"r\r\nb"),
|
||||
);
|
||||
ws.tabs[0].layout.focus_pane(first_pane);
|
||||
|
||||
app.workspaces = vec![ws];
|
||||
app.active = Some(0);
|
||||
app.selected = 0;
|
||||
app.mode = Mode::Terminal;
|
||||
|
||||
compute_view(&mut app, Rect::new(0, 0, 80, 20));
|
||||
let focused = app
|
||||
.view
|
||||
.pane_infos
|
||||
.iter()
|
||||
.find(|info| info.id == first_pane)
|
||||
.expect("focused pane info");
|
||||
|
||||
let backend = TestBackend::new(80, 20);
|
||||
let mut terminal = Terminal::new(backend).unwrap();
|
||||
terminal.draw(|frame| render(&app, frame)).unwrap();
|
||||
|
||||
terminal
|
||||
.backend_mut()
|
||||
.assert_cursor_position((focused.inner_rect.x + 4, focused.inner_rect.y));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_workspaces_agent_panel_entries_use_workspace_and_optional_tab_labels() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue