Add actions to move the active workspace to another monitor

This commit is contained in:
Andreas Stührk
2023-12-02 23:57:01 +01:00
committed by Ivan Molodetskikh
parent 0a715ce155
commit e51268a39e
4 changed files with 114 additions and 0 deletions
+4
View File
@@ -490,6 +490,10 @@ pub enum Action {
SetColumnWidth(#[knuffel(argument, str)] SizeChange), SetColumnWidth(#[knuffel(argument, str)] SizeChange),
SwitchLayout(#[knuffel(argument)] LayoutAction), SwitchLayout(#[knuffel(argument)] LayoutAction),
ShowHotkeyOverlay, ShowHotkeyOverlay,
MoveWorkspaceToMonitorLeft,
MoveWorkspaceToMonitorRight,
MoveWorkspaceToMonitorDown,
MoveWorkspaceToMonitorUp,
} }
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
+4
View File
@@ -275,6 +275,10 @@ binds {
// Mod+Shift+Ctrl+Left { move-window-to-monitor-left; } // Mod+Shift+Ctrl+Left { move-window-to-monitor-left; }
// ... // ...
// And you can also move a whole workspace to another monitor:
// Mod+Shift+Ctrl+Left { move-workspace-to-monitor-left; }
// ...
Mod+Page_Down { focus-workspace-down; } Mod+Page_Down { focus-workspace-down; }
Mod+Page_Up { focus-workspace-up; } Mod+Page_Up { focus-workspace-up; }
Mod+U { focus-workspace-down; } Mod+U { focus-workspace-down; }
+24
View File
@@ -609,6 +609,30 @@ impl State {
self.niri.queue_redraw_all(); self.niri.queue_redraw_all();
} }
} }
Action::MoveWorkspaceToMonitorLeft => {
if let Some(output) = self.niri.output_left() {
self.niri.layout.move_workspace_to_output(&output);
self.move_cursor_to_output(&output);
}
}
Action::MoveWorkspaceToMonitorRight => {
if let Some(output) = self.niri.output_right() {
self.niri.layout.move_workspace_to_output(&output);
self.move_cursor_to_output(&output);
}
}
Action::MoveWorkspaceToMonitorDown => {
if let Some(output) = self.niri.output_down() {
self.niri.layout.move_workspace_to_output(&output);
self.move_cursor_to_output(&output);
}
}
Action::MoveWorkspaceToMonitorUp => {
if let Some(output) = self.niri.output_up() {
self.niri.layout.move_workspace_to_output(&output);
self.move_cursor_to_output(&output);
}
}
} }
} }
+82
View File
@@ -1335,6 +1335,42 @@ impl<W: LayoutElement> Layout<W> {
} }
} }
pub fn move_workspace_to_output(&mut self, output: &Output) {
let MonitorSet::Normal {
monitors,
active_monitor_idx,
..
} = &mut self.monitor_set
else {
return;
};
let current = &mut monitors[*active_monitor_idx];
if current.active_workspace_idx == current.workspaces.len() - 1 {
// Insert a new empty workspace.
let ws = Workspace::new(current.output.clone(), current.options.clone());
current.workspaces.push(ws);
}
let mut ws = current.workspaces.remove(current.active_workspace_idx);
current.active_workspace_idx = current.active_workspace_idx.saturating_sub(1);
current.workspace_switch = None;
current.clean_up_workspaces();
ws.set_output(Some(output.clone()));
ws.original_output = OutputId::new(output);
let target_idx = monitors
.iter()
.position(|mon| &mon.output == output)
.unwrap();
let target = &mut monitors[target_idx];
target.workspaces.insert(target.active_workspace_idx, ws);
target.workspace_switch = None;
target.clean_up_workspaces();
*active_monitor_idx = target_idx;
}
pub fn set_fullscreen(&mut self, window: &W, is_fullscreen: bool) { pub fn set_fullscreen(&mut self, window: &W, is_fullscreen: bool) {
match &mut self.monitor_set { match &mut self.monitor_set {
MonitorSet::Normal { monitors, .. } => { MonitorSet::Normal { monitors, .. } => {
@@ -1737,6 +1773,7 @@ mod tests {
SetColumnWidth(#[proptest(strategy = "arbitrary_size_change()")] SizeChange), SetColumnWidth(#[proptest(strategy = "arbitrary_size_change()")] SizeChange),
SetWindowHeight(#[proptest(strategy = "arbitrary_size_change()")] SizeChange), SetWindowHeight(#[proptest(strategy = "arbitrary_size_change()")] SizeChange),
Communicate(#[proptest(strategy = "1..=5usize")] usize), Communicate(#[proptest(strategy = "1..=5usize")] usize),
MoveWorkspaceToOutput(#[proptest(strategy = "1..=5u8")] u8),
} }
impl Op { impl Op {
@@ -1910,6 +1947,14 @@ mod tests {
layout.update_window(&win); layout.update_window(&win);
} }
} }
Op::MoveWorkspaceToOutput(id) => {
let name = format!("output{id}");
let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {
return;
};
layout.move_workspace_to_output(&output);
}
} }
} }
} }
@@ -1996,6 +2041,7 @@ mod tests {
Op::MoveWindowDownOrToWorkspaceDown, Op::MoveWindowDownOrToWorkspaceDown,
Op::MoveWindowUp, Op::MoveWindowUp,
Op::MoveWindowUpOrToWorkspaceUp, Op::MoveWindowUpOrToWorkspaceUp,
Op::MoveWorkspaceToOutput(1),
]; ];
for third in every_op { for third in every_op {
@@ -2410,6 +2456,42 @@ mod tests {
check_ops(&ops); check_ops(&ops);
} }
#[test]
fn move_workspace_to_output() {
let ops = [
Op::AddOutput(1),
Op::AddOutput(2),
Op::FocusOutput(1),
Op::AddWindow {
id: 0,
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
min_max_size: Default::default(),
},
Op::MoveWorkspaceToOutput(2),
];
let mut layout = Layout::default();
for op in ops {
op.apply(&mut layout);
}
let MonitorSet::Normal {
monitors,
active_monitor_idx,
..
} = layout.monitor_set
else {
unreachable!()
};
assert_eq!(active_monitor_idx, 1);
assert_eq!(monitors[0].workspaces.len(), 1);
assert!(!monitors[0].workspaces[0].has_windows());
assert_eq!(monitors[1].active_workspace_idx, 0);
assert_eq!(monitors[1].workspaces.len(), 2);
assert!(monitors[1].workspaces[0].has_windows());
}
fn arbitrary_spacing() -> impl Strategy<Value = u16> { fn arbitrary_spacing() -> impl Strategy<Value = u16> {
// Give equal weight to: // Give equal weight to:
// - 0: the element is disabled // - 0: the element is disabled