atagen

monolith

public
Star0
Watch0

Commit b2274115d1c6

tarball

atagen <boss@atagen.co>2026-10-22 10:06 UTC

4 files changed+40-0
layout: add balance-block to share a block equally among its children
Modified file: crates/ipc/src/lib.rs+3 additions
@@ -57,6 +57,8 @@ pub enum Command {
5757 axis: Axis,
5858 delta: f64,
5959 },
60+ /// Shares the focused window's block equally among its children.
61+ BalanceBlock,
6062 Expand {
6163 scope: ExpandScope,
6264 },
@@ -166,6 +168,7 @@ impl Command {
166168 "flip" => none(Command::Flip)?,
167169 "maximize" => none(Command::Maximize)?,
168170 "invert-move" => none(Command::InvertMove)?,
171+ "balance-block" => none(Command::BalanceBlock)?,
169172 "float" => none(Command::Float)?,
170173 "fullscreen" => none(Command::Fullscreen)?,
171174 "sticky" => none(Command::Sticky)?,
Modified file: crates/layout/src/lib.rs+10 additions
@@ -687,6 +687,16 @@ impl Layout {
687687 done
688688 }
689689
690+ pub fn balance(&mut self) -> bool {
691+ let Some((w, o)) = self.focused_tiled() else {
692+ return false;
693+ };
694+ let mut tree = std::mem::take(&mut self.outputs[o].tree);
695+ let done = tree.balance(w, self.vis_on(o));
696+ self.outputs[o].tree = tree;
697+ done
698+ }
699+
690700 /// Whether `w` is tiled with a visible window across the split on side `dir`.
691701 pub fn has_split(&self, w: WindowId, dir: Dir) -> bool {
692702 let Some(o) = self.windows.get(w).and_then(|w| w.output) else {
Modified file: crates/layout/src/tree.rs+24 additions
@@ -483,6 +483,30 @@ impl<W: Copy + Eq + Hash> Tree<W> {
483483 false
484484 }
485485
486+ /// Gives every visible child of the nearest container where `w` has a
487+ /// visible sibling an equal share along that container's axis.
488+ pub fn balance(&mut self, w: W, vis: impl Fn(W) -> bool) -> bool {
489+ let Some(&(p, _)) = self.shown_path(w, &vis).first() else {
490+ return false;
491+ };
492+ let Some(axis) = self.axis(p) else {
493+ return false;
494+ };
495+ let shown: Vec<(NodeId, f64)> = self
496+ .children(p)
497+ .iter()
498+ .filter(|&&c| self.visible(c, &vis))
499+ .map(|&c| (c, self.extent(c, axis, &vis)))
500+ .collect();
501+ let mean = shown.iter().map(|s| s.1).sum::<f64>() / shown.len() as f64;
502+ for (c, e) in shown {
503+ if e > 0.0 {
504+ self.scale(c, axis, mean / e);
505+ }
506+ }
507+ true
508+ }
509+
486510 /// The lane a window's split on side `dir` lives in, as the child holding
487511 /// the window, its parent and the index of the visible sibling across
488512 /// the split.
Modified file: crates/monolith/src/commands.rs+3 additions
@@ -96,6 +96,9 @@ impl State {
9696 Command::Resize { axis, delta } => {
9797 self.wm.layout.resize(axis, delta);
9898 }
99+ Command::BalanceBlock => {
100+ self.wm.layout.balance();
101+ }
99102 Command::Expand { scope } => {
100103 self.wm.layout.expand(match scope {
101104 monolith_ipc::ExpandScope::Window => ExpandScope::Window,