aboutsummaryrefslogtreecommitdiff
path: root/src/layout
diff options
context:
space:
mode:
authorFluxTape <fluxtape.contact@gmail.com>2024-03-19 14:27:52 +0000
committerGitHub <noreply@github.com>2024-03-19 07:27:52 -0700
commit23ac3d73232f307186212293713d6801d37cff2a (patch)
tree68d4e721a3c3e88cae2157a27cd28944d1eb3ea4 /src/layout
parentc3327d36da25f37e86eb8f89bd74f2a4bc1ea744 (diff)
downloadniri-23ac3d73232f307186212293713d6801d37cff2a.tar.gz
niri-23ac3d73232f307186212293713d6801d37cff2a.tar.bz2
niri-23ac3d73232f307186212293713d6801d37cff2a.zip
Workspace back and forth (#253)
* implement workspace back and forth * Make our own ID counter instead of SerialCounter, use a newtype * Rename FocusWorkspaceBackAndForth to FocusWorkspacePrevious * Add focus-workspace-previous to tests * Don't special case in switch_workspace_previous * Minor clean up * Add switch_workspace_auto_back_and_forth to tests * Skip animation on switch_workspace_previous * Preserve previous_workspace_id on workspace movement * Make Workspace::id private with a getter Reduce the chance it gets overwritten. * Add test for workspace ID uniqueness * Update previous workspace ID upon moving workspace across monitors --------- Co-authored-by: Ivan Molodetskikh <yalterz@gmail.com>
Diffstat (limited to 'src/layout')
-rw-r--r--src/layout/mod.rs36
-rw-r--r--src/layout/monitor.rs37
-rw-r--r--src/layout/workspace.rs21
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);