Skip to content

toolbar: Add Toolbar and ToolbarGroup components - #3128

Open
bipinnatus2k wants to merge 11 commits into
longbridge:mainfrom
bipinnatus2k:feat/toolbar
Open

bipinnatus2k wants to merge 11 commits into
longbridge:mainfrom
bipinnatus2k:feat/toolbar

Conversation

@bipinnatus2k

Copy link
Copy Markdown

Description

Adds a Toolbar component: a themed bar that hosts a row of actions, usually placed at the top of a window or pane. It pairs with TitleBar above it and StatusBar below, and follows the same left/center/right region contract as StatusBar.

  • gpui-base owns the behavior. A Toolbar primitive with roving arrow-key focus across its items, and a ToolbarGroup semantic subgroup so a toolbar can separate clusters of actions and give a cluster a label.
  • gpui-component owns the presentation. A themed Toolbar with left / right regions and Sizable support (xs, sm, md, lg), plus the new toolbar.background / toolbar.border theme tokens. The tokens follow the status_bar precedent: they fall back to the title bar background and border, so existing themes pick up sane values without per-theme edits.
  • Story and docs. The gallery gains a Toolbar story covering the regions, group separators and sizes, and English plus Chinese documentation is added under website/component/.

Public API

gpui-base (gpui_base::toolbar, re-exported as gpui_base::Toolbar and gpui_base::ToolbarGroup)

  • Toolbar::new(id: impl Into<ElementId>) -> Self — create an empty toolbar.
  • Toolbar::axis(axis: Axis) -> Self — lay the bar out horizontally (default) or vertically.
  • Toolbar::disabled(disabled: bool) -> Self — cascade the disabled state to the items.
  • Implements Styled, ParentElement, InteractiveElement, StatefulInteractiveElement, RenderOnce.
  • ToolbarGroup::new(id: impl Into<ElementId>) -> Self — create an empty semantic subgroup.
  • ToolbarGroup::label(label: impl Into<SharedString>) -> Self — optional group label.
  • Implements Styled, ParentElement, InteractiveElement, StatefulInteractiveElement, RenderOnce.

gpui-component (gpui_component::toolbar)

  • Toolbar::new(id: impl Into<ElementId>) -> Self — create a themed toolbar at Size::Medium.
  • Toolbar::left(child: impl IntoElement) -> Self — append an element to the leading region.
  • Toolbar::right(child: impl IntoElement) -> Self — append an element to the trailing region.
  • Implements ParentElement, Styled, Sizable, RenderOnce.
  • New theme tokens: ThemeColor::toolbar (toolbar.background) and ThemeColor::toolbar_border (toolbar.border), with light and dark defaults in default-theme.json.

Breaking Changes

None; the change is additive.

How to Test

  • cargo check --workspace
  • cargo fmt --all -- --check
  • cargo run, then open the Toolbar story to try the regions, group separators and sizes

AI Assistance

The component was originally implemented with GLM (ZCode) assistance (see the Co-authored-by trailers in the commits). This submission was rebased onto the current main and verified with an agent.

bipinnatus2k and others added 11 commits September 18, 2026 22:12
An unstyled container that groups controls and owns the roving-focus
contract of an ARIA toolbar: `Role::Toolbar` with orientation, and
arrow keys that move focus among focusable descendants, wrapping at
the ends. Traversal walks the rendered tab stops constrained to the
container subtree, the same approach `Root` uses for focus traps, so
it works with any focusable children without their cooperation.

The container itself is not a tab stop. A non-wrapping mode would
need to tell a wrapped step from an ordinary one, which GPUI's public
tab-stop API cannot do, so wrapping is always on.

Co-authored-by: GLM (ZCode) <noreply@z.ai>
Follows the status_bar precedent: `toolbar.background` falls back to
the title bar background and `toolbar.border` to the title bar border,
so existing themes pick up sane values without per-theme edits.

Co-authored-by: GLM (ZCode) <noreply@z.ai>
A themed command bar with left/right pinned regions and a middle
region whose alignment follows the pinned ends, matching StatusBar's
region contract. Sizes via `Sizable` scale bar height, spacing, and
text together (28/32/40/48 px).

Keyboard roving and toolbar semantics come from `gpui_base::Toolbar`;
the styled layer only adds tokens, regions, and sizing. Each instance
takes an explicit id, which keeps its focus state stable across
frames.

Co-authored-by: GLM (ZCode) <noreply@z.ai>
Documents three sections: a document command bar layout, the four
sizes (with hosted buttons matching each bar size), and the region
alignment cases for the dynamic middle.

Co-authored-by: GLM (ZCode) <noreply@z.ai>
Bilingual component page covering regions, sizes, keyboard roving
focus, theming, and the API reference, plus entries in both component
indexes.

Co-authored-by: GLM (ZCode) <noreply@z.ai>
An unstyled container that groups a run of related toolbar items and
carries an accessible name, so assistive technology reads them as one
unit. The surrounding toolbar's roving arrow-key focus traverses
group items like direct children, since containment follows the
element tree.

Unlike Base UI's Toolbar.Group, the group cannot disable its
children: that API propagates through React context into Base UI's
own button primitives, which has no equivalent for arbitrary GPUI
children, and the a11y layer exposes no disabled state for a
container node.

Co-authored-by: GLM (ZCode) <noreply@z.ai>
Surface the base group through the styled toolbar module so
applications reach it next to Toolbar.

Co-authored-by: GLM (ZCode) <noreply@z.ai>
Group the history buttons under an accessible "History" label in the
document toolbar example.

Co-authored-by: GLM (ZCode) <noreply@z.ai>
Covers grouping with an accessible name, the spacing note, and the
boundary that groups cannot disable their children in GPUI.

Co-authored-by: GLM (ZCode) <noreply@z.ai>
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.

1 participant