diff options
Diffstat (limited to 'src/layout')
| -rw-r--r-- | src/layout/mod.rs | 36 | ||||
| -rw-r--r-- | src/layout/monitor.rs | 37 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 21 |
3 files changed, 93 insertions, 1 deletions
diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 7e3b9f59..55c1e45d 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -1165,6 +1165,20 @@ impl<W: LayoutElement> Layout<W> { monitor.switch_workspace(idx); } + pub fn switch_workspace_auto_back_and_forth(&mut self, idx: usize) { + let Some(monitor) = self.active_monitor() else { + return; + }; + monitor.switch_workspace_auto_back_and_forth(idx); + } + + pub fn switch_workspace_previous(&mut self) { + let Some(monitor) = self.active_monitor() else { + return; + }; + monitor.switch_workspace_previous(); + } + pub fn consume_into_column(&mut self) { let Some(monitor) = self.active_monitor() else { return; @@ -1221,8 +1235,12 @@ impl<W: LayoutElement> Layout<W> { #[cfg(test)] fn verify_invariants(&self) { + use std::collections::HashSet; + use crate::layout::monitor::WorkspaceSwitch; + let mut seen_workspace_id = HashSet::new(); + let (monitors, &primary_idx, &active_monitor_idx) = match &self.monitor_set { MonitorSet::Normal { monitors, @@ -1241,6 +1259,11 @@ impl<W: LayoutElement> Layout<W> { "workspace options must be synchronized with layout" ); + assert!( + seen_workspace_id.insert(workspace.id()), + "workspace id must be unique" + ); + workspace.verify_invariants(); } @@ -1325,6 +1348,11 @@ impl<W: LayoutElement> Layout<W> { "workspace options must be synchronized with layout" ); + assert!( + seen_workspace_id.insert(workspace.id()), + "workspace id must be unique" + ); + workspace.verify_invariants(); } } @@ -1529,6 +1557,8 @@ impl<W: LayoutElement> Layout<W> { .unwrap(); let target = &mut monitors[target_idx]; + target.previous_workspace_id = Some(target.workspaces[target.active_workspace_idx].id()); + // Insert the workspace after the currently active one. Unless the currently active one is // the last empty workspace, then insert before. let target_ws_idx = min(target.active_workspace_idx + 1, target.workspaces.len() - 1); @@ -2017,6 +2047,8 @@ mod tests { FocusWorkspaceDown, FocusWorkspaceUp, FocusWorkspace(#[proptest(strategy = "0..=4usize")] usize), + FocusWorkspaceAutoBackAndForth(#[proptest(strategy = "0..=4usize")] usize), + FocusWorkspacePrevious, MoveWindowToWorkspaceDown, MoveWindowToWorkspaceUp, MoveWindowToWorkspace(#[proptest(strategy = "0..=4usize")] usize), @@ -2219,6 +2251,10 @@ mod tests { Op::FocusWorkspaceDown => layout.switch_workspace_down(), Op::FocusWorkspaceUp => layout.switch_workspace_up(), Op::FocusWorkspace(idx) => layout.switch_workspace(idx), + Op::FocusWorkspaceAutoBackAndForth(idx) => { + layout.switch_workspace_auto_back_and_forth(idx) + } + Op::FocusWorkspacePrevious => layout.switch_workspace_previous(), Op::MoveWindowToWorkspaceDown => layout.move_to_workspace_down(), Op::MoveWindowToWorkspaceUp => layout.move_to_workspace_up(), Op::MoveWindowToWorkspace(idx) => layout.move_to_workspace(idx), diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs index 6660d1b2..dd5709dd 100644 --- a/src/layout/monitor.rs +++ b/src/layout/monitor.rs @@ -10,7 +10,8 @@ use smithay::output::Output; use smithay::utils::{Logical, Point, Rectangle, Scale}; use super::workspace::{ - compute_working_area, Column, ColumnWidth, OutputId, Workspace, WorkspaceRenderElement, + compute_working_area, Column, ColumnWidth, OutputId, Workspace, WorkspaceId, + WorkspaceRenderElement, }; use super::{LayoutElement, Options}; use crate::animation::Animation; @@ -35,6 +36,8 @@ pub struct Monitor<W: LayoutElement> { pub workspaces: Vec<Workspace<W>>, /// Index of the currently active workspace. pub active_workspace_idx: usize, + /// ID of the previously active workspace. + pub previous_workspace_id: Option<WorkspaceId>, /// In-progress switch between workspaces. pub workspace_switch: Option<WorkspaceSwitch>, /// Configurable properties of the layout. @@ -89,6 +92,7 @@ impl<W: LayoutElement> Monitor<W> { output, workspaces, active_workspace_idx: 0, + previous_workspace_id: None, workspace_switch: None, options, } @@ -114,6 +118,8 @@ impl<W: LayoutElement> Monitor<W> { .map(|s| s.current_idx()) .unwrap_or(self.active_workspace_idx as f64); + self.previous_workspace_id = Some(self.workspaces[self.active_workspace_idx].id()); + self.active_workspace_idx = idx; self.workspace_switch = Some(WorkspaceSwitch::Animation(Animation::new( @@ -461,6 +467,11 @@ impl<W: LayoutElement> Monitor<W> { )); } + fn previous_workspace_idx(&self) -> Option<usize> { + let id = self.previous_workspace_id?; + self.workspaces.iter().position(|w| w.id() == id) + } + pub fn switch_workspace(&mut self, idx: usize) { self.activate_workspace(min(idx, self.workspaces.len() - 1)); // Don't animate this action. @@ -469,6 +480,24 @@ impl<W: LayoutElement> Monitor<W> { self.clean_up_workspaces(); } + pub fn switch_workspace_auto_back_and_forth(&mut self, idx: usize) { + let idx = min(idx, self.workspaces.len() - 1); + + if idx == self.active_workspace_idx { + if let Some(prev_idx) = self.previous_workspace_idx() { + self.switch_workspace(prev_idx); + } + } else { + self.switch_workspace(idx); + } + } + + pub fn switch_workspace_previous(&mut self) { + if let Some(idx) = self.previous_workspace_idx() { + self.switch_workspace(idx); + } + } + pub fn consume_into_column(&mut self) { self.active_workspace().consume_into_column(); } @@ -564,8 +593,10 @@ impl<W: LayoutElement> Monitor<W> { self.workspaces.push(ws); } + let previous_workspace_id = self.previous_workspace_id; self.activate_workspace(new_idx); self.workspace_switch = None; + self.previous_workspace_id = previous_workspace_id; self.clean_up_workspaces(); } @@ -584,8 +615,10 @@ impl<W: LayoutElement> Monitor<W> { self.workspaces.push(ws); } + let previous_workspace_id = self.previous_workspace_id; self.activate_workspace(new_idx); self.workspace_switch = None; + self.previous_workspace_id = previous_workspace_id; self.clean_up_workspaces(); } @@ -833,6 +866,8 @@ impl<W: LayoutElement> Monitor<W> { gesture.center_idx as f64 + current_pos, ); + self.previous_workspace_id = Some(self.workspaces[self.active_workspace_idx].id()); + self.active_workspace_idx = new_idx; self.workspace_switch = Some(WorkspaceSwitch::Animation(Animation::new( gesture.current_idx, diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index 2c18b40e..e2a95ad4 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -18,6 +18,7 @@ use crate::animation::Animation; use crate::niri_render_elements; use crate::render_helpers::renderer::NiriRenderer; use crate::swipe_tracker::SwipeTracker; +use crate::utils::id::IdCounter; use crate::utils::output_size; /// Amount of touchpad movement to scroll the view for the width of one working area. @@ -76,11 +77,25 @@ pub struct Workspace<W: LayoutElement> { /// Configurable properties of the layout. pub options: Rc<Options>, + + /// Unique ID of this workspace. + id: WorkspaceId, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct OutputId(String); +static WORKSPACE_ID_COUNTER: IdCounter = IdCounter::new(); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct WorkspaceId(u32); + +impl WorkspaceId { + fn next() -> WorkspaceId { + WorkspaceId(WORKSPACE_ID_COUNTER.next()) + } +} + niri_render_elements! { WorkspaceRenderElement<R> => { Tile = TileRenderElement<R>, @@ -225,6 +240,7 @@ impl<W: LayoutElement> Workspace<W> { view_offset_adj: None, activate_prev_column_on_removal: None, options, + id: WorkspaceId::next(), } } @@ -240,9 +256,14 @@ impl<W: LayoutElement> Workspace<W> { view_offset_adj: None, activate_prev_column_on_removal: None, options, + id: WorkspaceId::next(), } } + pub fn id(&self) -> WorkspaceId { + self.id + } + pub fn advance_animations(&mut self, current_time: Duration, is_active: bool) { if let Some(ViewOffsetAdjustment::Animation(anim)) = &mut self.view_offset_adj { anim.set_current_time(current_time); |
