From 0330dfe25034bb6a00db15f3cb40d0b632972e0e Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Mon, 13 May 2024 10:07:42 -0700 Subject: [PATCH] feat(wm): add single window work area offsets This commit adds a new monitor configuration option, single_window_work_area_offset, which will apply to a monitor when only a single window is open on a workspace. This is implemented as a Rect to enable its use on both horizontally and vertically positioned monitors. This option will be particularly useful for ultrawide monitor users, where a single window taking up the full width of the work area can often hinder usability. resolve #434 --- komorebi/src/monitor.rs | 6 +++++- komorebi/src/process_event.rs | 3 ++- komorebi/src/static_config.rs | 6 ++++++ komorebi/src/window_manager.rs | 18 ++++++++++++------ komorebi/src/workspace.rs | 24 ++++++++++++++++++++++-- 5 files changed, 47 insertions(+), 10 deletions(-) diff --git a/komorebi/src/monitor.rs b/komorebi/src/monitor.rs index fb73f6916..820b6574b 100644 --- a/komorebi/src/monitor.rs +++ b/komorebi/src/monitor.rs @@ -36,6 +36,8 @@ pub struct Monitor { work_area_size: Rect, #[getset(get_copy = "pub", set = "pub")] work_area_offset: Option, + #[getset(get_copy = "pub", set = "pub")] + single_window_work_area_offset: Option, workspaces: Ring, #[serde(skip_serializing_if = "Option::is_none")] #[getset(get_copy = "pub", set = "pub")] @@ -58,6 +60,7 @@ pub fn new(id: isize, size: Rect, work_area_size: Rect, name: String) -> Monitor size, work_area_size, work_area_offset: None, + single_window_work_area_offset: None, workspaces, last_focused_workspace: None, workspace_names: HashMap::default(), @@ -194,6 +197,7 @@ impl Monitor { pub fn update_focused_workspace(&mut self, offset: Option) -> Result<()> { let work_area = *self.work_area_size(); + let single_window_work_area_offset = self.single_window_work_area_offset(); let offset = if self.work_area_offset().is_some() { self.work_area_offset() } else { @@ -202,7 +206,7 @@ impl Monitor { self.focused_workspace_mut() .ok_or_else(|| anyhow!("there is no workspace"))? - .update(&work_area, offset)?; + .update(&work_area, offset, single_window_work_area_offset)?; Ok(()) } diff --git a/komorebi/src/process_event.rs b/komorebi/src/process_event.rs index 2abe4c531..f5e65c912 100644 --- a/komorebi/src/process_event.rs +++ b/komorebi/src/process_event.rs @@ -126,6 +126,7 @@ impl WindowManager { for (i, monitor) in self.monitors_mut().iter_mut().enumerate() { let work_area = *monitor.work_area_size(); + let single_window_work_area_offset = monitor.single_window_work_area_offset(); let offset = if monitor.work_area_offset().is_some() { monitor.work_area_offset() } else { @@ -139,7 +140,7 @@ impl WindowManager { let reaped_orphans = workspace.reap_orphans()?; if reaped_orphans.0 > 0 || reaped_orphans.1 > 0 { - workspace.update(&work_area, offset)?; + workspace.update(&work_area, offset, single_window_work_area_offset)?; tracing::info!( "reaped {} orphan window(s) and {} orphaned container(s) on monitor: {}, workspace: {}", reaped_orphans.0, diff --git a/komorebi/src/static_config.rs b/komorebi/src/static_config.rs index b718d320f..1a6e76f0d 100644 --- a/komorebi/src/static_config.rs +++ b/komorebi/src/static_config.rs @@ -201,6 +201,9 @@ pub struct MonitorConfig { /// Monitor-specific work area offset (default: None) #[serde(skip_serializing_if = "Option::is_none")] pub work_area_offset: Option, + /// Single window work area offset (default: None) + #[serde(skip_serializing_if = "Option::is_none")] + pub single_window_work_area_offset: Option, } impl From<&Monitor> for MonitorConfig { @@ -213,6 +216,7 @@ impl From<&Monitor> for MonitorConfig { Self { workspaces, work_area_offset: value.work_area_offset(), + single_window_work_area_offset: value.single_window_work_area_offset(), } } } @@ -694,6 +698,7 @@ impl StaticConfig { if let Some(m) = wm.monitors_mut().get_mut(i) { m.ensure_workspace_count(monitor.workspaces.len()); m.set_work_area_offset(monitor.work_area_offset); + m.set_single_window_work_area_offset(monitor.single_window_work_area_offset); for (j, ws) in m.workspaces_mut().iter_mut().enumerate() { ws.load_static_config( @@ -749,6 +754,7 @@ impl StaticConfig { if let Some(m) = wm.monitors_mut().get_mut(i) { m.ensure_workspace_count(monitor.workspaces.len()); m.set_work_area_offset(monitor.work_area_offset); + m.set_single_window_work_area_offset(monitor.single_window_work_area_offset); for (j, ws) in m.workspaces_mut().iter_mut().enumerate() { ws.load_static_config( diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index adc1d35b4..cf9f0e177 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -713,6 +713,7 @@ impl WindowManager { for monitor in self.monitors_mut() { let work_area = *monitor.work_area_size(); + let single_window_work_area_offset = monitor.single_window_work_area_offset(); let offset = if monitor.work_area_offset().is_some() { monitor.work_area_offset() } else { @@ -730,7 +731,7 @@ impl WindowManager { } } - workspace.update(&work_area, offset)?; + workspace.update(&work_area, offset, single_window_work_area_offset)?; } Ok(()) @@ -1943,6 +1944,7 @@ impl WindowManager { .ok_or_else(|| anyhow!("there is no monitor"))?; let work_area = *monitor.work_area_size(); + let single_window_work_area_offset = monitor.single_window_work_area_offset(); let focused_workspace_idx = monitor.focused_workspace_idx(); let offset = if monitor.work_area_offset().is_some() { monitor.work_area_offset() @@ -1962,7 +1964,7 @@ impl WindowManager { // If this is the focused workspace on a non-focused screen, let's update it if focused_monitor_idx != monitor_idx && focused_workspace_idx == workspace_idx { - workspace.update(&work_area, offset)?; + workspace.update(&work_area, offset, single_window_work_area_offset)?; Ok(()) } else { Ok(self.update_focused_workspace(false, false)?) @@ -1991,6 +1993,7 @@ impl WindowManager { .ok_or_else(|| anyhow!("there is no monitor"))?; let work_area = *monitor.work_area_size(); + let single_window_work_area_offset = monitor.single_window_work_area_offset(); let focused_workspace_idx = monitor.focused_workspace_idx(); let offset = if monitor.work_area_offset().is_some() { monitor.work_area_offset() @@ -2012,7 +2015,7 @@ impl WindowManager { // If this is the focused workspace on a non-focused screen, let's update it if focused_monitor_idx != monitor_idx && focused_workspace_idx == workspace_idx { - workspace.update(&work_area, offset)?; + workspace.update(&work_area, offset, single_window_work_area_offset)?; Ok(()) } else { Ok(self.update_focused_workspace(false, false)?) @@ -2036,6 +2039,7 @@ impl WindowManager { .ok_or_else(|| anyhow!("there is no monitor"))?; let work_area = *monitor.work_area_size(); + let single_window_work_area_offset = monitor.single_window_work_area_offset(); let focused_workspace_idx = monitor.focused_workspace_idx(); let offset = if monitor.work_area_offset().is_some() { monitor.work_area_offset() @@ -2053,7 +2057,7 @@ impl WindowManager { // If this is the focused workspace on a non-focused screen, let's update it if focused_monitor_idx != monitor_idx && focused_workspace_idx == workspace_idx { - workspace.update(&work_area, offset)?; + workspace.update(&work_area, offset, single_window_work_area_offset)?; Ok(()) } else { Ok(self.update_focused_workspace(false, false)?) @@ -2078,6 +2082,7 @@ impl WindowManager { .ok_or_else(|| anyhow!("there is no monitor"))?; let work_area = *monitor.work_area_size(); + let single_window_work_area_offset = monitor.single_window_work_area_offset(); let focused_workspace_idx = monitor.focused_workspace_idx(); let offset = if monitor.work_area_offset().is_some() { monitor.work_area_offset() @@ -2094,7 +2099,7 @@ impl WindowManager { // If this is the focused workspace on a non-focused screen, let's update it if focused_monitor_idx != monitor_idx && focused_workspace_idx == workspace_idx { - workspace.update(&work_area, offset)?; + workspace.update(&work_area, offset, single_window_work_area_offset)?; Ok(()) } else { Ok(self.update_focused_workspace(false, false)?) @@ -2122,6 +2127,7 @@ impl WindowManager { .ok_or_else(|| anyhow!("there is no monitor"))?; let work_area = *monitor.work_area_size(); + let single_window_work_area_offset = monitor.single_window_work_area_offset(); let focused_workspace_idx = monitor.focused_workspace_idx(); let offset = if monitor.work_area_offset().is_some() { monitor.work_area_offset() @@ -2139,7 +2145,7 @@ impl WindowManager { // If this is the focused workspace on a non-focused screen, let's update it if focused_monitor_idx != monitor_idx && focused_workspace_idx == workspace_idx { - workspace.update(&work_area, offset)?; + workspace.update(&work_area, offset, single_window_work_area_offset)?; Ok(()) } else { Ok(self.update_focused_workspace(false, false)?) diff --git a/komorebi/src/workspace.rs b/komorebi/src/workspace.rs index df9387e3d..779b9e3be 100644 --- a/komorebi/src/workspace.rs +++ b/komorebi/src/workspace.rs @@ -219,13 +219,18 @@ impl Workspace { Ok(()) } - pub fn update(&mut self, work_area: &Rect, offset: Option) -> Result<()> { + pub fn update( + &mut self, + work_area: &Rect, + work_area_offset: Option, + single_window_work_area_offset: Option, + ) -> Result<()> { if !INITIAL_CONFIGURATION_LOADED.load(Ordering::SeqCst) { return Ok(()); } let container_padding = self.container_padding(); - let mut adjusted_work_area = offset.map_or_else( + let mut adjusted_work_area = work_area_offset.map_or_else( || *work_area, |offset| { let mut with_offset = *work_area; @@ -238,6 +243,21 @@ impl Workspace { }, ); + if self.containers().len() == 1 { + adjusted_work_area = single_window_work_area_offset.map_or_else( + || *work_area, + |offset| { + let mut with_offset = *work_area; + with_offset.left += offset.left; + with_offset.top += offset.top; + with_offset.right -= offset.right; + with_offset.bottom -= offset.bottom; + + with_offset + }, + ); + } + adjusted_work_area.add_padding(self.workspace_padding().unwrap_or_default()); self.enforce_resize_constraints();