feat(wm): add cmd to specify work area offsets

This commit adds a new komorebic command to specify offsets for work
areas to be applied across all monitors. The areas covered by these
offsets will be excluded from the tiling area, and can be used for
custom task bars, Rainmeter desktop widgets etc.

When setting an offset at the top, the same offset will need to be
applied to the bottom to ensure that the tiling area is not pushed off
of the screen, but this is not necessary when applying an offset to the
bottom as the top of the work area will never go lower than 0.

resolve #46
This commit is contained in:
LGUG2Z
2021-10-12 08:38:35 -07:00
parent ddafe599a2
commit 65bc1a966e
10 changed files with 81 additions and 12 deletions

4
Cargo.lock generated
View File

@@ -1172,9 +1172,9 @@ dependencies = [
[[package]]
name = "sysinfo"
version = "0.20.4"
version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffff4a02fa61eee51f95210fc9c98ea6eeb46bb071adeafd61e1a0b9b22c6a6d"
checksum = "e223c65cd36b485a34c2ce6e38efa40777d31c4166d9076030c74cdcf971679f"
dependencies = [
"cfg-if 1.0.0",
"core-foundation-sys",

View File

@@ -242,6 +242,7 @@ cycle-monitor Focus the monitor in the given cycle direction
cycle-workspace Focus the workspace in the given cycle direction
new-workspace Create and append a new workspace on the focused monitor
invisible-borders Set the invisible border dimensions around each window
work-area-offset Set offsets to exclude parts of the work area from tiling
adjust-container-padding Adjust container padding on the focused workspace
adjust-workspace-padding Adjust workspace padding on the focused workspace
change-layout Set the layout on the focused workspace
@@ -313,6 +314,7 @@ used [is available here](komorebi.sample.with.lib.ahk).
- [x] Additional manage rules based on exe name and window class
- [x] Identify applications which overflow their borders by exe name and class
- [x] Identify 'close/minimize to tray' applications by exe name and class
- [x] Configure work area offsets to preserve space for custom taskbars
- [x] Configure and compensate for the size of Windows 10's invisible borders
- [x] Toggle floating windows
- [x] Toggle monocle window

View File

@@ -72,6 +72,7 @@ pub enum SocketMessage {
ReloadConfiguration,
WatchConfiguration(bool),
InvisibleBorders(Rect),
WorkAreaOffset(Rect),
WorkspaceRule(ApplicationIdentifier, String, usize, usize),
FloatRule(ApplicationIdentifier, String),
ManageRule(ApplicationIdentifier, String),

View File

@@ -146,12 +146,16 @@ impl Monitor {
self.workspaces().len()
}
pub fn update_focused_workspace(&mut self, invisible_borders: &Rect) -> Result<()> {
pub fn update_focused_workspace(
&mut self,
offset: Option<Rect>,
invisible_borders: &Rect,
) -> Result<()> {
let work_area = *self.work_area_size();
self.focused_workspace_mut()
.ok_or_else(|| anyhow!("there is no workspace"))?
.update(&work_area, invisible_borders)?;
.update(&work_area, offset, invisible_borders)?;
Ok(())
}

View File

@@ -371,6 +371,10 @@ impl WindowManager {
self.invisible_borders = rect;
self.retile_all()?;
}
SocketMessage::WorkAreaOffset(rect) => {
self.work_area_offset = Option::from(rect);
self.retile_all()?;
}
SocketMessage::QuickSave => {
let workspace = self.focused_workspace()?;
let resize = workspace.resize_dimensions();

View File

@@ -66,13 +66,14 @@ impl WindowManager {
}
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
for (i, monitor) in self.monitors_mut().iter_mut().enumerate() {
let work_area = *monitor.work_area_size();
for (j, workspace) in monitor.workspaces_mut().iter_mut().enumerate() {
let reaped_orphans = workspace.reap_orphans()?;
if reaped_orphans.0 > 0 || reaped_orphans.1 > 0 {
workspace.update(&work_area, &invisible_borders)?;
workspace.update(&work_area, offset, &invisible_borders)?;
tracing::info!(
"reaped {} orphan window(s) and {} orphaned container(s) on monitor: {}, workspace: {}",
reaped_orphans.0,

View File

@@ -45,6 +45,7 @@ pub struct WindowManager {
pub command_listener: UnixListener,
pub is_paused: bool,
pub invisible_borders: Rect,
pub work_area_offset: Option<Rect>,
pub focus_follows_mouse: Option<FocusFollowsMouseImplementation>,
pub hotwatch: Hotwatch,
pub virtual_desktop_id: Option<usize>,
@@ -56,6 +57,7 @@ pub struct State {
pub monitors: Ring<Monitor>,
pub is_paused: bool,
pub invisible_borders: Rect,
pub work_area_offset: Option<Rect>,
pub focus_follows_mouse: Option<FocusFollowsMouseImplementation>,
pub has_pending_raise_op: bool,
pub float_identifiers: Vec<String>,
@@ -72,6 +74,7 @@ impl From<&mut WindowManager> for State {
monitors: wm.monitors.clone(),
is_paused: wm.is_paused,
invisible_borders: wm.invisible_borders,
work_area_offset: wm.work_area_offset,
focus_follows_mouse: wm.focus_follows_mouse.clone(),
has_pending_raise_op: wm.has_pending_raise_op,
float_identifiers: FLOAT_IDENTIFIERS.lock().clone(),
@@ -143,6 +146,7 @@ impl WindowManager {
right: 14,
bottom: 7,
},
work_area_offset: None,
focus_follows_mouse: None,
hotwatch: Hotwatch::new()?,
virtual_desktop_id,
@@ -265,6 +269,7 @@ impl WindowManager {
self.monitors_mut().retain(|m| !invalid.contains(&m.id()));
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
for monitor in self.monitors_mut() {
let mut should_update = false;
@@ -293,7 +298,7 @@ impl WindowManager {
}
if should_update {
monitor.update_focused_workspace(&invisible_borders)?;
monitor.update_focused_workspace(offset, &invisible_borders)?;
}
}
@@ -423,6 +428,8 @@ impl WindowManager {
#[tracing::instrument(skip(self))]
pub fn retile_all(&mut self) -> Result<()> {
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
for monitor in self.monitors_mut() {
let work_area = *monitor.work_area_size();
let workspace = monitor
@@ -434,7 +441,7 @@ impl WindowManager {
*resize = None;
}
workspace.update(&work_area, &invisible_borders)?;
workspace.update(&work_area, offset, &invisible_borders)?;
}
Ok(())
@@ -534,10 +541,11 @@ impl WindowManager {
tracing::info!("updating");
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
self.focused_monitor_mut()
.ok_or_else(|| anyhow!("there is no monitor"))?
.update_focused_workspace(&invisible_borders)?;
.update_focused_workspace(offset, &invisible_borders)?;
if mouse_follows_focus {
if let Some(window) = self.focused_workspace()?.maximized_window() {
@@ -660,6 +668,7 @@ impl WindowManager {
tracing::info!("moving container");
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
let monitor = self
.focused_monitor_mut()
@@ -685,7 +694,7 @@ impl WindowManager {
target_monitor.add_container(container)?;
target_monitor.load_focused_workspace()?;
target_monitor.update_focused_workspace(&invisible_borders)?;
target_monitor.update_focused_workspace(offset, &invisible_borders)?;
if follow {
self.focus_monitor(idx)?;
@@ -1075,6 +1084,7 @@ impl WindowManager {
tracing::info!("setting workspace layout");
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
let focused_monitor_idx = self.focused_monitor_idx();
let monitor = self
@@ -1094,7 +1104,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, &invisible_borders)?;
workspace.update(&work_area, offset, &invisible_borders)?;
Ok(())
} else {
Ok(self.update_focused_workspace(false)?)

View File

@@ -138,8 +138,25 @@ impl Workspace {
Ok(())
}
pub fn update(&mut self, work_area: &Rect, invisible_borders: &Rect) -> Result<()> {
let mut adjusted_work_area = *work_area;
pub fn update(
&mut self,
work_area: &Rect,
offset: Option<Rect>,
invisible_borders: &Rect,
) -> Result<()> {
let mut adjusted_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());
self.enforce_resize_constraints();

View File

@@ -108,6 +108,10 @@ InvisibleBorders(left, top, right, bottom) {
Run, komorebic.exe invisible-borders %left% %top% %right% %bottom%, , Hide
}
WorkAreaOffset(left, top, right, bottom) {
Run, komorebic.exe work-area-offset %left% %top% %right% %bottom%, , Hide
}
AdjustContainerPadding(sizing, adjustment) {
Run, komorebic.exe adjust-container-padding %sizing% %adjustment%, , Hide
}

View File

@@ -167,6 +167,18 @@ struct InvisibleBorders {
bottom: i32,
}
#[derive(Clap, AhkFunction)]
struct WorkAreaOffset {
/// Size of the left work area offset (set right to left * 2 to maintain right padding)
left: i32,
/// Size of the top work area offset (set bottom to the same value to maintain bottom padding)
top: i32,
/// Size of the right work area offset
right: i32,
/// Size of the bottom work area offset
bottom: i32,
}
#[derive(Clap, AhkFunction)]
struct EnsureWorkspaces {
/// Monitor index (zero-indexed)
@@ -366,6 +378,9 @@ enum SubCommand {
/// Set the invisible border dimensions around each window
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
InvisibleBorders(InvisibleBorders),
/// Set offsets to exclude parts of the work area from tiling
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
WorkAreaOffset(WorkAreaOffset),
/// Adjust container padding on the focused workspace
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
AdjustContainerPadding(AdjustContainerPadding),
@@ -538,6 +553,17 @@ fn main() -> Result<()> {
.as_bytes()?,
)?;
}
SubCommand::WorkAreaOffset(arg) => {
send_message(
&*SocketMessage::WorkAreaOffset(Rect {
left: arg.left,
top: arg.top,
right: arg.right,
bottom: arg.bottom,
})
.as_bytes()?,
)?;
}
SubCommand::ContainerPadding(arg) => {
send_message(
&*SocketMessage::ContainerPadding(arg.monitor, arg.workspace, arg.size)