aboutsummaryrefslogtreecommitdiff
path: root/src/ui/tab_surface.rs
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-09-02 14:24:33 +0300
committerGitHub <noreply@github.com>2026-09-02 14:24:33 +0300
commit6c0bb273d5d5405a00985621b17e36f8b4d64609 (patch)
treeceef5ea8adf337bf3c1e977b133e76cb763c6f1b /src/ui/tab_surface.rs
parentcc88b3b8e5bb9f7d9f23ed6ae85a52fd7b5b9ed6 (diff)
feat: support independent multi-client tab views (#3526)
Diffstat (limited to 'src/ui/tab_surface.rs')
-rw-r--r--src/ui/tab_surface.rs89
1 files changed, 77 insertions, 12 deletions
diff --git a/src/ui/tab_surface.rs b/src/ui/tab_surface.rs
index 766c1d83..d7b80bff 100644
--- a/src/ui/tab_surface.rs
+++ b/src/ui/tab_surface.rs
@@ -1,18 +1,26 @@
use ratatui::{layout::Rect, Frame};
-use super::panes::{compute_pane_infos, render_panes, resize_tab_panes};
+use super::panes::{compute_pane_infos_for_tab, render_panes, resize_tab_panes};
use crate::app::AppState;
use crate::layout::{PaneInfo, SplitBorder};
use crate::protocol::CursorState;
use crate::terminal::TerminalRuntimeRegistry;
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub(crate) struct TabSurfaceTarget {
+ pub(crate) workspace_index: usize,
+ pub(crate) tab_index: usize,
+}
+
pub(crate) struct TabSurfaceLayout {
+ pub(crate) target: Option<TabSurfaceTarget>,
pub(crate) pane_infos: Vec<PaneInfo>,
pub(crate) split_borders: Vec<SplitBorder>,
}
#[derive(Clone, Copy)]
pub(crate) struct TabSurfaceView<'a> {
+ pub(crate) target: Option<TabSurfaceTarget>,
pub(crate) pane_infos: &'a [PaneInfo],
pub(crate) split_borders: &'a [SplitBorder],
}
@@ -24,20 +32,60 @@ pub(crate) fn compute_tab_surface(
resize_panes: bool,
cell_size: crate::kitty_graphics::HostCellSize,
) -> TabSurfaceLayout {
- let split_borders = app
- .active
- .and_then(|i| app.workspaces.get(i))
- .map(|ws| {
- if ws.zoomed {
+ let target = app.active.and_then(|workspace_index| {
+ let workspace = app.workspaces.get(workspace_index)?;
+ Some(TabSurfaceTarget {
+ workspace_index,
+ tab_index: workspace.active_tab_index(),
+ })
+ });
+ compute_tab_surface_for(
+ app,
+ terminal_runtimes,
+ target,
+ area,
+ resize_panes,
+ cell_size,
+ )
+}
+
+pub(crate) fn compute_tab_surface_for(
+ app: &AppState,
+ terminal_runtimes: &TerminalRuntimeRegistry,
+ target: Option<TabSurfaceTarget>,
+ area: Rect,
+ resize_panes: bool,
+ cell_size: crate::kitty_graphics::HostCellSize,
+) -> TabSurfaceLayout {
+ let tab = target.and_then(|target| {
+ app.workspaces
+ .get(target.workspace_index)?
+ .tabs
+ .get(target.tab_index)
+ });
+ let split_borders = tab
+ .map(|tab| {
+ if tab.zoomed {
Vec::new()
} else {
- ws.layout.splits(area)
+ tab.layout.splits(area)
}
})
.unwrap_or_default();
- let pane_infos = compute_pane_infos(app, terminal_runtimes, area, resize_panes, cell_size);
+ let pane_infos = target.map_or_else(Vec::new, |target| {
+ compute_pane_infos_for_tab(
+ app,
+ terminal_runtimes,
+ target.workspace_index,
+ target.tab_index,
+ area,
+ resize_panes,
+ cell_size,
+ )
+ });
TabSurfaceLayout {
+ target,
pane_infos,
split_borders,
}
@@ -46,11 +94,26 @@ pub(crate) fn compute_tab_surface(
pub(crate) fn resize_tab_surface(
app: &AppState,
terminal_runtimes: &TerminalRuntimeRegistry,
- tab: &crate::workspace::Tab,
+ workspace_index: usize,
+ tab_index: usize,
area: Rect,
cell_size: crate::kitty_graphics::HostCellSize,
) {
- resize_tab_panes(app, terminal_runtimes, tab, area, cell_size);
+ let Some(tab) = app
+ .workspaces
+ .get(workspace_index)
+ .and_then(|workspace| workspace.tabs.get(tab_index))
+ else {
+ return;
+ };
+ resize_tab_panes(
+ app,
+ terminal_runtimes,
+ workspace_index,
+ tab,
+ area,
+ cell_size,
+ );
}
pub(crate) fn render_tab_surface(
@@ -63,6 +126,7 @@ pub(crate) fn render_tab_surface(
app,
terminal_runtimes,
frame,
+ surface.target,
surface.pane_infos,
surface.split_borders,
);
@@ -73,7 +137,7 @@ pub(crate) fn tab_surface_hyperlinks(
terminal_runtimes: &TerminalRuntimeRegistry,
surface: TabSurfaceView<'_>,
) -> Vec<((u16, u16), String, String)> {
- let Some(ws_idx) = app.active else {
+ let Some(ws_idx) = surface.target.map(|target| target.workspace_index) else {
return Vec::new();
};
if app.workspaces.get(ws_idx).is_none() {
@@ -95,7 +159,7 @@ pub(crate) fn tab_surface_cursor(
terminal_runtimes: &TerminalRuntimeRegistry,
surface: TabSurfaceView<'_>,
) -> Option<CursorState> {
- let ws_idx = app.active?;
+ let ws_idx = surface.target?.workspace_index;
let info = surface.pane_infos.iter().find(|info| info.is_focused)?;
if !app.pane_exposes_host_cursor(ws_idx, info.id) {
return None;
@@ -192,6 +256,7 @@ mod tests {
app.view.pane_infos.clear();
let surface_view = TabSurfaceView {
+ target: surface.target,
pane_infos: &surface.pane_infos,
split_borders: &surface.split_borders,
};