baskerville / bspwm

A tiling window manager based on binary space partitioning
BSD 2-Clause "Simplified" License
7.81k stars 416 forks source link

Master Stack tiling layouts #1450

Open yogeshdnumb opened 1 year ago

yogeshdnumb commented 1 year ago

It is almost present in every DE and WMs then why not bspwm It is much useful than the dwindle one Is there a way to implement that in bspwm

emanuele6 commented 1 year ago

My opinion on this is that automatic master-stack layouts are useless, they are basically bspwm's monocle layout, but worse because you have other stuff occupying the screen.

I think having a hotkey to manually arrange a group ("subtree") of windows vertically and horizontally is much better.

Personally, I use super + z and super + x for this:

# super + z -> layout becomes tiled; all splits are balanced and vertical
# super + x -> layout becomes tiled; all splits are balanced and horizontal
super + {z,x}
    bspc desktop 'focused.!user_tiled' -l tiled; \
    root=$(bspc query -N -n 'focused.!leaf') \
        || root=$(bspc query -N -n '@/.!leaf') \
        || exit 1; \
    while bspc node "$root"'#any.!{vertical,horizontal}.!leaf.descendant_of' -y next; do :; done; \
    bspc node "$root" -B
backport to bspwm 0.9.10 in case you are not compiling bspwm from the code in this git repository

```bash # super + z -> layout becomes tiled; all splits are balanced and vertical # super + x -> layout becomes tiled; all splits are balanced and horizontal super + {z,x} bspc desktop -l tiled; \ case {v,h} in \ v) sel=vertical r=-90 cr=90 ;; \ *) sel=horizontal r=90 cr=-90; \ esac; \ root=$(bspc query -N -n 'focused.!leaf') \ || root=$(bspc query -N -n '@/.!leaf') \ || exit 1; \ while nid=$(bspc query -N "$root" -n 'any.!'"$sel"'.!leaf.descendant_of'); do \ if bspc node "$nid" -R "$r"; then \ bspc node "$nid"'#@1' -R "$cr"; \ bspc node "$nid"'#@2' -R "$cr"; \ fi; \ done; \ bspc node "$root" -B ```

When I press those hotkeys normally, all windows get "stacked" verically/horizontally.

(</= is focus, h is a horizontal split, and v is a vertical split)

+-----+-----+   super+z   +-----------+   super+x   +--+--+--+==+
|     |     |             |     A     |             |  |  |  =  =
|     |  B  |             +-----------+             |  |  |  =  =
|     |     |             |     B     |             |  |  |  =  =
|  A  +--+==+     ==>     +-----------+     ==>     |A |B |C =D<=
|     |  =  =             |     C     |             |  |  |  =  =
|     |C =D<=             +===========+             |  |  |  =  =
|     |  =  =             =     D<    =             |  |  |  =  =
+-----+--+==+             +===========+             +--+--+--+==+
    v                         v                         h
  /   \                     /   \                     /   \
 A     h                   A     v                   A     h
     /   \                     /   \                     /   \
    B     v                   B     v                   B     h
         / \                       / \                       / \
        C   D<                    C   D<                    C   D<

So they change all the split of in the current current desktop to horizontal/vertical and they balance them so all the tiled windows occupy the same space.

But if the focused node is not a window, and is an internal node, it will do something slightly different, it will only change the splits in the subtree of that internal node.

To focus the parent node of a node you can use an hotkey like this one:

super + p
    bspc node @parent -f

And then you can do stuff like this:

+-----+-----+   super+p   +-----+-----+   super+p   +-----+=====+   super+x   +-----+=====+
|     |     |             |     |     |             |     =     =             |     =     =
|     |  B  |             |     |  B  |             |     =  B  =             |     =  B  =
|     |     |             |     |     |             |     =     =             |     =-----=
|  A  +--+==+     ==>     |  A  +=====+     ==>     |  A  =--+--=     ==>     |  A  =     =
|     |  =  =             |     =  |  =             |     =  |  =             |     =  C  =
|     |C =D<=             |     =C |D =             |     =C |D =             |     =-----=
|     |  =  =             |     =  |  =             |     =  |  =             |     =     =
|     |  =  =             |     =  |  =             |     =  |  =             |     =  D  =
+-----+--+==+             +-----+=====+             +-----+=====+             +-----+=====+
    v                         v                         v                         v
  /   \                     /   \                     /   \                     /   \
 A     h                   A     h                   A     h<                  A     h<
     /   \                     /   \                     /   \                     /   \
    B     v                   B     v<                  B     v                   B     h
         / \                       / \                       / \                       / \
        C   D<                    C   D                     C   D                     C   D

I have other hotkeys that act only of the subtree the focused internal node instead of root, if the focused node is not a "leaf" (\~window).

some of the hotkeys I use

