diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-09-02 14:24:33 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-02 14:24:33 +0300 |
| commit | 6c0bb273d5d5405a00985621b17e36f8b4d64609 (patch) | |
| tree | ceef5ea8adf337bf3c1e977b133e76cb763c6f1b /src/app | |
| parent | cc88b3b8e5bb9f7d9f23ed6ae85a52fd7b5b9ed6 (diff) | |
feat: support independent multi-client tab views (#3526)
Diffstat (limited to 'src/app')
| -rw-r--r-- | src/app/api.rs | 81 | ||||
| -rw-r--r-- | src/app/creation.rs | 19 | ||||
| -rw-r--r-- | src/app/ids.rs | 8 | ||||
| -rw-r--r-- | src/app/popup.rs | 5 | ||||
| -rw-r--r-- | src/app/state.rs | 39 |
5 files changed, 73 insertions, 79 deletions
diff --git a/src/app/api.rs b/src/app/api.rs index ff5a7dc4..e6f6b6ec 100644 --- a/src/app/api.rs +++ b/src/app/api.rs @@ -657,8 +657,56 @@ impl App { self.sync_focus_events_with_outer_event(None); } - pub(crate) fn send_outer_focus_event(&mut self, event: crate::ghostty::FocusEvent) { - self.sync_focus_events_with_outer_event(Some(event)); + pub(crate) fn accept_current_focus_without_events(&mut self) { + self.last_focus = self.state.active.and_then(|idx| { + self.state + .workspaces + .get(idx) + .and_then(|workspace| workspace.focused_pane_id().map(|pane_id| (idx, pane_id))) + }); + } + + pub(crate) fn accept_current_focus_with_api_events(&mut self) { + let current_focus = self.state.active.and_then(|idx| { + self.state + .workspaces + .get(idx) + .and_then(|workspace| workspace.focused_pane_id().map(|pane_id| (idx, pane_id))) + }); + if current_focus == self.last_focus { + return; + } + self.last_focus = current_focus; + if let Some((ws_idx, pane_id)) = current_focus { + self.emit_focus_api_events(ws_idx, pane_id); + } + } + + fn emit_focus_api_events(&mut self, ws_idx: usize, pane_id: crate::layout::PaneId) { + self.emit_event(crate::api::schema::EventEnvelope { + event: crate::api::schema::EventKind::WorkspaceFocused, + data: crate::api::schema::EventData::WorkspaceFocused { + workspace_id: self.public_workspace_id(ws_idx), + }, + }); + if let Some(tab_id) = self.public_tab_id(ws_idx, self.state.workspaces[ws_idx].active_tab) { + self.emit_event(crate::api::schema::EventEnvelope { + event: crate::api::schema::EventKind::TabFocused, + data: crate::api::schema::EventData::TabFocused { + tab_id, + workspace_id: self.public_workspace_id(ws_idx), + }, + }); + } + if let Some(public_pane_id) = self.public_pane_id(ws_idx, pane_id) { + self.emit_event(crate::api::schema::EventEnvelope { + event: crate::api::schema::EventKind::PaneFocused, + data: crate::api::schema::EventData::PaneFocused { + pane_id: public_pane_id, + workspace_id: self.public_workspace_id(ws_idx), + }, + }); + } } fn sync_focus_events_with_outer_event( @@ -690,38 +738,13 @@ impl App { } }); self.send_pane_focus_event(ws_idx, pane_id, event); - self.emit_event(crate::api::schema::EventEnvelope { - event: crate::api::schema::EventKind::WorkspaceFocused, - data: crate::api::schema::EventData::WorkspaceFocused { - workspace_id: self.public_workspace_id(ws_idx), - }, - }); - if let Some(tab_id) = - self.public_tab_id(ws_idx, self.state.workspaces[ws_idx].active_tab) - { - self.emit_event(crate::api::schema::EventEnvelope { - event: crate::api::schema::EventKind::TabFocused, - data: crate::api::schema::EventData::TabFocused { - tab_id, - workspace_id: self.public_workspace_id(ws_idx), - }, - }); - } - if let Some(public_pane_id) = self.public_pane_id(ws_idx, pane_id) { - self.emit_event(crate::api::schema::EventEnvelope { - event: crate::api::schema::EventKind::PaneFocused, - data: crate::api::schema::EventData::PaneFocused { - pane_id: public_pane_id, - workspace_id: self.public_workspace_id(ws_idx), - }, - }); - } + self.emit_focus_api_events(ws_idx, pane_id); } self.last_focus = current_focus; } - fn send_pane_focus_event( + pub(crate) fn send_pane_focus_event( &self, ws_idx: usize, pane_id: crate::layout::PaneId, diff --git a/src/app/creation.rs b/src/app/creation.rs index fbe61902..af34e540 100644 --- a/src/app/creation.rs +++ b/src/app/creation.rs @@ -79,8 +79,23 @@ impl App { } pub(crate) fn resolved_new_workspace_cwd_from(&self, ws_idx: usize) -> PathBuf { - let follow_cwd = self - .focused_pane_cwd_in_workspace(ws_idx) + let tab_idx = self + .state + .workspaces + .get(ws_idx) + .map(crate::workspace::Workspace::active_tab_index); + self.resolved_new_workspace_cwd_from_tab(ws_idx, tab_idx) + } + + pub(crate) fn resolved_new_workspace_cwd_from_tab( + &self, + ws_idx: usize, + tab_idx: Option<usize>, + ) -> PathBuf { + let follow_cwd = tab_idx + .and_then(|tab_idx| self.state.workspaces.get(ws_idx)?.tabs.get(tab_idx)) + .map(|tab| tab.layout.focused()) + .and_then(|pane_id| self.launch_cwd_for_pane_in_workspace(ws_idx, pane_id)) .or_else(|| self.seed_cwd_from_workspace(ws_idx)); self.resolve_new_terminal_cwd(follow_cwd) } diff --git a/src/app/ids.rs b/src/app/ids.rs index c33a5fff..df273250 100644 --- a/src/app/ids.rs +++ b/src/app/ids.rs @@ -12,11 +12,11 @@ impl App { .find_map(|(ws_idx, ws)| ws.pane_state(pane_id).map(|pane| (ws_idx, pane))) } - pub(super) fn public_workspace_id(&self, ws_idx: usize) -> String { + pub(crate) fn public_workspace_id(&self, ws_idx: usize) -> String { self.state.workspaces[ws_idx].id.clone() } - pub(super) fn public_tab_id(&self, ws_idx: usize, tab_idx: usize) -> Option<String> { + pub(crate) fn public_tab_id(&self, ws_idx: usize, tab_idx: usize) -> Option<String> { let ws = self.state.workspaces.get(ws_idx)?; let tab_number = ws.public_tab_number(tab_idx)?; Some(crate::workspace::public_tab_id_for_number( @@ -57,7 +57,7 @@ impl App { ) } - pub(super) fn parse_workspace_id(&self, id: &str) -> Option<usize> { + pub(crate) fn parse_workspace_id(&self, id: &str) -> Option<usize> { self.state .workspaces .iter() @@ -66,7 +66,7 @@ impl App { .or_else(|| id.parse::<usize>().ok()?.checked_sub(1)) } - pub(super) fn parse_tab_id(&self, id: &str) -> Option<(usize, usize)> { + pub(crate) fn parse_tab_id(&self, id: &str) -> Option<(usize, usize)> { if let Some(rest) = id.strip_prefix("t_") { let (ws_raw, tab_raw) = rest.rsplit_once('_')?; let ws_idx = self.parse_workspace_id(ws_raw)?; diff --git a/src/app/popup.rs b/src/app/popup.rs index 8bcd8ae3..0643ea8f 100644 --- a/src/app/popup.rs +++ b/src/app/popup.rs @@ -13,11 +13,6 @@ pub(crate) struct PopupGeometry { } impl App { - pub(crate) fn popup_runtime(&self) -> Option<&TerminalRuntime> { - let terminal_id = &self.state.popup_pane.as_ref()?.terminal_id; - self.terminal_runtimes.get(terminal_id) - } - pub(crate) fn close_popup_pane(&mut self) -> bool { let Some(popup) = self.state.popup_pane.take() else { return false; diff --git a/src/app/state.rs b/src/app/state.rs index 0ce9fb6d..3d3700b2 100644 --- a/src/app/state.rs +++ b/src/app/state.rs @@ -921,35 +921,6 @@ impl AppState { }) } - pub(crate) fn app_surface_pane_ids(&self) -> std::collections::HashSet<PaneId> { - let mut pane_ids = std::collections::HashSet::new(); - if let Some(popup) = &self.popup_pane { - pane_ids.insert(popup.pane_id); - } - let Some(tab) = self - .active - .and_then(|ws_idx| self.workspaces.get(ws_idx)) - .and_then(crate::workspace::Workspace::active_tab) - else { - return pane_ids; - }; - if tab.zoomed { - pane_ids.insert(tab.layout.focused()); - } else { - pane_ids.extend(tab.panes.keys().copied()); - } - pane_ids - } - - pub(crate) fn focused_pane_requests_mouse_capture_from( - &self, - terminal_runtimes: &crate::terminal::TerminalRuntimeRegistry, - ) -> bool { - self.active - .and_then(|idx| self.focused_runtime_in_workspace(terminal_runtimes, idx)) - .is_some_and(crate::terminal::TerminalRuntime::mouse_reporting_enabled) - } - pub fn estimate_pane_size(&self) -> (u16, u16) { if let Some(info) = self.view.pane_infos.first() { (info.rect.height, info.rect.width) @@ -984,16 +955,6 @@ impl AppState { terminal_runtimes.get(terminal_id) } - pub(crate) fn focused_runtime_in_workspace<'a>( - &'a self, - terminal_runtimes: &'a crate::terminal::TerminalRuntimeRegistry, - ws_idx: usize, - ) -> Option<&'a crate::terminal::TerminalRuntime> { - let ws = self.workspaces.get(ws_idx)?; - let pane_id = ws.focused_pane_id()?; - self.runtime_for_pane_in_workspace(terminal_runtimes, ws_idx, pane_id) - } - pub(crate) fn pane_visible_on_active_surface( &self, ws_idx: usize, |
