Skip to content

Latest commit

 

History

History
421 lines (338 loc) · 40.2 KB

README.md

File metadata and controls

421 lines (338 loc) · 40.2 KB

Qtile Bonsai


Introduction

Qtile Bonsai provides a flexible layout for the qtile tiling window manager that allows you to arrange windows as tabs, splits and even subtabs inside splits.

It also provides an API with window-management operations that allow for quick access and rearrangements of tabs/windows.

For a quick feeler, look at the demo below, or the visual guide further below.


qtile_bonsai_demo.mp4

Getting Started

Installation

Assuming you already have qtile up and running, you have the following options for installation.

PyPI

pip install qtile-bonsai 

Note

If you have qtile installed globally (eg. via your distro's package manager), you likely have to do:

pip install qtile-bonsai --break-system-packages

Nowadays pip tries to play it safe and not potentially modify some dependency that your system's Python-software may depend on. For qtile-bonsai, using this flag should be safe.

An alternative approach would be to have installed qtile via pipx and then 'inject' qtile-bonsai into the same virtualenv where qtile resides:

pipx install qtile
pipx inject qtile qtile-bonsai

AUR

For arch-based distros, you can install it from the AUR either manually or with your favorite AUR-helper. For example:

yay -S qtile-bonsai

NixOS

It is available in nixpgks as qtile-bonsai:

{
  services.xserver.windowManager.qtile = {
    enable = true;
    extraPackages = python3Packages: with python3Packages; [
      qtile-bonsai
    ];
  };
}

Configuration

1. Make Bonsai available as a layout in your qtile config

from qtile_bonsai import Bonsai


layouts = [
    Bonsai(**{
      # Specify your options here. These examples are defaults.
      "window.border_size": 1,
      "tab_bar.height": 20,
      
      # You can specify subtab level specific options if desired by prefixing
      # the option key with the appropriate level, eg. L1, L2, L3 etc.
      # For example, the following options affect only 2nd level subtabs and
      # their windows:
      # "L2.window.border_color": "#ff0000",
      # "L2.window.margin": 5,
    }),
]

2. Add your personal keybindings to your qtile config

from libqtile.config import EzKey, KeyChord
from libqtile.lazy import lazy
from libqtile.utils import guess_terminal


terminal = guess_terminal()
rofi_run_cmd = "rofi -show drun -m -1"

keys = [
    # Open your terminal emulator quickly. See further below for how to
    # directly open other apps as splits/tabs using something like rofi.
    EzKey("M-v", lazy.layout.spawn_split(terminal, "x")),
    EzKey("M-x", lazy.layout.spawn_split(terminal, "y")),
    EzKey("M-t", lazy.layout.spawn_tab(terminal)),
    EzKey("M-S-t", lazy.layout.spawn_tab(terminal, new_level=True)),
    
    # Sometimes it's handy to have a split open in the 'previous' position
    EzKey("M-S-v", lazy.layout.spawn_split(terminal, "x", position="previous")),
    EzKey("M-S-x", lazy.layout.spawn_split(terminal, "y", position="previous")),

    # Motions to move focus. The names are compatible with built-in layouts.
    EzKey("M-h", lazy.layout.left()),
    EzKey("M-l", lazy.layout.right()),
    EzKey("M-k", lazy.layout.up()),
    EzKey("M-j", lazy.layout.down()),
    EzKey("M-d", lazy.layout.prev_tab()),
    EzKey("M-f", lazy.layout.next_tab()),
    
    # Precise motions to move directly to specific tabs at the nearest tab level
    EzKey("M-1", lazy.layout.focus_nth_tab(1, level=-1)),
    EzKey("M-2", lazy.layout.focus_nth_tab(2, level=-1)),
    EzKey("M-3", lazy.layout.focus_nth_tab(3, level=-1)),
    EzKey("M-4", lazy.layout.focus_nth_tab(4, level=-1)),
    EzKey("M-5", lazy.layout.focus_nth_tab(5, level=-1)),
    
    # Precise motions to move to specific windows. The options provided here let
    # us pick the nth window counting only from under currently active [sub]tabs
    EzKey("C-1", lazy.layout.focus_nth_window(1, ignore_inactive_tabs_at_levels=[1,2])),
    EzKey("C-2", lazy.layout.focus_nth_window(2, ignore_inactive_tabs_at_levels=[1,2])),
    EzKey("C-3", lazy.layout.focus_nth_window(3, ignore_inactive_tabs_at_levels=[1,2])),
    EzKey("C-4", lazy.layout.focus_nth_window(4, ignore_inactive_tabs_at_levels=[1,2])),
    EzKey("C-5", lazy.layout.focus_nth_window(5, ignore_inactive_tabs_at_levels=[1,2])),

    # Resize operations
    EzKey("M-C-h", lazy.layout.resize("left", 100)),
    EzKey("M-C-l", lazy.layout.resize("right", 100)),
    EzKey("M-C-k", lazy.layout.resize("up", 100)),
    EzKey("M-C-j", lazy.layout.resize("down", 100)),

    # Swap windows/tabs with neighbors
    EzKey("M-S-h", lazy.layout.swap("left")),
    EzKey("M-S-l", lazy.layout.swap("right")),
    EzKey("M-S-k", lazy.layout.swap("up")),
    EzKey("M-S-j", lazy.layout.swap("down")),
    EzKey("A-S-d", lazy.layout.swap_tabs("previous")),
    EzKey("A-S-f", lazy.layout.swap_tabs("next")),
    
    # Manipulate selections after entering container-select mode
    EzKey("M-o", lazy.layout.select_container_outer()),
    EzKey("M-i", lazy.layout.select_container_inner()),

    # It's kinda nice to have more advanced window management commands under a
    # qtile key chord.
    KeyChord(
        ["mod4"],
        "w",
        [
            # Use something like rofi to pick GUI apps to open as splits/tabs.
            EzKey("v", lazy.layout.spawn_split(rofi_run_cmd, "x")),
            EzKey("x", lazy.layout.spawn_split(rofi_run_cmd, "y")),
            EzKey("t", lazy.layout.spawn_tab(rofi_run_cmd)),
            EzKey("S-t", lazy.layout.spawn_tab(rofi_run_cmd, new_level=True)),
            
            # Toggle container-selection mode to split/tab over containers of
            # multiple windows. Manipulate using select_container_outer()/select_container_inner()
            EzKey("C-v", lazy.layout.toggle_container_select_mode()),
            
            EzKey("o", lazy.layout.pull_out()),
            EzKey("u", lazy.layout.pull_out_to_tab()),
            
            EzKey("r", lazy.layout.rename_tab()),
            
            # Directional commands to merge windows with their neighbor into subtabs.
            KeyChord(
                [],
                "m",
                [
                    EzKey("h", lazy.layout.merge_to_subtab("left")),
                    EzKey("l", lazy.layout.merge_to_subtab("right")),
                    EzKey("j", lazy.layout.merge_to_subtab("down")),
                    EzKey("k", lazy.layout.merge_to_subtab("up")),

                    # Merge entire tabs with each other as splits
                    EzKey("S-h", lazy.layout.merge_tabs("previous")),
                    EzKey("S-l", lazy.layout.merge_tabs("next")),
                ],
            ),
            
            # Directional commands for push_in() to move window inside neighbor space.
            KeyChord(
                [],
                "i",
                [
                    EzKey("j", lazy.layout.push_in("down")),
                    EzKey("k", lazy.layout.push_in("up")),
                    EzKey("h", lazy.layout.push_in("left")),
                    EzKey("l", lazy.layout.push_in("right")),
                    
                    # It's nice to be able to push directly into the deepest
                    # neighbor node when desired. The default bindings above
                    # will have us push into the largest neighbor container.
                    EzKey(
                        "S-j",
                        lazy.layout.push_in("down", dest_selection="mru_deepest"),
                    ),
                    EzKey(
                        "S-k",
                        lazy.layout.push_in("up", dest_selection="mru_deepest"),
                    ),
                    EzKey(
                        "S-h",
                        lazy.layout.push_in("left", dest_selection="mru_deepest"),
                    ),
                    EzKey(
                        "S-l",
                        lazy.layout.push_in("right", dest_selection="mru_deepest"),
                    ),
                ],
            ),
        ]
    ),
    
    # Your other bindings
    # ...
]

3. [Optional] Add the BonsaiBar widget to your qtile bar

qtile-bonsai comes with an optional BonsaiBar widget that lets you view all your top-level tabs on the qtile-bar.

The default behavior is to automatically hide the top-level/outermost tab-bar if there is a BonsaiBar widget on the relevant screen. If there isn't, the tab bar is shown as usual.

from libqtile import bar
from libqtile.config import Screen

from qtile_bonsai import BonsaiBar


screens = [
    Screen(top=bar.Bar([
        BonsaiBar(**{
            # "length": 500,
            # "sync_with": "bonsai_on_same_screen",
            # "tab.width": 50,
            # ...
        }),
        # ... your other widgets ...
    ])),
]

Visual Guide

Click on the image to open a web view with the full guide.

Visual Guide

Reference

Layout Configuration

Tip

Most options have subtab-level support! ie. you can have one setting for top level windows and another setting for windows under 2nd level subtabs. eg:

Bonsai({
  "window.margin": 10,
  "L2.window.margin": 5,
})

The format is L<subtab-level>.<option-name> = <value>


Option Name Default Value Description
window.margin 0 Size of the margin space around windows.
Can be an int or a list of ints in [top,
right, bottom, left] ordering.
window.single.margin (unset) Size of the margin space around a window
when it is the single window remaining
under a top-level tab.
Can be an int or a list of ints in [top,
right, bottom, left] ordering.
If not specified, will fall back to
reading from window.margin.
window.border_size 1 Width of the border around windows. Must
be a single integer value since that's
what qtile allows for window borders.
window.single.border_size (unset) Size of the border around a window when
it is the single window remaining under
a top-level tab.
Must be a single integer value since
that's what qtile allows for window
borders.
If not specified, will fall back to
reading from window.border_size.
window.border_color Gruvbox.dull_yellow Color of the border around windows
window.active.border_color Gruvbox.vivid_yellow Color of the border around an active
window
window.normalize_on_remove True Whether or not to normalize the
remaining windows after a window is
removed.
If True, the remaining sibling windows
will all become of equal size.
If False, the next (right/down) window
will take up the free space.
window.default_add_mode tab (Experimental)

Determines how windows get added if they
are not explicitly spawned as a split or
a tab.
Can be one of "tab" or "match_previous".
If "match_previous", then then new
window will get added in the same way
the previous window was. eg. if the
previous window was added as a y-split,
so will the new window.

NOTE:
Setting this to "tab" may seem
convenient, since externally spawned GUI
apps get added as background tabs
instead of messing up the current split
layout.
But due to how the window creation flow
happens, when many splits are requested
in quick succession, this may cause some
windows requested as a split to open up
as a tab instead.
tab_bar.height 20 Height of tab bars
tab_bar.hide_when single_tab When to hide the tab bar. Allowed values
are 'never', 'always', 'single_tab'.

When 'single_tab' is configured, the bar
is not shown whenever there is a lone
tab remaining, but shows up again when
another tab is added.

For nested tab levels, configuring
'always' or 'single_tab' actually means
that when only a single tab remains, its
contents get 'merged' upwards,
eliminating the sub-tab level.
tab_bar.hide_L1_when_bonsai_bar_on_screen True For L1 (top level) tab bars only. If
True, the L1 tab bar is hidden away if
there is a BonsaiBar widget on the
screen this layout's group is on.
Otherwise the the L1 tab bar is shown
(depending on tab_bar.hide_when).

This is dynamic and essentially makes it
so the L1 tab bar shows up 'when
required'.
Handy in multi-screen setups if some
screens aren't configured to have a
qtile-bar, but the main screen does and
has a BonsaiBar widget as well.

Note that this takes precedence over
tab_bar.hide_when for L1 bars.
tab_bar.margin 0 Size of the margin space around tab
bars.
Can be an int or a list of ints in [top,
right, bottom, left] ordering.
tab_bar.border_size 0 Size of the border around tab bars.
Must be a single integer value since
that's what qtile allows for window
borders.
tab_bar.border_color Gruvbox.dark_yellow Color of border around tab bars
tab_bar.bg_color Gruvbox.bg0 Background color of tab bars, beind
their tabs
tab_bar.tab.width 50 Width of a tab on a tab bar.

Can be an int or auto. If auto, the
tabs take up as much of the available
screen space as possible.

Note that this width follows the 'margin
box'/'principal box' model, so it
includes any configured margin amount.
tab_bar.tab.margin 0 Size of the space on either outer side
of individual tabs.
Can be an int or a list of ints in [top,
right, bottom, left] ordering.
tab_bar.tab.padding 0 Size of the space on either inner side
of individual tabs.
Can be an int or a list of ints in [top,
right, bottom, left] ordering.
tab_bar.tab.bg_color Gruvbox.dull_yellow Background color of individual tabs
tab_bar.tab.fg_color Gruvbox.fg1 Foreground text color of individual tabs
tab_bar.tab.font_family Mono Font family to use for tab titles
tab_bar.tab.font_size 13 Font size to use for tab titles
tab_bar.tab.active.bg_color Gruvbox.vivid_yellow Background color of active tabs
tab_bar.tab.active.fg_color Gruvbox.bg0_hard Foreground text color of the active tab
tab_bar.tab.title_provider None A callback that generates the title for
a tab. The callback accepts 3 parameters
and returns the final title string. The
params are:
1. index:
    The index of the current tab in the
    list of tabs.
2. active_pane:
    The active Pane instance under
    this tab. A Pane is just a
    container for a window and can be
    accessed via pane.window.
3. tab:
    The current Tab instance.

For example, here's a callback that
returns the active window's title:
def my_title_provider(index,
active_pane, tab):
    return active_pane.window.name
container_select_mode.border_size 3 Size of the border around the active
selection when container_select_mode
is active.
container_select_mode.border_color Gruvbox.dark_purple Color of the border around the active
selection when container_select_mode
is active.
auto_cwd_for_terminals True (Experimental)

If True, when spawning new windows by
specifying a program that happens to
be a well-known terminal emulator, will
try to open the new terminal window in
same working directory as the last
focused window.
restore.threshold_seconds 4 You likely don't need to tweak this.
Controls the time within which a
persisted state file is considered to be
from a recent qtile config-
reload/restart event. If the persisted
file is this many seconds old, we
restore our window tree from it.

Layout Commands

Command Name Description
spawn_split Launch the provided program into a new window that splits the
currently focused window along the specified axis.

Args:
    program:
        The program to launch.
    axis:
        The axis along which to split the currently focused window. Can
        be 'x' or 'y'.
        An x split will end up with two left/right windows.
        A y split will end up with two top/bottom windows.
    ratio:
        The ratio of sizes by which to split the current window.
        If a window has a width of 100, then splitting on the x-axis
        with a ratio = 0.3 will result in a left window of width 30 and
        a right window of width 70.
        Defaults to 0.5.
    normalize:
        If True, overrides ratio and leads to the new window and
        all sibling windows becoming of equal size along the
        corresponding split axis.
        Defaults to True.
    position:
        Whether the new split content appears after or before the
        currently focused window.
        Can be "next" or "previous". Defaults to "next".

Examples:
- layout.spawn_split(my_terminal, "x")
- layout.spawn_split( my_terminal, "y", ratio=0.2, normalize=False)
- layout.spawn_split(my_terminal, "x", position="previous")
spawn_tab Launch the provided program into a new window as a new tab.

Args:
    program:
        The program to launch.
    new_level:
        If True, create a new sub-tab level with 2 tabs. The first
        sub-tab being the currently focused window, the second sub-tab
        being the newly launched program.
    level:
        If provided, launch the new window as a tab at the provided
        level of tabs in the currently focused window's tab
        hierarchy.
        Level 1 is the topmost level.

Examples:
    - layout.spawn_tab(my_terminal)
    - layout.spawn_tab(my_terminal, new_level=True)
    - layout.spawn_tab("qutebrowser", level=1)
move_focus Move focus to the window in the specified direction relative to the
currently focused window. If there are multiple candidates, the most
recently focused of them will be chosen.
When container_select_mode is active, will similarly pick neighboring
nodes, which may consist of multiple windows under it.

Args:
    direction:
        The direction in which a neighbor is found to move focus to.
        Can be "up"/"down"/"left"/"right".
    wrap:
        If True, will wrap around the edge and select items from the
        other end of the screen. Defaults to True.
left Same as move_focus("left"). For compatibility with API of other
built-in layouts.
right Same as move_focus("right"). For compatibility with API of other
built-in layouts.
up Same as move_focus("up"). For compatibility with API of other built-
in layouts.
down Same as move_focus("down"). For compatibility with API of other
built-in layouts.
next_tab Switch focus to the next tab. The window that was previously active
there will be focused.

Args:
    level:
        When subtabs are involved, specifies at which (1-based) tab-
        level the tab-activation should take place.
        Defaults to -1, meaning the nearest tab.
    wrap:
        If True, will cycle back to the fist tab if invoked on the
        last tab.
        Defaults to True.

Examples:
    - layout.next_tab()<br>&nbsp;&nbsp;&nbsp;&nbsp;- layout.next_tab(level=1) # Explicitly activate the next top-most
    tab.
prev_tab Same as next_tab() but switches focus to the previous tab.
focus_nth_tab Switches focus to the nth tab at the specified tab level.

Args:
    n:
        The 1-based index of the tab that should be focused.
    level:
        When there are subtab levels at play, specifies which
        TabContainer's
        tabs among the hierarchy of active TabContainers is being
        acted upon.
        Tab levels are 1-based.
        level=1 indicates outermost/top-level tabs.
        level=-1 (default) indicates the innermost/nearest tabs.

Examples:
    - layout.focus_nth_tab(2) # 2nd top-level tab
    - layout.focus_nth_tab(3, level=-1) # 3rd from nearest tabs
focus_nth_window Switches focus to the nth window.

Counting is always done based on the geospatial position of windows -
ie.
starting from the leftmost+innermost window (ie. we traverse leaves of
the tree, left to right).

Args:
    n:
        The 1-based index of the window in the list of all candidate
        windows.
    ignore_inactive_tabs_at_levels:
        For the specified list of tab levels, only consider windows
        under the active tab at that level, ignoring windows under
        inactive/background tabs.

        eg. [1] means we should start counting n from the first
        window in the currently active level 1 (top-level) tab,
        ignoring windows under inactive tabs. But if there are any
        subtabs under this active tabs, we DO consider the inactive
        windows under background/inactive subtabs.

        eg. [1,2] means we start counting n from the first window
        of the active top-level tab, and if there are any level 2
        subtabs under the active tab, we pick windows only from the
        active level 2 tab as well, ignoring inactive subtabs.

        eg. [] or None (default) means consider every single window
        - even if it's inactive under a background tab.

        eg. [2] means we start counting from the very first window at
        the top level, even if it is inactive under a background tab.
        But whenever there are level 2 subtabs to consider, we only
        count its windows that are under the active level 2 subtab.

    Examples:
        - layout.focus_nth_window(1)
        - layout.focus_nth_window(3,
        ignore_inactive_tabs_at_levels=[1])
        - layout.focus_nth_window(2, ignore_inactive_tabs_at_levels=[1,
        2])
resize Resizes by moving an appropriate border leftwards. Usually this is the
right/bottom border, but for the 'last' node under a SplitContainer, it
will be the left/top border.

Basically the way tmux does resizing.

If there are multiple nested windows under the area being resized,
those windows are resized proportionally.

Args:
    amount:
        The amount by which to resize.

Examples:
    - layout.resize("left", 100)
    - layout.resize("right", 100)
swap Swaps the currently focused window with the nearest window in the
specified direction. If there are multiple candidates to pick from,
then the most recently focused one is chosen.

Args:
    wrap:
        If True, will wrap around the edge and select windows from
        the other end of the screen to swap.
        Defaults to False.
swap_tabs Swaps the currently active tab with the previous tab.

Args:
    wrap:
        If True, will wrap around the edge of the tab bar and swap
        with the last tab.
        Defaults to True.
rename_tab Rename the currently active tab.

Args:
    widget:
        The qtile widget that should be used for obtaining user input
        for the renaming. The 'prompt' widget is used by default.
merge_tabs Merge the currently active tab with another tab, such that both tabs'
contents now appear in 2 splits.

Args:
    direction:
        Which neighbor tab to merge with. Can be either "next" or
        "previous".
    axis:
        The axis along which the merged content should appear as
        splits.

Examples:
    - layout.merge_tabs("previous")
    - layout.merge_tabs("next", "y")
merge_to_subtab Merge the currently focused window (or an ancestor node) with a
neighboring node in the specified direction, so that they both come
under a (possibly new) subtab.

Args:
    direction:
        The direction in which to find a neighbor to merge with.
    src_selection:
        Determines how the source window/node should be resolved. ie.
        do we pick just the current window, or all windows under an
        appropriate ancestor container.
        Valid values are defined in NodeHierarchySelectionMode. See
        below.
    dest_selection:
        Determines how the neighboring node should be resolved, similar
        to how src_selection is resolved.
        Valid values are defined in NodeHierarchySelectionMode. See
        below.
    normalize:
        If True, any removals during the merge process will ensure
        all sibling nodes are resized to be of equal dimensions.

Valid values for NodeHierarchySelectionMode are:
    "mru_deepest":
        Pick a single innermost window. If there are multiple such
        neighboring windows, pick the most recently used (MRU) one.
    "mru_subtab_else_deepest" (default):
        If the target is under a subtab, pick the subtab. If there is
        no subtab in play, behaves like mru_deepest.
    "mru_largest"
        Given a window, pick the largest ancestor node that the
        window's border is a fragment of. This resolves to a
        SplitContainer or a TabContainer.
    "mru_subtab_else_largest"
        If the target is under a subtab, pick the subtab. If there is
        no subtab in play, behaves like mru_largest.

Examples:
    layout.merge_to_subtab( "right",
    dest_selection="mru_subtab_else_deepest", )
    layout.merge_to_subtab( "up", src_selection="mru_deepest",
    dest_selection="mru_deepest", )
push_in Move the currently focused window (or a related node in its hierarchy)
into a neighboring window's container.

Args:
    direction:
        The direction in which to find a neighbor whose container we
        push into.
    src_selection:
        (See docs in merge_to_subtab())
    dest_selection:
        (See docs in merge_to_subtab())
    normalize:
        If True, any removals during the process will ensure all
        sibling nodes are resized to be of equal dimensions.
    wrap:
        If True, will wrap around the edge of the screen and push
        into the container on the other end.

Examples:
- layout.push_in("right", dest_selection="mru_deepest")
- layout.push_in("down", dest_selection="mru_largest", wrap=False)
pull_out Move the currently focused window out from its SplitContainer into an
ancestor SplitContainer at a higher level. It effectively moves a
window 'outwards'.

Args:
    position:
        Whether the pulled out node appears before or after its
        original container node.
        Can be "next" or "previous". Defaults to "previous".
    src_selection:
        Can either be "mru_deepest" (default) or
        "mru_subtab_else_deepest".
        (See docs in merge_to_subtab())
    normalize:
        If True, all sibling nodes involved in the rearrangement are
        resized to be of equal dimensions.

Examples:
    - layout.pull_out()
    - layout.pull_out(src_selection="mru_subtab_else_deepest")
    - layout.pull_out(position="next")
pull_out_to_tab Extract the currently focused window into a new tab at the nearest
TabContainer.

Args:
    normalize:
        If True, any removals during the process will ensure all
        sibling nodes are resized to be of equal dimensions.
normalize Starting from the focused window's container, make all windows in the
container of equal size.

Args:
    recurse:
        If True, then nested nodes are also normalized similarly.
normalize_tab Starting from the focused window's tab, make all windows in the tab of
equal size under their respective containers.

Args:
    recurse:
        If True, then nested nodes are also normalized similarly.
        Defaults to True.
normalize_all Make all windows under all tabs be of equal size under their respective
containers.
toggle_container_select_mode Enable container-select mode where we can select not just a window, but
even their container nodes.

This will activate a special border around the active selection. You
can move its focus around using the same bindings as for switching
window focus. You can also select upper/parent or lower/child nodes
with the select_container_outer() and select_container_inner()
commands.

Handy for cases where you want to split over a collection of windows or
make a new subtab level over a collection of windows.

Aside from focus-switching motions, the only operations supported are
spawn_split() and spawn_tab(). Triggering other commands will
simply exit container-select mode.
select_container_inner When in container-select mode, it will narrow the active selection by
selecting the first descendent node.
select_container_outer When in container-select mode, it will expand the active selection by
selecting the next ancestor node.
tree_repr Returns a YAML-like text representation of the internal tree hierarchy.

BonsaiBar Widget

Option Name Default Value Description
length 500 The standard length property of qtile
widgets.

As usual, it can be a fixed integer, or
one of the 'special' bar constants:
bar.CALCULATED or bar.STRETCH.
sync_with bonsai_on_same_screen The Bonsai layout whose state should be
rendered on this widget.

Can be one of the following:
    - bonsai_with_focus:
        The Bonsai layout of the window
        that is currently focused. This
        is relevant in a multi-screen
        setup - the widget will keep
        updating based on which screen's
        Bonsai layout has focus.
    - bonsai_on_same_screen:
        The widget will stick to
        displaying the state of the
        Bonsai layout that is on the
        same screen as the widget's bar.
bg_color None Background color of the bar.
If None, the qtile-bar's' background
color is used.
font_family Mono Font family to use for tab titles
font_size 15 Size of the font to use for tab titles
tab.width 50 Width of a tab on the bar.

Can be an int or auto. If auto, the
tabs take up as much of the available
space on the bar as possible.

Note that if the length option is set
to bar.CALCULATED, then you cannot
provide auto here, as we would need
fixed tab width values to perform the
bar.CALCULATED computation.

Note that this width follows the 'margin
box'/'principal box' model, so it
includes any configured margin amount.
tab.margin 0 Size of the space on either outer side
of individual tabs.
Can be an int or a list of ints in [top,
right, bottom, left] ordering.
tab.padding 0 Size of the space on either inner side
of individual tabs.
Can be an int or a list of ints in [top,
right, bottom, left] ordering.
tab.bg_color Gruvbox.dull_yellow Background color of the inactive tabs
tab.fg_color Gruvbox.fg1 Foreground color of the inactive tabs
tab.active.bg_color Gruvbox.vivid_yellow Background color of active tab
tab.active.fg_color Gruvbox.bg0_hard Foreground color of active tab
container_select_mode.indicator.bg_color Gruvbox.bg0_hard Background color of active tab when in
container_select_mode.
container_select_mode.indicator.fg_color Gruvbox.bg0_hard Foreground color of active tab when in
container_select_mode.

Support

For any bug reports, please file an issue. For questions/discussions, use the GitHub Discussions section, or you can ask on the qtile subreddit.