mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-03-11 21:11:40 +01:00
feat(wm): swap with last container across monitors
This commit ensures that when moving across a monitor boundary, the origin window container will be swapped with the last focused window container on the other side of the monitor boundary. If there is no window container on the other side of the window boundary, it will be treated as a move instead of a swap. re #145
This commit is contained in:
@@ -967,42 +967,91 @@ impl WindowManager {
|
||||
|
||||
let workspace = self.focused_workspace()?;
|
||||
|
||||
let current_idx = workspace.focused_container_idx();
|
||||
let new_idx = workspace.new_idx_for_direction(direction);
|
||||
let origin_container_idx = workspace.focused_container_idx();
|
||||
let origin_monitor_idx = self.focused_monitor_idx();
|
||||
let target_container_idx = workspace.new_idx_for_direction(direction);
|
||||
|
||||
match new_idx {
|
||||
match target_container_idx {
|
||||
// If there is nowhere to move on the current workspace, try to move it onto the monitor
|
||||
// in that direction if there is one
|
||||
None => {
|
||||
let monitor_idx = self
|
||||
let target_monitor_idx = self
|
||||
.monitor_index_in_direction(direction)
|
||||
.ok_or_else(|| anyhow!("there is no container or monitor in this direction"))?;
|
||||
|
||||
// move to the target monitor
|
||||
self.move_container_to_monitor(monitor_idx, None, true)?;
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
{
|
||||
// remove the container from the origin monitor workspace
|
||||
let origin_container = self
|
||||
.focused_workspace_mut()?
|
||||
.remove_container_by_idx(origin_container_idx)
|
||||
.ok_or_else(|| {
|
||||
anyhow!("could not remove container at given origin index")
|
||||
})?;
|
||||
|
||||
// by default move_container_to_monitor appends to the end, so remove it
|
||||
let container = workspace
|
||||
.remove_container(workspace.containers().len() - 1)
|
||||
.ok_or_else(|| {
|
||||
anyhow!("the container was not found to have been moved to monitor in the desired direction")
|
||||
})?;
|
||||
// focus the target monitor
|
||||
self.focus_monitor(target_monitor_idx)?;
|
||||
|
||||
// decide where to reinsert it before redrawing
|
||||
let final_idx = match direction {
|
||||
OperationDirection::Right | OperationDirection::Down => 0,
|
||||
OperationDirection::Left | OperationDirection::Up => {
|
||||
workspace.containers().len()
|
||||
// get the focused workspace on the target monitor
|
||||
let target_workspace = self.focused_workspace_mut()?;
|
||||
|
||||
// insert the origin container into the focused workspace on the target monitor
|
||||
// at the position where the currently focused container on that workspace is
|
||||
target_workspace.insert_container_at_idx(
|
||||
target_workspace.focused_container_idx(),
|
||||
origin_container,
|
||||
);
|
||||
|
||||
// if there is only one container on the target workspace after the insertion
|
||||
// it means that there won't be one swapped back, so we have to decrement the
|
||||
// focused position
|
||||
if target_workspace.containers().len() == 1 {
|
||||
let origin_workspace =
|
||||
self.focused_workspace_for_monitor_idx_mut(origin_monitor_idx)?;
|
||||
|
||||
origin_workspace
|
||||
.focus_container(origin_workspace.focused_container_idx() - 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// insert it in the right place on the target monitor's workspace
|
||||
workspace.insert_container(container, final_idx);
|
||||
{
|
||||
let target_workspace = self.focused_workspace_mut()?;
|
||||
|
||||
// if the target workspace doesn't have more than one container, this means it
|
||||
// was previously empty, by only doing the second part of the swap when there is
|
||||
// more than one container, we can fall back to a "move" if there is nothing to
|
||||
// swap with on the target monitor
|
||||
if target_workspace.containers().len() > 1 {
|
||||
// remove the container from the target monitor workspace
|
||||
let target_container = target_workspace
|
||||
// this is now focused_container_idx + 1 because we have inserted our origin container
|
||||
.remove_container_by_idx(target_workspace.focused_container_idx() + 1)
|
||||
.ok_or_else(|| {
|
||||
anyhow!("could not remove container at given target index")
|
||||
})?;
|
||||
|
||||
let origin_workspace =
|
||||
self.focused_workspace_for_monitor_idx_mut(origin_monitor_idx)?;
|
||||
|
||||
// insert the container from the target monitor workspace into the origin monitor workspace
|
||||
// at the same position from which our origin container was removed
|
||||
origin_workspace
|
||||
.insert_container_at_idx(origin_container_idx, target_container);
|
||||
}
|
||||
|
||||
// make sure to update the origin monitor workspace layout because it is no
|
||||
// longer focused so it won't get updated at the end of this fn
|
||||
let offset = self.work_area_offset;
|
||||
let invisible_borders = self.invisible_borders;
|
||||
|
||||
self.monitors_mut()
|
||||
.get_mut(origin_monitor_idx)
|
||||
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
|
||||
.update_focused_workspace(offset, &invisible_borders)?;
|
||||
}
|
||||
}
|
||||
Some(new_idx) => {
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
workspace.swap_containers(current_idx, new_idx);
|
||||
workspace.swap_containers(origin_container_idx, new_idx);
|
||||
workspace.focus_container(new_idx);
|
||||
}
|
||||
}
|
||||
@@ -1769,6 +1818,22 @@ impl WindowManager {
|
||||
.ok_or_else(|| anyhow!("there is no workspace"))
|
||||
}
|
||||
|
||||
pub fn focused_workspace_for_monitor_idx(&self, idx: usize) -> Result<&Workspace> {
|
||||
self.monitors()
|
||||
.get(idx)
|
||||
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
|
||||
.focused_workspace()
|
||||
.ok_or_else(|| anyhow!("there is no workspace"))
|
||||
}
|
||||
|
||||
pub fn focused_workspace_for_monitor_idx_mut(&mut self, idx: usize) -> Result<&mut Workspace> {
|
||||
self.monitors_mut()
|
||||
.get_mut(idx)
|
||||
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
|
||||
.focused_workspace_mut()
|
||||
.ok_or_else(|| anyhow!("there is no workspace"))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn focus_workspace(&mut self, idx: usize) -> Result<()> {
|
||||
tracing::info!("focusing workspace");
|
||||
|
||||
@@ -412,12 +412,11 @@ impl Workspace {
|
||||
self.focus_last_container();
|
||||
}
|
||||
|
||||
pub fn insert_container(&mut self, container: Container, idx: usize) {
|
||||
pub fn insert_container_at_idx(&mut self, idx: usize, container: Container) {
|
||||
self.containers_mut().insert(idx, container);
|
||||
self.focus_container(idx);
|
||||
}
|
||||
|
||||
fn remove_container_by_idx(&mut self, idx: usize) -> Option<Container> {
|
||||
pub fn remove_container_by_idx(&mut self, idx: usize) -> Option<Container> {
|
||||
if idx < self.resize_dimensions().len() {
|
||||
self.resize_dimensions_mut().remove(idx);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user