aboutsummaryrefslogtreecommitdiff
path: root/src/app/actions.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/actions.rs')
-rw-r--r--src/app/actions.rs120
1 files changed, 120 insertions, 0 deletions
diff --git a/src/app/actions.rs b/src/app/actions.rs
index 0709ed4c..10d28054 100644
--- a/src/app/actions.rs
+++ b/src/app/actions.rs
@@ -1390,6 +1390,73 @@ impl AppState {
true
}
+ pub fn move_workspace_block(
+ &mut self,
+ workspace_ids: &[String],
+ before_workspace_id: Option<&str>,
+ ) -> bool {
+ let moved_ids = workspace_ids
+ .iter()
+ .map(String::as_str)
+ .collect::<std::collections::HashSet<_>>();
+ if moved_ids.is_empty()
+ || moved_ids.len() != workspace_ids.len()
+ || !workspace_ids
+ .iter()
+ .all(|id| self.workspaces.iter().any(|workspace| workspace.id == *id))
+ || before_workspace_id.is_some_and(|id| {
+ moved_ids.contains(id)
+ || !self.workspaces.iter().any(|workspace| workspace.id == id)
+ })
+ {
+ return false;
+ }
+
+ let mut desired_ids = self
+ .workspaces
+ .iter()
+ .filter(|workspace| !moved_ids.contains(workspace.id.as_str()))
+ .map(|workspace| workspace.id.clone())
+ .collect::<Vec<_>>();
+ let insert_idx = before_workspace_id
+ .and_then(|id| desired_ids.iter().position(|candidate| candidate == id))
+ .unwrap_or(desired_ids.len());
+ desired_ids.splice(insert_idx..insert_idx, workspace_ids.iter().cloned());
+ if self
+ .workspaces
+ .iter()
+ .map(|workspace| workspace.id.as_str())
+ .eq(desired_ids.iter().map(String::as_str))
+ {
+ return false;
+ }
+
+ let active_id = self.active.map(|idx| self.workspaces[idx].id.clone());
+ let selected_id = self
+ .workspaces
+ .get(self.selected)
+ .map(|workspace| workspace.id.clone());
+ let desired_positions = desired_ids
+ .iter()
+ .enumerate()
+ .map(|(index, id)| (id.clone(), index))
+ .collect::<std::collections::HashMap<_, _>>();
+
+ self.mark_session_dirty();
+ self.workspaces.sort_by_key(|workspace| {
+ desired_positions
+ .get(&workspace.id)
+ .copied()
+ .unwrap_or(usize::MAX)
+ });
+ self.active = active_id.and_then(|id| self.workspaces.iter().position(|ws| ws.id == id));
+ self.selected = selected_id
+ .and_then(|id| self.workspaces.iter().position(|ws| ws.id == id))
+ .unwrap_or(0);
+ self.ensure_workspace_visible(self.selected);
+ true
+ }
+
pub fn scroll_tabs_left(&mut self) {
self.tab_scroll_follow_active = false;
self.tab_scroll = self.tab_scroll.saturating_sub(1);
@@ -4434,6 +4501,59 @@ mod tests {
}
#[test]
+ fn move_workspace_block_collects_non_contiguous_members() {
+ let mut state =
+ app_with_workspaces(&["child-one", "normal", "parent", "child-two", "tail"]);
+ let parent_id = state.workspaces[2].id.clone();
+ let child_one_id = state.workspaces[0].id.clone();
+ let child_two_id = state.workspaces[3].id.clone();
+ let tail_id = state.workspaces[4].id.clone();
+ state.active = Some(0);
+ state.selected = 4;
+
+ assert!(state.move_workspace_block(
+ &[parent_id, child_one_id.clone(), child_two_id],
+ Some(&tail_id),
+ ));
+
+ let names = state
+ .workspaces
+ .iter()
+ .map(|workspace| workspace.display_name())
+ .collect::<Vec<_>>();
+ assert_eq!(
+ names,
+ ["normal", "parent", "child-one", "child-two", "tail"]
+ );
+ assert_eq!(state.workspaces[state.active.unwrap()].id, child_one_id);
+ assert_eq!(state.workspaces[state.selected].id, tail_id);
+ }
+
+ #[test]
+ fn move_workspace_block_rejects_invalid_and_noop_orders() {
+ let mut state = app_with_workspaces(&["a", "b", "c"]);
+ let ids = state
+ .workspaces
+ .iter()
+ .map(|workspace| workspace.id.clone())
+ .collect::<Vec<_>>();
+
+ assert!(!state.move_workspace_block(&[], None));
+ assert!(!state.move_workspace_block(&[ids[0].clone(), ids[0].clone()], None));
+ assert!(!state.move_workspace_block(&["missing".into()], None));
+ assert!(!state.move_workspace_block(&[ids[0].clone()], Some(&ids[0])));
+ assert!(!state.move_workspace_block(&[ids[0].clone()], Some(&ids[1])));
+ assert_eq!(
+ state
+ .workspaces
+ .iter()
+ .map(|workspace| workspace.display_name())
+ .collect::<Vec<_>>(),
+ ["a", "b", "c"]
+ );
+ }
+
+ #[test]
fn close_workspace_adjusts_indices() {
let mut state = app_with_workspaces(&["a", "b", "c"]);
state.selected = 1;