feat(wm): padding for named workspaces

workspace-padding and container-padding take the index of a workspace's monitor
and its own index, so a script that names its workspaces still has to count them
to pad one, while focus-named-workspace, named-workspace-layout and
named-workspace-tiling already take the name.

named-workspace-padding and named-workspace-container-padding take the name
instead. They look the workspace up on every monitor the way the other named
commands do, set the same padding the commands by index set, and change nothing
for a name no workspace has.

The tests check the messages the subcommands send, that a named workspace takes
its paddings and the tile in front follows, and that a name nobody has changes
nothing. The commands sweep of the VM suite sends both commands to a real
session.
This commit is contained in:
LGUG2Z committed 2026-10-28 07:20:37 -07:00
1 parent 189aeb1005
commit ac265de1e3
10 files changed
+198 -15

No files matched your search

@@ -0,0 +1,17 @@
```text
Set the container padding for the specified workspace
Usage: named-workspace-container-padding <WORKSPACE> <SIZE>
Arguments:
<WORKSPACE>
Target workspace name
<SIZE>
Pixels to pad with as an integer
Options:
-h, --help
Print help
```
+17
View File
@@ -0,0 +1,17 @@
```text
Set the workspace padding for the specified workspace
Usage: named-workspace-padding <WORKSPACE> <SIZE>
Arguments:
<WORKSPACE>
Target workspace name
<SIZE>
Pixels to pad with as an integer
Options:
-h, --help
Print help
```
+9 -7
View File
@@ -405,13 +405,15 @@ focused is in no container of the focused workspace
([`WindowManager::handle_unmanaged_window_behaviour`]).
`WorkspacePadding`, `ContainerPadding` and their `FocusedWorkspace` forms set a workspace's own
padding, and `AdjustWorkspacePadding` and `AdjustContainerPadding` change the focused
workspace's by a number of pixels, which needs a padding of its own to change
([`WindowManager::adjust_workspace_padding`]). `WorkAreaOffset`, `MonitorWorkAreaOffset` and
`WorkspaceWorkAreaOffset` set the offset of the window manager, of a monitor or of a workspace,
and `ToggleWindowBasedWorkAreaOffset` sets whether the focused workspace takes its monitor's
window-based offset ([`Workspace::apply_window_based_work_area_offset`]). Each tiles the
workspace in front again, or every monitor.
padding, and `NamedWorkspacePadding` and `NamedWorkspaceContainerPadding` do the same for the
workspace with a name, which they look up like `NamedWorkspaceLayout` does
([`WindowManager::monitor_workspace_index_by_name`]). `AdjustWorkspacePadding` and
`AdjustContainerPadding` change the focused workspace's by a number of pixels, which needs a padding
of its own to change ([`WindowManager::adjust_workspace_padding`]). `WorkAreaOffset`,
`MonitorWorkAreaOffset` and `WorkspaceWorkAreaOffset` set the offset of the window manager, of a
monitor or of a workspace, and `ToggleWindowBasedWorkAreaOffset` sets whether the focused workspace
takes its monitor's window-based offset ([`Workspace::apply_window_based_work_area_offset`]). Each
tiles the workspace in front again, or every monitor.
`ResizeWindowEdge` and `ResizeWindowAxis` resize the focused container
([`WindowManager::resize_window`]). A resize is an adjustment of the container's tile, kept in
+7
View File
@@ -21,11 +21,18 @@ set the paddings of the focused workspace, and
[`komorebic container-padding`](../cli/container-padding.md) those of a workspace given by the
index of its monitor and its own, counted from 0.
[`komorebic named-workspace-padding`](../cli/named-workspace-padding.md) and
[`komorebic named-workspace-container-padding`](../cli/named-workspace-container-padding.md) find
the workspace by the [name](workspaces.md#names) it was given instead, on whichever monitor has
it. A name no workspace has changes nothing.
```sh
komorebic focused-workspace-padding 20
komorebic focused-workspace-container-padding 10
komorebic workspace-padding 0 1 0
komorebic container-padding 0 1 0
komorebic named-workspace-padding mail 0
komorebic named-workspace-container-padding mail 0
```
[`komorebic adjust-workspace-padding`](../cli/adjust-workspace-padding.md) and
+2
View File
@@ -419,8 +419,10 @@ pub enum SocketMessage {
FocusMonitorWorkspaceNumber(usize, usize),
FocusNamedWorkspace(String),
ContainerPadding(usize, usize, i32),
NamedWorkspaceContainerPadding(String, i32),
FocusedWorkspaceContainerPadding(i32),
WorkspacePadding(usize, usize, i32),
NamedWorkspacePadding(String, i32),
FocusedWorkspacePadding(i32),
WorkspaceTiling(usize, usize, bool),
NamedWorkspaceTiling(String, bool),
+20 -4
View File
@@ -53,10 +53,12 @@
//! workspace, which komorebi does not move, or puts a floating window back
//! ([`WindowManager::toggle_float`]).
//! - **WorkspacePadding** and **ContainerPadding** set the paddings of a workspace given by the
//! index of its monitor and its own, **FocusedWorkspacePadding** and
//! **FocusedWorkspaceContainerPadding** those of the focused workspace, and
//! **AdjustWorkspacePadding** and **AdjustContainerPadding** make the focused workspace's larger
//! or smaller ([`WindowManager::set_workspace_padding`],
//! index of its monitor and its own, **NamedWorkspacePadding** and
//! **NamedWorkspaceContainerPadding** those of the workspace with a name
//! ([`WindowManager::monitor_workspace_index_by_name`]; a name no workspace has changes
//! nothing), **FocusedWorkspacePadding** and **FocusedWorkspaceContainerPadding** those of the
//! focused workspace, and **AdjustWorkspacePadding** and **AdjustContainerPadding** make the
//! focused workspace's larger or smaller ([`WindowManager::set_workspace_padding`],
//! [`WindowManager::set_container_padding`], [`WindowManager::adjust_workspace_padding`],
//! [`WindowManager::adjust_container_padding`]).
//! - **WorkAreaOffset**, **MonitorWorkAreaOffset** and **WorkspaceWorkAreaOffset** set the work
@@ -1140,9 +1142,23 @@ impl WindowManager {
SocketMessage::ContainerPadding(monitor_idx, workspace_idx, size) => {
self.set_container_padding(monitor_idx, workspace_idx, size)?;
}
SocketMessage::NamedWorkspaceContainerPadding(ref workspace, size) => {
if let Some((monitor_idx, workspace_idx)) =
self.monitor_workspace_index_by_name(workspace)
{
self.set_container_padding(monitor_idx, workspace_idx, size)?;
}
}
SocketMessage::WorkspacePadding(monitor_idx, workspace_idx, size) => {
self.set_workspace_padding(monitor_idx, workspace_idx, size)?;
}
SocketMessage::NamedWorkspacePadding(ref workspace, size) => {
if let Some((monitor_idx, workspace_idx)) =
self.monitor_workspace_index_by_name(workspace)
{
self.set_workspace_padding(monitor_idx, workspace_idx, size)?;
}
}
SocketMessage::FocusedWorkspaceContainerPadding(adjustment) => {
let focused_monitor_idx = self.focused_monitor_idx();
+68
View File
@@ -88,6 +88,17 @@ fn one_window_without_padding(id: u32) -> (Arc<FakeBackend>, WindowManager) {
(fake, wm)
}
/// The workspace padding and the container padding each workspace of the focused monitor has of
/// its own
fn paddings(wm: &WindowManager) -> Vec<(Option<i32>, Option<i32>)> {
wm.focused_monitor()
.unwrap()
.workspaces()
.iter()
.map(|workspace| (workspace.workspace_padding, workspace.container_padding))
.collect()
}
#[test]
fn the_paddings_of_the_focused_workspace_are_set_and_adjusted() {
let (fake, mut wm) = desktop_with(&[28_001]);
@@ -156,6 +167,63 @@ fn the_paddings_of_a_workspace_are_set_by_its_indices() {
);
}
#[test]
fn the_paddings_of_a_workspace_are_set_by_its_name_and_a_name_nobody_has_changes_nothing() {
let (fake, mut wm) = desktop_with(&[28_041]);
send(
&fake,
&mut wm,
SocketMessage::EnsureNamedWorkspaces(0, vec![String::from("main"), String::from("side")]),
)
.unwrap();
// the workspace in front is tiled again at once
send(
&fake,
&mut wm,
SocketMessage::NamedWorkspacePadding(String::from("main"), 20),
)
.unwrap();
assert_eq!(fake.rect_of(28_041), Some(rect(25, 25, 1870, 1000)));
send(
&fake,
&mut wm,
SocketMessage::NamedWorkspaceContainerPadding(String::from("main"), 0),
)
.unwrap();
assert_eq!(fake.rect_of(28_041), Some(rect(20, 20, 1880, 1010)));
// another workspace takes its own paddings, and the one in front keeps its tile
send(
&fake,
&mut wm,
SocketMessage::NamedWorkspacePadding(String::from("side"), 7),
)
.unwrap();
send(
&fake,
&mut wm,
SocketMessage::NamedWorkspaceContainerPadding(String::from("side"), 3),
)
.unwrap();
assert_eq!(fake.rect_of(28_041), Some(rect(20, 20, 1880, 1010)));
assert_eq!(paddings(&wm), [(Some(20), Some(0)), (Some(7), Some(3))]);
send(
&fake,
&mut wm,
SocketMessage::NamedWorkspacePadding(String::from("nowhere"), 99),
)
.unwrap();
send(
&fake,
&mut wm,
SocketMessage::NamedWorkspaceContainerPadding(String::from("nowhere"), 99),
)
.unwrap();
assert_eq!(paddings(&wm), [(Some(20), Some(0)), (Some(7), Some(3))]);
}
#[test]
fn the_workspaces_offset_wins_over_the_monitors_which_wins_over_the_global_one() {
let (fake, mut wm) = one_window_without_padding(28_021);
+44 -4
View File
@@ -47,10 +47,11 @@
//! `cross-monitor-move-behaviour` and `toggle-cross-monitor-move-behaviour` set what `move` does
//! at the edge of a monitor, and `display-index-preference` gives a monitor an index.
//! `eager-focus` focuses the first window of an executable, on any workspace of any monitor.
//! - `workspace-name` names a workspace, which `focus-named-workspace`, `named-workspace-layout`
//! and `named-workspace-tiling` find by its name; `workspace-layout` and `workspace-tiling` set
//! the layout and the tiling of a workspace by its index, and `ensure-workspaces` and
//! `ensure-named-workspaces` give a monitor workspaces up to a number, or with names.
//! - `workspace-name` names a workspace, which `focus-named-workspace`, `named-workspace-layout`,
//! `named-workspace-tiling`, `named-workspace-padding` and `named-workspace-container-padding`
//! find by its name; `workspace-layout` and `workspace-tiling` set the layout and the tiling of
//! a workspace by its index, and `ensure-workspaces` and `ensure-named-workspaces` give a
//! monitor workspaces up to a number, or with names.
//!
//! `ignore-rule` and `manage-rule` add a rule that keeps an application out of the layout or
//! always in it, `session-float-rule` floats the focused window and every window like it until the
@@ -297,6 +298,27 @@ gen_padding_subcommand_args! {
WorkspacePadding,
}
macro_rules! gen_named_padding_subcommand_args {
// SubCommand Pattern
( $( $name:ident ),+ $(,)? ) => {
$(
#[derive(clap::Parser)]
pub struct $name {
/// Target workspace name
workspace: String,
/// Pixels to pad with as an integer
size: i32,
}
)+
};
}
gen_named_padding_subcommand_args! {
NamedWorkspaceContainerPadding,
NamedWorkspacePadding,
}
macro_rules! gen_padding_adjustment_subcommand_args {
// SubCommand Pattern
( $( $name:ident ),+ $(,)? ) => {
@@ -773,9 +795,15 @@ enum SubCommand {
/// Set the container padding for the specified workspace
#[clap(arg_required_else_help = true)]
ContainerPadding(ContainerPadding),
/// Set the container padding for the specified workspace
#[clap(arg_required_else_help = true)]
NamedWorkspaceContainerPadding(NamedWorkspaceContainerPadding),
/// Set the workspace padding for the specified workspace
#[clap(arg_required_else_help = true)]
WorkspacePadding(WorkspacePadding),
/// Set the workspace padding for the specified workspace
#[clap(arg_required_else_help = true)]
NamedWorkspacePadding(NamedWorkspacePadding),
/// Set the layout for the specified workspace
#[clap(arg_required_else_help = true)]
WorkspaceLayout(WorkspaceLayout),
@@ -1223,6 +1251,12 @@ fn main() -> eyre::Result<()> {
args.size,
))?;
}
SubCommand::NamedWorkspaceContainerPadding(args) => {
send_message(&SocketMessage::NamedWorkspaceContainerPadding(
args.workspace,
args.size,
))?;
}
SubCommand::WorkspacePadding(args) => {
send_message(&SocketMessage::WorkspacePadding(
args.monitor,
@@ -1230,6 +1264,12 @@ fn main() -> eyre::Result<()> {
args.size,
))?;
}
SubCommand::NamedWorkspacePadding(args) => {
send_message(&SocketMessage::NamedWorkspacePadding(
args.workspace,
args.size,
))?;
}
SubCommand::FocusedWorkspacePadding(args) => {
send_message(&SocketMessage::FocusedWorkspacePadding(args.size))?;
}
+10
View File
@@ -315,6 +315,14 @@ fn the_padding_subcommands_send_the_workspace_and_the_pixels_they_are_given() {
&["container-padding", "1", "0", "8"],
r#"{"type":"ContainerPadding","content":[1,0,8]}"#,
);
sends(
&["named-workspace-padding", "mail", "12"],
r#"{"type":"NamedWorkspacePadding","content":["mail",12]}"#,
);
sends(
&["named-workspace-container-padding", "mail", "8"],
r#"{"type":"NamedWorkspaceContainerPadding","content":["mail",8]}"#,
);
sends(
&["adjust-workspace-padding", "increase", "5"],
r#"{"type":"AdjustWorkspacePadding","content":["Increase",5]}"#,
@@ -689,7 +697,9 @@ fn docgen_writes_the_help_of_every_subcommand_in_a_text_fence() {
"move-to-workspace.md",
"move-workspace-to-monitor.md",
"move.md",
"named-workspace-container-padding.md",
"named-workspace-layout.md",
"named-workspace-padding.md",
"named-workspace-tiling.md",
"new-workspace.md",
"preselect-direction.md",
+4
View File
@@ -278,6 +278,10 @@ container-padding 0 0 12
container-padding 0 0 10
workspace-padding 0 0 12
workspace-padding 0 0 10
named-workspace-container-padding one 12
named-workspace-container-padding one 10
named-workspace-padding one 12
named-workspace-padding one 10
cross-monitor-move-behaviour insert
cross-monitor-move-behaviour swap
toggle-cross-monitor-move-behaviour