Resize tmux Main Panes By Percentage

As of tmux 2.6, by default, main-pane-horizontal and main-pane-vertical layouts require some extra legwork to stay proportional. This article shows a script that can set dimensions of this type of layout through percentages. Note: By default the even-vertical, even-horizontal, and tiled layouts should always stay even.

This has been mentioned before on the tmux issue tracker at tmux/tmux#888. There is a need to create a script that's flexible enough to resize main pane widths and heights based on the size of the client. This is subject to change as the user's terminal size changes (due to switching monitors, maximizing, tiling window managers, adjusting font size, screen resolutions, and so on).

The only way to get a comfortable proportion set almost every time is to use a percentage.

We could use run-shell, which allows us to do some variable interpolation and kung-fu. I have tried this in my own config for a few weeks and for some reason, the options wouldn't stick. Invoking a shell script would be more reliable.

Since we're there, though, user's may have different preferences for what proportions they want. For instance, I want main-vertical at 66%. Other users want a different ratio. And also, some users may prefer main-horizontal. So let's use script arguments and conditionals to handle these cases.

Second, let's try to make is POSIX-compliant as possible so it can work on systems like FreeBSD and OpenBSD which don't include Bash/Zsh by default.

Third, let's give the user some feedback if they don't enter an argument, such as the percentage, the right way.

Forth, let's allow the user to specify a target window, optionally.

The latest script can be found in it's entirety at tony/tmux-config/blob/master/scripts/resize-adaptable.sh, or below:

lflag=
pflag=
tflag=
while getopts l:p:t: name;
do
    case $name in
    l)    lflag=1
      layout_name=$OPTARG;;
    p)    pflag=1
          percentage="$OPTARG";;
    t)    tflag=1
          target="$OPTARG";;
    ?)   printf "Usage: %s: [-l layout-name] [-p percentage] [-t target-window]\n" $0
          exit 2;;
    esac
done

if [ ! -z "$pflag" ]; then
    if ! [ "$percentage" -eq "$percentage" ] 2> /dev/null; then
        printf "Percentage (-p) must be an integer" >&2
        exit 1
    fi
fi
if [ ! -z "$lflag" ]; then
    if [ $layout_name != 'main-horizontal' ] && [ $layout_name != 'main-vertical' ] ; then
        printf "layout name must be main-horizontal or main-vertical" >&2
        exit 1
    fi
fi

if [ "$layout_name" = "main-vertical" ]; then
    MAIN_PANE_SIZE=$(expr $(tmux display -p '#{window_width}') \* $percentage \/ 100)
    MAIN_SIZE_OPTION='main-pane-width'

fi

if [ "$layout_name" = "main-horizontal" ]; then
    MAIN_PANE_SIZE=$(expr $(tmux display -p '#{window_height}') \* $percentage \/ 100)
    MAIN_SIZE_OPTION='main-pane-height'
fi

if [ ! -z "$target" ]; then
    tmux setw -t $target $MAIN_SIZE_OPTION $MAIN_PANE_SIZE; tmux select-layout -t $target $layout_name
else
    tmux setw $MAIN_SIZE_OPTION $MAIN_PANE_SIZE; tmux select-layout $layout_name
fi

exit 0

I placed the script in ~/.tmux/scripts/resize-adaptable.sh.

In order to make this available as a keybinding, let's do this in our ~/.tmux.conf:

# set to main-horizontal, 66% height for main pane
bind m run-shell "~/.tmux/scripts/resize-adaptable.sh -l main-horizontal -p 66"
# Same thing for verical layouts
bind M run-shell "~/.tmux/scripts/resize-adaptable.sh -l main-vertical -p 50"

Above, create a keybinding, in this case, we picked m, you can switch m with anything. You can use prefix: to open the prompt and so ~/.tmux.conf return to reload the config.

Try prefixm. Your panes should resize to be 2/3 the height of your client's window size, something like this:

Main horitontal

.___________.
|           |
|           | < main pane 66% height
|-----------|
|   |   |   | < other panes, split left to right
|___|___|___|

prefixM will set the main pane on the left side at half the client's width, stacking other panes on the right, from top to bottom. It looks like:

Main vertical

   /----------- main pane half width
.___________.
|     |     |
|     |_____|
|     |     |  <  < other panes on the right, split top to bottom
|     |_____|
|     |     |
|_____|_____|

Example usage:

Case 1: Resize to a main-horizontal, main pane 66% of client height:

$ ./scripts/resize-adaptable.sh -p 66 -l main-horizontal

Case 2: Same as Case 1, target "devel" window:

$ ./scripts/resize-adaptable.sh -p 66 -l main-horizontal -t devel

Case 3: Resize to a main-horizontal, main pane half width:

$ ./scripts/resize-adaptable.sh -p 50 -l main-vertical

Case 4: Same as Case 3, target "mywindow":

$ ./scripts/resize-adaptable.sh -p 50 -l main-vertical -t mywindow