diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-09-03 02:47:48 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-03 02:47:48 +0300 |
| commit | 94f6d9c0d9bb9cf9ffae99d8bbfb09e9bf2fc9e0 (patch) | |
| tree | e4dfc7ea631cbb872ffc59a7ca0270a8fe000d7d /src/client/shell | |
| parent | 2ae8b91ca5919c26df7ce779b0e9a5dd98b769ae (diff) | |
fix: reveal newly focused spaces in the sidebar (#3554)
Diffstat (limited to 'src/client/shell')
| -rw-r--r-- | src/client/shell/composition.rs | 1 | ||||
| -rw-r--r-- | src/client/shell/render.rs | 1 | ||||
| -rw-r--r-- | src/client/shell/scroll.rs | 23 | ||||
| -rw-r--r-- | src/client/shell/sidebar.rs | 22 | ||||
| -rw-r--r-- | src/client/shell/state.rs | 11 | ||||
| -rw-r--r-- | src/client/shell/tests/chrome_context.rs | 47 |
6 files changed, 104 insertions, 1 deletions
diff --git a/src/client/shell/composition.rs b/src/client/shell/composition.rs index 04ca995c..bcbf92fa 100644 --- a/src/client/shell/composition.rs +++ b/src/client/shell/composition.rs @@ -57,6 +57,7 @@ impl ClientShellState { workspace_scroll: &mut self.workspace_scroll, agent_scroll: &mut self.agent_scroll, tab_scroll: &mut self.tab_scroll, + reveal_focused_workspace: &mut self.reveal_focused_workspace, reveal_focused_tab: &mut self.reveal_focused_tab, sidebar_collapsed: self.sidebar_collapsed, sidebar_section_split: self.sidebar_section_split, diff --git a/src/client/shell/render.rs b/src/client/shell/render.rs index 0e7f3908..387695ec 100644 --- a/src/client/shell/render.rs +++ b/src/client/shell/render.rs @@ -200,6 +200,7 @@ pub(super) struct ShellRenderState<'a> { pub(super) workspace_scroll: &'a mut usize, pub(super) agent_scroll: &'a mut usize, pub(super) tab_scroll: &'a mut usize, + pub(super) reveal_focused_workspace: &'a mut bool, pub(super) reveal_focused_tab: &'a mut bool, pub(super) sidebar_collapsed: bool, pub(super) sidebar_section_split: f32, diff --git a/src/client/shell/scroll.rs b/src/client/shell/scroll.rs index a0496cb8..4ff507d9 100644 --- a/src/client/shell/scroll.rs +++ b/src/client/shell/scroll.rs @@ -53,6 +53,29 @@ pub(super) fn list_scroll_metrics( } } +pub(super) fn list_scroll_start_to_reveal( + row_heights: &[u16], + gaps_after: &[u16], + body_height: u16, + requested_start: usize, + target: usize, +) -> usize { + let mut metrics = list_scroll_metrics(row_heights, gaps_after, body_height, requested_start); + let mut start = metrics + .max_offset_from_bottom + .saturating_sub(metrics.offset_from_bottom); + if target < start { + return target; + } + while target >= start.saturating_add(metrics.viewport_rows) + && start < metrics.max_offset_from_bottom + { + start = start.saturating_add(1); + metrics = list_scroll_metrics(row_heights, gaps_after, body_height, start); + } + start +} + pub(super) fn render_list_scrollbar( buffer: &mut Buffer, track: Rect, diff --git a/src/client/shell/sidebar.rs b/src/client/shell/sidebar.rs index f8a87043..2a71e297 100644 --- a/src/client/shell/sidebar.rs +++ b/src/client/shell/sidebar.rs @@ -246,12 +246,32 @@ pub(crate) fn render_sidebar( .map_or(0, |next| u16::from(!next.indented) * config.spaces.row_gap) }) .collect::<Vec<_>>(); - let metrics = super::scroll::list_scroll_metrics( + let mut metrics = super::scroll::list_scroll_metrics( &row_heights, &gaps, body.height, *state.workspace_scroll, ); + if !body.is_empty() && std::mem::take(state.reveal_focused_workspace) { + if let Some(target) = entries + .iter() + .position(|entry| snapshot.workspaces[entry.index].focused) + { + *state.workspace_scroll = super::scroll::list_scroll_start_to_reveal( + &row_heights, + &gaps, + body.height, + *state.workspace_scroll, + target, + ); + metrics = super::scroll::list_scroll_metrics( + &row_heights, + &gaps, + body.height, + *state.workspace_scroll, + ); + } + } hits.workspace_max_scroll = metrics.max_offset_from_bottom; hits.workspace_scroll_metrics = Some(metrics); *state.workspace_scroll = metrics diff --git a/src/client/shell/state.rs b/src/client/shell/state.rs index ffac93da..6b00e6c3 100644 --- a/src/client/shell/state.rs +++ b/src/client/shell/state.rs @@ -880,6 +880,7 @@ pub(crate) struct ClientShellState { pub(super) agent_scroll: usize, pub(super) tab_scroll: usize, pub(super) mobile_switcher_scroll: usize, + pub(super) reveal_focused_workspace: bool, pub(super) reveal_mobile_workspace: bool, pub(super) mobile_switcher_suspended: bool, pub(super) reveal_focused_tab: bool, @@ -1021,6 +1022,7 @@ impl ClientShellState { agent_scroll: 0, tab_scroll: 0, mobile_switcher_scroll: 0, + reveal_focused_workspace: true, reveal_mobile_workspace: false, mobile_switcher_suspended: false, reveal_focused_tab: true, @@ -1242,6 +1244,7 @@ impl ClientShellState { self.agent_scroll = 0; self.tab_scroll = 0; self.mobile_switcher_scroll = 0; + self.reveal_focused_workspace = true; self.reveal_mobile_workspace = false; self.mobile_switcher_suspended = false; self.reveal_focused_tab = true; @@ -1317,6 +1320,14 @@ impl ClientShellState { }) || render::tab_bar_status_width(current) != render::tab_bar_status_width(&snapshot) }); + if self + .snapshot + .as_deref() + .and_then(|current| current.focused_workspace_id.as_deref()) + != snapshot.focused_workspace_id.as_deref() + { + self.reveal_focused_workspace = true; + } if tab_layout_changed || self .snapshot diff --git a/src/client/shell/tests/chrome_context.rs b/src/client/shell/tests/chrome_context.rs index eeb272c2..ff611a9b 100644 --- a/src/client/shell/tests/chrome_context.rs +++ b/src/client/shell/tests/chrome_context.rs @@ -47,6 +47,53 @@ fn tab_overflow_controls_scroll_the_client_owned_tab_bar() { } #[test] +fn focused_workspace_change_reveals_new_workspace_in_full_sidebar() { + let mut initial = snapshot(); + let template = initial.workspaces[0].clone(); + initial.workspaces = (1..=12) + .map(|number| ClientShellWorkspace { + workspace_id: format!("ws_{number}"), + number, + label: format!("space-{number}"), + branch: None, + focused: number == 1, + ..template.clone() + }) + .collect(); + + let mut state = ClientShellState::new(ClientShellConfig::from_config(&Config::default())); + state.set_snapshot(Box::new(initial)); + state.set_pane_surface(surface()); + state.compose(106, 20).expect("full sidebar"); + assert!(state.hits.workspace_max_scroll > 0); + assert!(state + .hits + .workspaces + .iter() + .all(|hit| hit.workspace_id != "ws_12")); + + let mut update = state.snapshot.as_deref().expect("snapshot").clone(); + update.revision = 2; + update.focused_workspace_id = Some("ws_12".into()); + for workspace in &mut update.workspaces { + workspace.focused = workspace.workspace_id == "ws_12"; + } + let mut updated_surface = surface(); + updated_surface.projection_revision = 2; + state.set_snapshot(Box::new(update)); + state.set_pane_surface(updated_surface); + state.compose(106, 2).expect("zero-height workspace body"); + assert!(state.reveal_focused_workspace); + state.compose(106, 20).expect("updated full sidebar"); + + assert!(state + .hits + .workspaces + .iter() + .any(|hit| hit.workspace_id == "ws_12")); +} + +#[test] fn client_owned_sidebar_dividers_resize_live() { let mut state = ClientShellState::new(ClientShellConfig::from_config(&Config::default())); state.set_snapshot(Box::new(snapshot())); |
