aboutsummaryrefslogtreecommitdiff
path: root/src/layout
diff options
context:
space:
mode:
Diffstat (limited to 'src/layout')
-rw-r--r--src/layout/mod.rs37
-rw-r--r--src/layout/monitor.rs77
-rw-r--r--src/layout/workspace.rs73
3 files changed, 186 insertions, 1 deletions
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index fbbd1292..5933b217 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -897,6 +897,27 @@ impl<W: LayoutElement> Layout<W> {
monitor.move_to_workspace(idx);
}
+ pub fn move_column_to_workspace_up(&mut self) {
+ let Some(monitor) = self.active_monitor() else {
+ return;
+ };
+ monitor.move_column_to_workspace_up();
+ }
+
+ pub fn move_column_to_workspace_down(&mut self) {
+ let Some(monitor) = self.active_monitor() else {
+ return;
+ };
+ monitor.move_column_to_workspace_down();
+ }
+
+ pub fn move_column_to_workspace(&mut self, idx: usize) {
+ let Some(monitor) = self.active_monitor() else {
+ return;
+ };
+ monitor.move_column_to_workspace(idx);
+ }
+
pub fn switch_workspace_up(&mut self) {
let Some(monitor) = self.active_monitor() else {
return;
@@ -1608,6 +1629,9 @@ mod tests {
MoveWindowToWorkspaceDown,
MoveWindowToWorkspaceUp,
MoveWindowToWorkspace(#[proptest(strategy = "0..=4usize")] usize),
+ MoveColumnToWorkspaceDown,
+ MoveColumnToWorkspaceUp,
+ MoveColumnToWorkspace(#[proptest(strategy = "0..=4usize")] usize),
MoveWorkspaceDown,
MoveWorkspaceUp,
MoveWindowToOutput(#[proptest(strategy = "1..=5u8")] u8),
@@ -1729,6 +1753,9 @@ mod tests {
Op::MoveWindowToWorkspaceDown => layout.move_to_workspace_down(),
Op::MoveWindowToWorkspaceUp => layout.move_to_workspace_up(),
Op::MoveWindowToWorkspace(idx) => layout.move_to_workspace(idx),
+ Op::MoveColumnToWorkspaceDown => layout.move_column_to_workspace_down(),
+ Op::MoveColumnToWorkspaceUp => layout.move_column_to_workspace_up(),
+ Op::MoveColumnToWorkspace(idx) => layout.move_column_to_workspace(idx),
Op::MoveWindowToOutput(id) => {
let name = format!("output{id}");
let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {
@@ -1855,6 +1882,11 @@ mod tests {
Op::MoveWindowToWorkspace(1),
Op::MoveWindowToWorkspace(2),
Op::MoveWindowToWorkspace(3),
+ Op::MoveColumnToWorkspaceDown,
+ Op::MoveColumnToWorkspaceUp,
+ Op::MoveColumnToWorkspace(1),
+ Op::MoveColumnToWorkspace(2),
+ Op::MoveColumnToWorkspace(3),
Op::MoveWindowDown,
Op::MoveWindowDownOrToWorkspaceDown,
Op::MoveWindowUp,
@@ -1977,6 +2009,11 @@ mod tests {
Op::MoveWindowToWorkspace(1),
Op::MoveWindowToWorkspace(2),
Op::MoveWindowToWorkspace(3),
+ Op::MoveColumnToWorkspaceDown,
+ Op::MoveColumnToWorkspaceUp,
+ Op::MoveColumnToWorkspace(1),
+ Op::MoveColumnToWorkspace(2),
+ Op::MoveColumnToWorkspace(3),
Op::MoveWindowDown,
Op::MoveWindowDownOrToWorkspaceDown,
Op::MoveWindowUp,
diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs
index a5c4d6dd..11eef73a 100644
--- a/src/layout/monitor.rs
+++ b/src/layout/monitor.rs
@@ -12,7 +12,7 @@ use smithay::output::Output;
use smithay::utils::{Logical, Point, Rectangle, Scale};
use super::workspace::{
- compute_working_area, ColumnWidth, OutputId, Workspace, WorkspaceRenderElement,
+ compute_working_area, Column, ColumnWidth, OutputId, Workspace, WorkspaceRenderElement,
};
use super::{LayoutElement, Options};
use crate::animation::Animation;
@@ -127,6 +127,25 @@ impl<W: LayoutElement> Monitor<W> {
}
}
+ pub fn add_column(&mut self, workspace_idx: usize, column: Column<W>, activate: bool) {
+ let workspace = &mut self.workspaces[workspace_idx];
+
+ workspace.add_column(column, activate);
+
+ // After adding a new window, workspace becomes this output's own.
+ workspace.original_output = OutputId::new(&self.output);
+
+ if workspace_idx == self.workspaces.len() - 1 {
+ // Insert a new empty workspace.
+ let ws = Workspace::new(self.output.clone(), self.options.clone());
+ self.workspaces.push(ws);
+ }
+
+ if activate {
+ self.activate_workspace(workspace_idx);
+ }
+ }
+
fn clean_up_workspaces(&mut self) {
assert!(self.workspace_switch.is_none());
@@ -323,6 +342,62 @@ impl<W: LayoutElement> Monitor<W> {
self.clean_up_workspaces();
}
+ pub fn move_column_to_workspace_up(&mut self) {
+ let source_workspace_idx = self.active_workspace_idx;
+
+ let new_idx = source_workspace_idx.saturating_sub(1);
+ if new_idx == source_workspace_idx {
+ return;
+ }
+
+ let workspace = &mut self.workspaces[source_workspace_idx];
+ if workspace.columns.is_empty() {
+ return;
+ }
+
+ let column = workspace.remove_column_by_idx(workspace.active_column_idx);
+ self.add_column(new_idx, column, true);
+ }
+
+ pub fn move_column_to_workspace_down(&mut self) {
+ let source_workspace_idx = self.active_workspace_idx;
+
+ let new_idx = min(source_workspace_idx + 1, self.workspaces.len() - 1);
+ if new_idx == source_workspace_idx {
+ return;
+ }
+
+ let workspace = &mut self.workspaces[source_workspace_idx];
+ if workspace.columns.is_empty() {
+ return;
+ }
+
+ let column = workspace.remove_column_by_idx(workspace.active_column_idx);
+ self.add_column(new_idx, column, true);
+ }
+
+ pub fn move_column_to_workspace(&mut self, idx: usize) {
+ let source_workspace_idx = self.active_workspace_idx;
+
+ let new_idx = min(idx, self.workspaces.len() - 1);
+ if new_idx == source_workspace_idx {
+ return;
+ }
+
+ let workspace = &mut self.workspaces[source_workspace_idx];
+ if workspace.columns.is_empty() {
+ return;
+ }
+
+ let column = workspace.remove_column_by_idx(workspace.active_column_idx);
+ self.add_column(new_idx, column, true);
+
+ // Don't animate this action.
+ self.workspace_switch = None;
+
+ self.clean_up_workspaces();
+ }
+
pub fn switch_workspace_up(&mut self) {
self.activate_workspace(self.active_workspace_idx.saturating_sub(1));
}
diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs
index 3259785d..7405ecec 100644
--- a/src/layout/workspace.rs
+++ b/src/layout/workspace.rs
@@ -574,6 +574,43 @@ impl<W: LayoutElement> Workspace<W> {
}
}
+ pub fn add_column(&mut self, mut column: Column<W>, activate: bool) {
+ for tile in &column.tiles {
+ self.enter_output_for_window(tile.window());
+ }
+
+ let was_empty = self.columns.is_empty();
+
+ let idx = if self.columns.is_empty() {
+ 0
+ } else {
+ self.active_column_idx + 1
+ };
+
+ column.set_view_size(self.view_size, self.working_area);
+ let width = column.width();
+ self.columns.insert(idx, column);
+
+ if activate {
+ // If this is the first window on an empty workspace, skip the animation from whatever
+ // view_offset was left over.
+ if was_empty {
+ if self.options.center_focused_column == CenterFocusedColumn::Always {
+ self.view_offset =
+ -(self.working_area.size.w - width) / 2 - self.working_area.loc.x;
+ } else {
+ // Try to make the code produce a left-aligned offset, even in presence of left
+ // exclusive zones.
+ self.view_offset = self.compute_new_view_offset_for_column(self.column_x(0), 0);
+ }
+ self.view_offset_anim = None;
+ }
+
+ self.activate_column(idx);
+ self.activate_prev_column_on_removal = true;
+ }
+ }
+
pub fn remove_window_by_idx(&mut self, column_idx: usize, window_idx: usize) -> W {
let column = &mut self.columns[column_idx];
let window = column.tiles.remove(window_idx).into_window();
@@ -618,6 +655,42 @@ impl<W: LayoutElement> Workspace<W> {
window
}
+ pub fn remove_column_by_idx(&mut self, column_idx: usize) -> Column<W> {
+ let column = self.columns.remove(column_idx);
+
+ if let Some(output) = &self.output {
+ for tile in &column.tiles {
+ tile.window().output_leave(output);
+ }
+ }
+
+ if column_idx + 1 == self.active_column_idx {
+ // The previous column, that we were going to activate upon removal of the active
+ // column, has just been itself removed.
+ self.activate_prev_column_on_removal = false;
+ }
+
+ // FIXME: activate_column below computes current view position to compute the new view
+ // position, which can include the column we're removing here. This leads to unwanted
+ // view jumps.
+ if self.columns.is_empty() {
+ return column;
+ }
+
+ if self.active_column_idx > column_idx
+ || (self.active_column_idx == column_idx && self.activate_prev_column_on_removal)
+ {
+ // A column to the left was removed; preserve the current position.
+ // FIXME: preserve activate_prev_column_on_removal.
+ // Or, the active column was removed, and we needed to activate the previous column.
+ self.activate_column(self.active_column_idx.saturating_sub(1));
+ } else {
+ self.activate_column(min(self.active_column_idx, self.columns.len() - 1));
+ }
+
+ column
+ }
+
pub fn remove_window(&mut self, window: &W) {
let column_idx = self
.columns