```bash # Cycle Layout (shift => set layout to tiled) super + {_,shift + }Tab bspc desktop -l {next,tiled} # Focus the parent of the focused node super + p bspc node @parent -f # Focus the {first,second} child of the focused node super + shift + {_,alt + }p bspc node @{1,2} -f # Toggle splitting type of the focused node if it is not a leaf node super + shift + r bspc node 'focused.!leaf' -y next \ || bspc node '@/.!leaf' -y next # Balance the focused inner node, or the root node super + b bspc node 'focused.!leaf' -B || \ bspc node '@/.!leaf' -B # Set the split ratio of the focused inner node, or the root node to 50% super + alt + b bspc node 'focused.!leaf' -r 0.5 || \ bspc node '@/.!leaf' -r 0.5 # Cycle splitting type of the parent node super + r if bspc query -D focused.tiled > /dev/null; then \ bspc node '@brother.!floating.!fullscreen.!hidden.leaf#focused.!floating.!fullscreen#@parent' -y next \ || bspc node '@brother.!hidden.!leaf#any.!hidden.!floating.!fullscreen.leaf.descendant_of#focused.!floating.!fullscreen#@parent' -y next \ || bspc node '@/.focused.!leaf' -y next; \ fi # super + z -> layout becomes tiled; all splits are balanced and vertical # super + x -> layout becomes tiled; all splits are balanced and horizontal super + {z,x} bspc desktop 'focused.!user_tiled' -l tiled; \ root=$(bspc query -N -n 'focused.!leaf') \ || root=$(bspc query -N -n '@/.!leaf') \ || exit 1; \ while bspc node "$root"'#any.!{vertical,horizontal}.!leaf.descendant_of' -y next; do :; done; \ bspc node "$root" -B # Focus a floating window / Cycle floating windows. super + {_,shift + }g bspc node 'focused.!leaf' -f 'any.!hidden.floating.descendant_of' \ || bspc node focused.floating -f '{next,prev}.local.!hidden.floating' \ || bspc node 'last.local.!focused.!hidden.floating' -f \ || bspc node 'any.local.!focused.!hidden.floating' -f # Focus a (pseudo_)tiled window / Cycle (pseudo_)tiled windows. super + {_,shift + }c bspc node 'focused.!leaf' -f 'any.!hidden.!focused.!floating.!fullscreen.window.descendant_of' \ || bspc node 'focused.!floating.!fullscreen.window' -f '{next,prev}.local.!hidden.!floating.!fullscreen.window' \ || bspc node 'last.local.!focused.!hidden.!floating.!fullscreen.window' -f \ || bspc node 'any.local.!focused.!hidden.!floating.!fullscreen.window' -f # Swap (pseudo_)tiled nodes. super + ctrl + {_,shift + }c bspc node 'focused.!floating.!fullscreen.window' -s '{next,prev}.local.!floating.!fullscreen.window' # Toggle _EMANUELE6_INVERT super + alt + i bspc query -N focused -n '.!hidden.window.descendant_of' | \ while IFS= read -r wid; do \ case $(xprop -id "$wid" _EMANUELE6_INVERT) in \ '_EMANUELE6_INVERT(CARDINAL) = 1') \ xprop -id "$wid" -remove _EMANUELE6_INVERT ;; \ *) \ xprop -id "$wid" -format _EMANUELE6_INVERT 8c \ -set _EMANUELE6_INVERT 1; \ esac \ done # Set/Unset _EMANUELE6_INVERT super + ctrl {_,shift + }i bspc query -N focused -n '.!hidden.window.descendant_of' | \ while IFS= read -r wid; do \ xprop -id "$wid" \ {-format _EMANUELE6_INVERT 8c -set _EMANUELE6_INVERT 1 \ ,-remove _EMANUELE6_INVERT}; \ done super + {_,shift + }{h,j,k,l} bspc node 'focused.!floating' -{f,s} '{west,south,north,east}.!floating' ```


If you really want something automatic, you can use the spiral automatic insertion scheme, maybe with initial set to first_child. I personally do not like it, but it is pretty much the window disposition you want, except windows in the "stack" are spiraly.

bspc config automatic_scheme spiral
bspc config initial_polarity first_child

Note that is not a layout, so it does force the windows to be displayed in that way regardless of the tree state, you can resize them, move them, etc. And note that this does not automatically destructively move the nodes in your tree to force the windows to be displayed in the spiral.

It is just an insertion scheme that tells bspwm how to create new splits for when a new window (or receptacle is inserted)

It looks something like this:

+-----------+    +-----+-----+    +-----+-----+    +-----+-----+    +-----+-----+
|           |    |     |     |    |     |     |    |     |     |    |     |     |
|           |    |     |     |    |     |  B  |    |     |  C  |    |     |  D  |
|           |    |     |     |    |     |     |    |     |     |    |     |     |
|     A     | => |  B  |  A  | => |  C  +-----+ => |  D  +--+--+ => |  E  +--+--+
|           |    |     |     |    |     |     |    |     |  |  |    |     |A |  |
|           |    |     |     |    |     |  A  |    |     |A |B |    |     +--+C |
|           |    |     |     |    |     |     |    |     |  |  |    |     |B |  |
+-----------+    +-----+-----+    +-----+-----+    +-----+--+--+    +-----+--+--+

Also, since it is insertion only, if you remove a leaf (close a window), the layout will not remain consistent.