Skip to content

Commit

Permalink
feat(wm): move/focus across monitor edges
Browse files Browse the repository at this point in the history
This commit introduces the ability to operate across monitor boundaries
with the 'move' and 'focus' commands.

When operating down or to the right, the target index of the monitor in
that direction will be 0. When operating up or to the left, the target
index will either be len() - 1 if focusing, or len() if moving.

re #145
  • Loading branch information
LGUG2Z committed Jun 27, 2022
1 parent 4576078 commit 336a4e3
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 12 deletions.
108 changes: 96 additions & 12 deletions komorebi/src/window_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,39 @@ impl WindowManager {
Ok(())
}

pub fn monitor_index_in_direction(&self, direction: OperationDirection) -> Option<usize> {
let current_monitor_size = self.focused_monitor_size().ok()?;

for (idx, monitor) in self.monitors.elements().iter().enumerate() {
match direction {
OperationDirection::Left => {
if monitor.size().left + monitor.size().right == current_monitor_size.left {
return Option::from(idx);
}
}
OperationDirection::Right => {
if current_monitor_size.right + current_monitor_size.left == monitor.size().left
{
return Option::from(idx);
}
}
OperationDirection::Up => {
if monitor.size().top + monitor.size().bottom == current_monitor_size.top {
return Option::from(idx);
}
}
OperationDirection::Down => {
if current_monitor_size.top + current_monitor_size.bottom == monitor.size().top
{
return Option::from(idx);
}
}
}
}

None
}

#[tracing::instrument(skip(self))]
pub fn reconcile_monitors(&mut self) -> Result<()> {
let valid_hmonitors = WindowsApi::valid_hmonitors()?;
Expand Down Expand Up @@ -903,13 +936,24 @@ impl WindowManager {

tracing::info!("focusing container");

let workspace = self.focused_workspace_mut()?;
let workspace = self.focused_workspace()?;
let new_idx = workspace.new_idx_for_direction(direction);

let new_idx = workspace
.new_idx_for_direction(direction)
.ok_or_else(|| anyhow!("this is not a valid direction from the current position"))?;
// if there is no container in that direction for this workspace
if new_idx.is_none() {
// check if there is a monitor in that direction
let monitor_idx = self
.monitor_index_in_direction(direction)
.ok_or_else(|| anyhow!("there is no container or monitor in this direction"))?;
self.focus_monitor(monitor_idx)?;
}

let workspace = self.focused_workspace_mut()?;
workspace.focus_container(new_idx.unwrap_or_else(|| match direction {
OperationDirection::Right | OperationDirection::Down => 0,
OperationDirection::Left | OperationDirection::Up => workspace.containers().len() - 1,
}));

workspace.focus_container(new_idx);
self.focused_window_mut()?.focus(self.mouse_follows_focus)?;

Ok(())
Expand All @@ -921,15 +965,48 @@ impl WindowManager {

tracing::info!("moving container");

let workspace = self.focused_workspace_mut()?;
let workspace = self.focused_workspace()?;

let current_idx = workspace.focused_container_idx();
let new_idx = workspace
.new_idx_for_direction(direction)
.ok_or_else(|| anyhow!("this is not a valid direction from the current position"))?;
let new_idx = workspace.new_idx_for_direction(direction);

match new_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
.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()?;

// 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")
})?;

// decide where to reinsert it before redrawing
let final_idx = match direction {
OperationDirection::Right | OperationDirection::Down => 0,
OperationDirection::Left | OperationDirection::Up => {
workspace.containers().len()
}
};

// insert it in the right place on the target monitor's workspace
workspace.insert_container(container, final_idx);
}
Some(new_idx) => {
let workspace = self.focused_workspace_mut()?;
workspace.swap_containers(current_idx, new_idx);
workspace.focus_container(new_idx);
}
}

workspace.swap_containers(current_idx, new_idx);
workspace.focus_container(new_idx);
self.update_focused_workspace(self.mouse_follows_focus)
}

Expand Down Expand Up @@ -1627,6 +1704,13 @@ impl WindowManager {
self.update_focused_workspace(false)
}

pub fn focused_monitor_size(&self) -> Result<Rect> {
Ok(*self
.focused_monitor()
.ok_or_else(|| anyhow!("there is no monitor"))?
.size())
}

pub fn focused_monitor_work_area(&self) -> Result<Rect> {
Ok(*self
.focused_monitor()
Expand Down Expand Up @@ -1659,7 +1743,7 @@ impl WindowManager {
None
}

pub fn monitor_idx_from_current_pos(&mut self) -> Option<usize> {
pub fn monitor_idx_from_current_pos(&self) -> Option<usize> {
let hmonitor = WindowsApi::monitor_from_point(WindowsApi::cursor_pos().ok()?);

for (i, monitor) in self.monitors().iter().enumerate() {
Expand Down
5 changes: 5 additions & 0 deletions komorebi/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,11 @@ impl Workspace {
self.focus_last_container();
}

pub fn insert_container(&mut self, container: Container, idx: usize) {
self.containers_mut().insert(idx, container);
self.focus_container(idx);
}

fn remove_container_by_idx(&mut self, idx: usize) -> Option<Container> {
if idx < self.resize_dimensions().len() {
self.resize_dimensions_mut().remove(idx);
Expand Down

0 comments on commit 336a4e3

Please sign in to comment.