Toolbar#
A horizontal strip of buttons, labels, menus, and other widgets — the
multi-purpose chrome primitive. Items are added left-to-right via
add_button(), add_label(), add_menu(), add_divider(),
add_spacer(), and add_widget(). A window’s top region is a stack of
toolbars you build with add_toolbar() (see Toolbars in a window).
Usage#
Build a toolbar by adding items left to right — each add_* call appends one.
add_spacer() splits the bar: items added before it stay on the left, and
everything after it is pushed to the right edge. The same widget serves as a
standalone strip and as the building block of a window’s chrome — a stack of
toolbars (see Toolbars in a window).
Dividers and spacers#
add_divider() inserts a thin vertical rule between item groups.
add_spacer() inserts a flexible gap that pushes everything added after it
to the right side.
tb.add_button("Bold", icon="type-bold")
tb.add_button("Italic", icon="type-italic")
tb.add_divider()
tb.add_button("Align left", icon="text-left")
tb.add_button("Align center", icon="text-center")
tb.add_button("Align right", icon="text-right")
tb.add_spacer()
tb.add_button(icon="gear") # pinned to the right
Labels#
add_label() adds non-interactive text, optionally with an icon. Use it
for the application name or section titles.
tb.add_label("My App", font="heading-md")
tb.add_divider()
tb.add_button("New", icon="file-earmark-plus")
Controlling items after creation#
add_button() returns the Button and add_label()
returns the Label, so you can keep a reference and drive it
later with the widget’s live properties — no need to rebuild the bar.
save = tb.add_button("Save", icon="floppy", on_click=save_doc)
status = tb.add_label("Ready")
save.disabled = True # disable until there are changes
save.text = "Saving…" # update the label text
status.text = "All changes saved"
The bar applies its own density and surface to the items it builds, so a
returned button or label already matches the rest of the toolbar.
Density#
The Toolbar is multi-purpose, so the right density depends on the role:
"default"(the standalonebs.Toolbardefault) — roomier buttons and padding. Use it for a primary command bar where the buttons are the focus."compact"— a tight strip. Use it for window chrome (menu bars, title bars), secondary bars (a rich-text formatting strip), or anywhere vertical space is tight. Items packed on it — buttons, menu triggers, the theme toggle — all follow the bar’s density automatically.
Window chrome added with add_toolbar() defaults to "compact" (window
bars read as tight strips); pass density="default" for a roomier one. A
standalone bs.Toolbar defaults to "default".
tb = bs.Toolbar(horizontal="stretch", density="compact")
tb.add_button(icon="type-bold")
tb.add_button(icon="type-italic")
tb.add_button(icon="type-underline")
tb.add_divider()
tb.add_button(icon="text-left")
tb.add_button(icon="text-center")
tb.add_button(icon="text-right")
tb.add_divider()
tb.add_button(icon="list-ul")
tb.add_button(icon="list-ol")
tb.add_spacer()
tb.add_button(icon="arrow-counterclockwise")
tb.add_button(icon="arrow-clockwise")
Border and surface#
show_border=True draws a border around the toolbar. surface= controls
the background token — 'card' lifts it slightly from the page background.
tb = bs.Toolbar(horizontal="stretch", show_border=True, surface="card")
tb.add_button("New", icon="file-earmark-plus")
tb.add_button("Open", icon="folder2-open")
tb.add_button("Save", icon="floppy")
tb.add_spacer()
tb.add_button(icon="gear")
Custom widgets#
Use add_widget() to embed any widget (a
Select, a
TextField, …). Pass the widget class and let
the toolbar build it — it applies the bar’s density and surface (for any
the class accepts), so the widget matches the rest of the bar:
tb = bs.Toolbar(horizontal="stretch", density="compact")
tb.add_widget(bs.Select, options=["main", "dev", "feat/new-ui"], value="main")
tb.add_widget(bs.TextField, placeholder="Search", width=24)
To add a widget you have already built yourself, parent it onto the bar directly
— bs.MyCustomWidget(parent=tb) — and it attaches automatically, keeping
whatever you configured.
Toolbars in a window#
A window’s top chrome is a stack of toolbars, added with
app.add_toolbar() (the same on Window and AppShell). Each call stacks
a new full-width band, top to bottom; divider=True draws a hairline beneath a
band. The returned Toolbar is a scoping context manager — with
app.add_toolbar() as bar: reads naturally and hands back the handle, but you
fill the bar only through its add_* methods (so each item inherits the bar’s
density and surface).
with bs.AppShell(title="My App") as shell:
# A menu row.
with shell.add_toolbar() as menus:
with menus.add_menu("File") as file:
file.add_action("Quit", shortcut="Mod+Q", on_click=shell.close)
with menus.add_menu("View") as view:
view.add_action("Refresh", shortcut="Mod+R", on_click=refresh)
menus.add_spacer()
menus.add_theme_toggle()
# A command row beneath it, separated by a hairline.
with shell.add_toolbar(divider=True) as commands:
commands.add_button("Run", icon="play", accent="primary", on_click=run)
Window chrome defaults to surface='chrome' and density='compact'; override
either per call.
Window controls#
show_window_controls=True adds minimize / maximize / close at the right edge
and lets the toolbar drag the window (double-click maximizes). This is how you
build the title bar of an undecorated window — see the Undecorated window
section on the App page for the full example and screenshot.
Widget sizing#
All widgets accept self-placement kwargs via **kwargs. The parent
container determines which options apply — Column / Row parents use
the layout kwargs below, grid-based parents use grid kwargs.
Column (vertical layout)
Used inside a Column, App, or any other container with a column layout.
Children are arranged top-to-bottom, so horizontal aligns each child across
the width and grow shares the vertical space. (vertical does not apply —
the order of the children sets their top-to-bottom position.)
|
Cross-axis placement of the widget: |
|
Claim and fill a share of the leftover vertical space (the layout
direction). |
|
External spacing in pixels. Accepts an integer (equal on all
sides), a 2-tuple |
|
Horizontal external spacing (left and right). Accepts an integer
or a 2-tuple |
|
Vertical external spacing (top and bottom). Accepts an integer
or a 2-tuple |
Row (horizontal layout)
Used inside a Row or any other container with a row layout. Children are
arranged left-to-right, so vertical aligns each child across the height and
grow shares the horizontal space. (horizontal does not apply — the order
of the children sets their left-to-right position.)
|
Cross-axis placement of the widget: |
|
Claim and fill a share of the leftover horizontal space (the layout
direction). |
|
External spacing in pixels. Accepts an integer (equal on all
sides), a 2-tuple |
|
Horizontal external spacing (left and right). Accepts an integer
or a 2-tuple |
|
Vertical external spacing (top and bottom). Accepts an integer
or a 2-tuple |
Grid
Used inside a Grid container.
|
Zero-based row and column indices. |
|
Number of rows or columns to span. |
|
Horizontal placement within the grid cell: |
|
Vertical placement within the grid cell: |
|
External spacing in pixels. Accepts an integer, a 2-tuple
|
|
Horizontal external spacing. Accepts an integer or |
|
Vertical external spacing. Accepts an integer or |
See also#
AppShell —
full application scaffold with a built-in toolbar, sidebar, and page stack.
Button —
standalone button widget.
API#
The complete reference for Toolbar lives on the
Widgets API page. At a glance:
A horizontal strip of buttons, labels, and other widgets. |
Full Example#
1
2with bs.App(title="Toolbar demo", minsize=(700, 300), padding=16) as app:
3
4 with bs.Column(grow=True, horizontal="stretch", horizontal_items="stretch", gap=16):
5
6 # ── Default density ────────────────────────────────────────────────
7 with bs.Column(horizontal_items="stretch", padding=(16, 12, 16, 4)):
8 bs.Label("Default", font="heading-sm")
9
10 tb1 = bs.Toolbar(horizontal="stretch")
11 tb1.add_label("Editor", font="heading-md")
12 tb1.add_divider()
13 tb1.add_button("New", icon="file-earmark-plus")
14 tb1.add_button("Open", icon="folder2-open")
15 tb1.add_button("Save", icon="floppy")
16 tb1.add_spacer()
17 tb1.add_theme_toggle()
18 tb1.add_button(icon="gear")
19
20 # ── Compact density ────────────────────────────────────────────────
21 with bs.Column(horizontal_items="stretch", padding=(16, 12, 16, 4)):
22 bs.Label("Compact density", font="heading-sm")
23
24 tb2 = bs.Toolbar(horizontal="stretch", density="compact")
25 tb2.add_button(icon="type-bold")
26 tb2.add_button(icon="type-italic")
27 tb2.add_button(icon="type-underline")
28 tb2.add_divider()
29 tb2.add_button(icon="text-left")
30 tb2.add_button(icon="text-center")
31 tb2.add_button(icon="text-right")
32 tb2.add_divider()
33 tb2.add_button(icon="list-ul")
34 tb2.add_button(icon="list-ol")
35 tb2.add_spacer()
36 tb2.add_button(icon="arrow-counterclockwise")
37 tb2.add_button(icon="arrow-clockwise")
38
39 # ── Accents and variants ───────────────────────────────────────────
40 with bs.Column(horizontal_items="stretch", padding=(16, 12, 16, 4)):
41 bs.Label("Accents and variants", font="heading-sm")
42 tb3 = bs.Toolbar(horizontal="stretch")
43 tb3.add_button("Publish", icon="cloud-upload", accent="primary")
44 tb3.add_button("Preview", icon="eye")
45 tb3.add_button("Draft", icon="pencil")
46 tb3.add_spacer()
47 tb3.add_button("Discard", icon="trash", accent="danger")
48
49 # ── Border & Surface ────────────────────────────────────────────────
50 with bs.Column(horizontal_items="stretch", padding=(16, 12, 16, 4)):
51 bs.Label("Border and Surface", font="heading-sm")
52
53 tb1 = bs.Toolbar(horizontal="stretch", show_border=True, surface="card")
54 tb1.add_button("New", icon="file-earmark-plus")
55 tb1.add_button("Open", icon="folder2-open")
56 tb1.add_button("Save", icon="floppy")
57
58
59app.run()