feat(wm): promote containers by swapping indices

This commit adds a new SocketMessage variant PromoteSwap and
corresponding komorebic promote-swap command.

PromoteSwap will also promote the focused window container to the
largest tile in the layout, but instead of removing the focused
container x from the Ring and inserting it into position 0, possibly
changing the positions of other windows between 0 and x, the indices x
and 0 in the Ring will be swapped directly.
This commit is contained in:
LGUG2Z
2025-11-04 08:02:05 -08:00
parent d0ae92ca3a
commit 416dd94670
7 changed files with 47 additions and 2 deletions

12
docs/cli/promote-swap.md Normal file
View File

@@ -0,0 +1,12 @@
# promote-swap
```
Promote the focused window to the largest tile by swapping container indices with the largest tile
Usage: komorebic.exe promote-swap
Options:
-h, --help
Print help
```

View File

@@ -1,7 +1,7 @@
# promote
```
Promote the focused window to the top of the tree
Promote the focused window to the largest tile via container removal and re-insertion
Usage: komorebic.exe promote

View File

@@ -89,6 +89,7 @@ pub enum SocketMessage {
Close,
Minimize,
Promote,
PromoteSwap,
PromoteFocus,
PromoteWindow(OperationDirection),
EagerFocus(String),

View File

@@ -213,6 +213,7 @@ impl WindowManager {
let mut force_update_borders = false;
match message {
SocketMessage::Promote => self.promote_container_to_front()?,
SocketMessage::PromoteSwap => self.promote_container_swap()?,
SocketMessage::PromoteFocus => self.promote_focus_to_front()?,
SocketMessage::PromoteWindow(direction) => {
self.focus_container_in_direction(direction)?;

View File

@@ -2892,6 +2892,31 @@ impl WindowManager {
self.update_focused_workspace(self.mouse_follows_focus, true)
}
#[tracing::instrument(skip(self))]
pub fn promote_container_swap(&mut self) -> eyre::Result<()> {
self.handle_unmanaged_window_behaviour()?;
let workspace = self.focused_workspace_mut()?;
let focused_container_idx = workspace.focused_container_idx();
let primary_idx = match &workspace.layout {
Layout::Default(_) => 0,
Layout::Custom(layout) => layout.first_container_idx(
layout
.primary_idx()
.ok_or_eyre("this custom layout does not have a primary column")?,
),
};
if matches!(workspace.layout, Layout::Default(DefaultLayout::Grid)) {
tracing::debug!("ignoring promote-swap command for grid layout");
return Ok(());
}
workspace.swap_containers(focused_container_idx, primary_idx);
self.update_focused_workspace(self.mouse_follows_focus, true)
}
#[tracing::instrument(skip(self))]
pub fn promote_focus_to_front(&mut self) -> eyre::Result<()> {
self.handle_unmanaged_window_behaviour()?;

View File

@@ -1267,8 +1267,10 @@ enum SubCommand {
/// Flip the layout on the focused workspace
#[clap(arg_required_else_help = true)]
FlipLayout(FlipLayout),
/// Promote the focused window to the top of the tree
/// Promote the focused window to the largest tile via container removal and re-insertion
Promote,
/// Promote the focused window to the largest tile by swapping container indices with the largest tile
PromoteSwap,
/// Promote the user focus to the top of the tree
PromoteFocus,
/// Promote the window in the specified direction
@@ -2029,6 +2031,9 @@ fn main() -> eyre::Result<()> {
SubCommand::Promote => {
send_message(&SocketMessage::Promote)?;
}
SubCommand::PromoteSwap => {
send_message(&SocketMessage::PromoteSwap)?;
}
SubCommand::PromoteFocus => {
send_message(&SocketMessage::PromoteFocus)?;
}

View File

@@ -172,6 +172,7 @@ nav:
- cli/scrolling-layout-columns.md
- cli/flip-layout.md
- cli/promote.md
- cli/promote-swap.md
- cli/promote-focus.md
- cli/promote-window.md
- cli/retile.md