aboutsummaryrefslogtreecommitdiff
path: root/src/client/shell
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/shell')
-rw-r--r--src/client/shell/composition.rs1
-rw-r--r--src/client/shell/render.rs1
-rw-r--r--src/client/shell/scroll.rs23
-rw-r--r--src/client/shell/sidebar.rs22
-rw-r--r--src/client/shell/state.rs11
-rw-r--r--src/client/shell/tests/chrome_context.rs47
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()));