Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui): swap layouts and stacked panes #2167

Merged
merged 57 commits into from
Feb 17, 2023
Merged

Conversation

imsnif
Copy link
Member

@imsnif imsnif commented Feb 16, 2023

Hi! This started out as me wanting to add the capability of applying layouts at runtime but it went a little bit off the rails.
There were a few pieces of infrastructure that needed to be laid down in order to do the above, which will happen in the next PR.
This PR however does include many exciting changes that I for one cannot wait to start using.

So what do we have here?

2023-02-16 17-06-26

Swap Layouts

Swap layouts allow us to change the layout of the currently focused tab at runtime. They are built-in, but completely configurable just as the layouts are.
By default, we can rotate through these swap layouts with Alt [ or Alt ] (also configurable like all of our keybindings), gaining access to more layouts according to their constraints.
I included 3 basic swap layouts (horizontal, vertical and stacked) for each one of our built-in layouts so the experience will be smooth out of the box with users not having to configure anything for this to "just work".

Swap layouts can also be applied (separately) to floating panes. If floating fanes are visible on screen and we switch a layout with Alt [ or Alt ], the floating panes will be rearranged instead of the tiled panes. The built-in floating panes layouts are "staggered", "enlarged" and "spread".

I added a piece of UI to both the status bar and the compact status bar that shows which layout we are currently in. In the status-bar it also shows the keybinding to switch the layout. It appears as soon as there is more than 1 selectable pane on screen (before that, it's not relevant).

auto_layout

Starting with this PR, panes opened with Alt + n or Ctrl p + n will be laid out according to the currently active swap_layout (if one is available). I feel this will significantly improve the experience, as they will both be laid out deterministically and be placed more comfortably on screen.

When a pane is closed, the current swap_layout (if available) will reapply itself.

If a new pane is added and the current swap layout constraint does not fit (eg. the constraint is max_panes=5 and this is pane number 6), Zellij will look for a different layout with a fitting constraint. This allows Zellij to progressively change its layout depending on how many panes are on screen.

If the user performs an action that explicitly changes the layout (eg. resizing a pane or doing a horizontal/vertical split) the layout will be considered "dirty" and this behaviour will be suspended. Pressing Alt + [ or Alt + ] in this case will reapply the current layout, allowing users to mess things around and then snap them back to a desired pre-set condition when they're ready.

This behaviour can be disabled in the configuration by specifying auto_layout false.

Configuration

Swap layouts can either be configured in the same file as regular layouts or in a separate .swap.kdl file in the same folder. (eg. if my layout is called my-cool-layout.kdl, my swap layout file will be called my-cool-layout.swap.kdl).

Here's an example:

swap_tiled_layout name="vertical" {
    tab max_panes=5 {
        pane split_direction="vertical" {
            pane
            pane { children; }
        }
    }
    tab max_panes=8 {
        pane split_direction="vertical" {
            pane { children; }
            pane { pane; pane; pane; pane; }
        }
    }
    tab max_panes=12 {
        pane split_direction="vertical" {
            pane { children; }
            pane { pane; pane; pane; pane; }
            pane { pane; pane; pane; pane; }
        }
    }
}

swap_floating_layout name="spread" {
    floating_panes max_panes=1 {
        pane {y "50%"; x "50%"; }
    }
    floating_panes max_panes=2 {
        pane { x "1%"; y "25%"; width "45%"; }
        pane { x "50%"; y "25%"; width "45%"; }
    }
    floating_panes max_panes=3 {
        pane focus=true { y "55%"; width "45%"; height "45%"; }
        pane { x "1%"; y "1%"; width "45%"; }
        pane { x "50%"; y "1%"; width "45%"; }
    }
}

A swap_tiled_layout node can contain a name field in its title (if present, this is the name that will be used in the UI to indicate the active layout). Its children are normal layout tab nodes that can include all of the existing attributes of tab nodes in addition to a constraint node (more on these below) and a children node. If a children node is included in the tab node, all new panes will be opened as logical children of said node.

A swap_floating_layout is similar to a swap_tiled_layout, only it cannot include a children node, and the floating_panes node is used as its swap unit instead of a tab node.

When Zellij swaps layouts, it will progressively move through the tab or floating_panes nodes in the currently active layout, looking for the first one whose constraints match the current state of panes.

Panes in swap layouts can include specific plugins or commands, but it's important to note that swap layouts will never open or close panes. Meaning if a command exists in the swap layout, Zellij will do its best effort to find a matching pane and place it in the right place, but it will not start this command. Same for plugins.

Swap layouts can also be tab_templates just like normal tiled or floating layouts can be. In order to save repetition when creating many similar swap layouts.

Constraints

Currently there are two possible mutually exclusive constraints for layouts max_panes and min_panes. If there is demand, it is conceivable that in the future we'll add the ability for them to not be mutually exclusive, as well as add additional constraints (eg. min_screen_size and max_screen_size to allow for responsive layouts).

Stacked Panes

One of the swap layouts I included for all built-in layouts (default, compact and strider) is stacked. This swap layout includes a new Zellij capability, which is to stack panes. Stacked panes will show up as one liner panes - only rendering their title lines whether pane-frames are enabled or not - with one "flexible" pane which is the focused one. This flexible pane can be moved up or down and the stack will react accordingly.

If stacked panes are command panes (meaning they were either opened with pane command="htop" or with zellij run -- htop), their one-liner variety will also include their EXIT CODE, allowing us to easily throw a few long running tasks into a stack and visually gauge when they're done and if anything went wrong with them.

Stacked panes can be configured in swap layouts as so:

swap_tiled_layout name="stacked" {
    ui min_panes=5 {
        pane split_direction="vertical" {
            pane
            pane { children stacked=true; }
        }
    }
}

Toggle pane location forwards/backwards

This PR also includes a minor fix to move_pane, allowing it to also work with floating panes. By default this is Ctrl h + n and will move the currently focused pane around forwards or backwards. This PR also includes a new bindable action MovePaneBackwards, by default Ctrl h + p to move the same pane backwards. In conjunction with the swaped layout and stacked panes features, this provides for quite a powerful experience!

@imsnif imsnif temporarily deployed to cachix February 16, 2023 16:20 — with GitHub Actions Inactive
@imsnif imsnif temporarily deployed to cachix February 16, 2023 16:29 — with GitHub Actions Inactive
@imsnif imsnif temporarily deployed to cachix February 17, 2023 09:53 — with GitHub Actions Inactive
@imsnif imsnif temporarily deployed to cachix February 17, 2023 10:24 — with GitHub Actions Inactive
@imsnif imsnif merged commit f1ff272 into main Feb 17, 2023
joshheyse pushed a commit to joshheyse/zellij that referenced this pull request Mar 11, 2023
* relayout working with hard coded layout

* work

* refactor(layout): PaneLayout => TiledPaneLayout

* tests passing

* tests passing

* tests passing

* stacked panes and passing tests

* tests for stacked panes

* refactor(panes): stacked panes

* fix: focusing into stacked panes from the left/right

* fix(layouts): handle stacked layouts in the middle of the screen

* fix(pane-stack): focus correctly when coming to stack from above/below

* fix(stacked-panes): resize stack

* fix(stacked-panes): focus with mouse

* fix(stacked-panes): focus next pane

* fix(layout-applier): sane focus order

* fix(stacked-panes): better titles for one-liners

* fix(stacked-panes): handle moving pane location in stack

* fix(relayout): properly calculate display area

* fix(relayout): properly calculate rounding errors

* fix(stacked-panes): properly handle closing a pane near a stack

* fix(swap-layouts): adjust swap layout sort order

* feat(swap-layouts): ui + ux

* fix(swap-layouts): include base layout

* refactor(layout): remove unused method

* fix(swap-layouts): respect pane contents and focus

* work

* fix(swap-layouts): load swap layouts from external file

* fix(swap-layouts): properly truncate layout children

* fix(stacked-panes): allow stacked panes to become fullscreen

* fix(swap-layouts): work with multiple tabs

* fix(swap-layouts): embed/eject panes properly with auto-layout

* fix(stacked-panes): close last pane in stack

* fix(stacked-panes): move focus for all clients in stack

* fix(floating-panes): set layout damaged when moving panes

* fix(relayout): move out of unfitting layout when resizing whole tab

* fix(ui): background color for swap layout indicator

* fix(keybinds): add switch next layout in tmux

* fix(ui): swap layout indication in compact layout

* fix(compact): correct swap constraint

* fix(tests): tmux swap config shortcut

* fix(resizes): cache resizes so as not to confuse panes (eg. vim) with multiple resizes that it debounces weirdly

* feat(cli): dump swap layouts

* fix(ui): stacked panes without pane frames

* fix(ux): move pane forward/backwards also with floating panes

* refactor(lint): remove unused stuff

* refactor(tab): move swap layouts to separate file

* style(fmt): rustfmt

* style(fmt): rustfmt

* refactor(panes): various cleanups

* chore(deps): upgrade termwiz to get alt left-bracket

* fix(assets): merge conflicts of binary files

* style(fmt): rustfmt

* style(clippy): no thank you!

* chore(repo): remove garbage file
@janos-r
Copy link

janos-r commented May 8, 2024

How to make Alt [ and Alt ] work on a custom default layout?
I only want to have a custom default workspace, but I can't swap the automatic layouts in it.

@imsnif
Copy link
Member Author

imsnif commented May 8, 2024

You need to create swap layouts for your custom layout (or re-use the default ones) for this to work. Check out: https://zellij.dev/documentation/swap-layouts.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants