Skip to content

Window management

Window management actions change window positions, floating state, and zoom. This page focuses on behavior and examples. For the full action list and defaults, see Actions reference.

Swap actions

Swap the focused window’s position with another window. Focus follows the window being moved.

Directional swap

spoon.Shoji:bindHotkeys({
swap_left = { { "ctrl", "alt", "shift" }, "h" },
swap_down = { { "ctrl", "alt", "shift" }, "j" },
swap_up = { { "ctrl", "alt", "shift" }, "k" },
swap_right = { { "ctrl", "alt", "shift" }, "l" },
})

Example with swap_right (asterisk marks focus):

Before:

┌─────────────────────────────┬─────────────────────────────┐
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ 1* │ 2 │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
└─────────────────────────────┴─────────────────────────────┘

After:

┌─────────────────────────────┬─────────────────────────────┐
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ 2 │ 1* │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
└─────────────────────────────┴─────────────────────────────┘

The focused window trades places with the target. Focus stays with window 1.

Order-based swap

spoon.Shoji:bindHotkeys({
swap_next = { { "ctrl", "alt", "shift" }, "n" },
swap_prev = { { "ctrl", "alt", "shift" }, "p" },
})

Swaps with the adjacent window in stack order. Good for reordering windows without worrying about screen positions. The order wraps around.

Swap with main

spoon.Shoji:bindHotkeys({
swap_main = { { "ctrl", "alt", "shift" }, "return" },
})

Promotes the focused window to the main (first) position:

Before (focus on 3):

┌─────────────────────────────┬─────────────────────────────┐
│ │ │
│ │ │
│ │ │
│ │ │
│ │ 2 │
│ │ │
│ │ │
│ │ │
│ 1 ├─────────────────────────────┤
│ │ │
│ │ │
│ │ │
│ │ 3* │
│ │ │
│ │ │
│ │ │
│ │ │
└─────────────────────────────┴─────────────────────────────┘

After:

┌─────────────────────────────┬─────────────────────────────┐
│ │ │
│ │ │
│ │ │
│ │ │
│ │ 2 │
│ │ │
│ │ │
│ │ │
│ 3* ├─────────────────────────────┤
│ │ │
│ │ │
│ │ │
│ │ 1 │
│ │ │
│ │ │
│ │ │
│ │ │
└─────────────────────────────┴─────────────────────────────┘

If the focused window is already in main position, nothing happens.

Toggle floating

spoon.Shoji:bindHotkeys({
toggle_float = { { "ctrl", "alt", "shift" }, "t" },
})

Switches the focused window between tiling and floating modes:

  • Tiling to floating: The window leaves the tiled layout. Remaining windows expand to fill the gap.
  • Floating to tiling: The window rejoins the layout as a new entry, typically at the end of the tile order.

An on-screen alert confirms the new state (“Window floating” or “Window tiled”).

Float state persistence

  • Persists across retiles and layout changes
  • Cleared when the window closes
  • Per-window, not per-macOS Space (a window stays floating on all macOS Spaces)

Floating window behavior

Floating windows:

  • Ignore all tiling operations
  • Can be freely moved and resized
  • Sit above tiled windows (standard macOS z-order)
  • Are skipped by focus navigation actions

Toggle zoom

spoon.Shoji:bindHotkeys({
toggle_zoom = { { "ctrl", "alt" }, "f" },
})

Temporarily maximizes the focused window to fill the entire screen work area. The window floats above the tiled layout while zoomed, and remaining tiled windows rearrange to fill the gap.

Press again to unzoom and restore the window to its tiled position.

How zoom differs from floating

  • Floating removes a window from tiling so you can position it freely. The window keeps its current size and position.
  • Zoom floats and maximizes the window in one step. Unzooming restores it to tiling automatically.

Floating state preservation

If a window is already floating when you zoom it, unzooming keeps it floating (rather than forcing it back into tiling). This prevents accidentally losing a deliberate floating state.

Zoom state persistence

  • Persists across retiles and layout changes (same as floating)
  • Cleared when the window closes

Retile space

spoon.Shoji:bindHotkeys({
retile_space = { { "ctrl", "alt", "shift" }, "r" },
})

Reapplies the current layout using existing parameters. Useful when windows get misaligned or after manual moves.

To reset main_ratio, main_count, and layout-specific state, use reset_space in Layout control.