Skip to content

TitleBar

Header: winui/ui/hosting/TitleBar.hpp. Spec: winui/ui/hosting/TitleBar.hpp.

A chrome row with an icon, title and subtitle, optional back and pane-toggle (hamburger) buttons, and three content slots (left header, content, right header).

The bar itself takes no focus. The back and pane-toggle buttons (subtle Buttons) and whatever sits in the slots are each their own Tab stop. Use it either as a row at the top of a page, or as the window's caption in a window with WindowStyle::extend_content_into_title_bar. In a window, the bar follows it: it keeps the right header clear of the space Window::caption_insets() reports for the caption buttons, and dims while Window::active() is false. hit_region() then tells the host which parts drag the window. set_caption_insets() and set_active() override what the window says, for a bar drawn somewhere other than its own window's caption. The bar has no minimize, maximize or close buttons, since the window keeps its own. It doesn't set the window's title from title(), so an app sets both.

Example:

ui::Property<bool>                can_go_back{false};
ui::Control<ui::NavigationFrame>  frame;
ui::Control<ui::NavigationShell>  nav;

window.set_content(ui::VerticalStack{.content = {
    ui::TitleBar{
        .title                      = "Contoso Notes",
        .subtitle                   = "Preview",
        .icon                       = ui::IconGlyph{0xE70B},
        .back_button_visible        = can_go_back,
        .pane_toggle_button_visible = true,
        .content_alignment          = ui::Alignment::Stretch,
        .on_back_requested          = [&] { frame->go_back(); },
        .on_pane_toggle_requested   = [&] { nav->set_pane_open(!nav->is_pane_open()); },
        .content                    = ui::TextBox{.placeholder_text = "Search", .max_width = 360.0f},
    },
    // ... the page below the bar ...
}});
// Run the window with ui::WindowOptions{.style = {.extend_content_into_title_bar = true}} to make
// the bar the caption; without it the bar is an ordinary row under the standard title bar.

ui::TitleBar

A horizontal chrome row: icon, title, subtitle, an optional back/pane-toggle button, and three arbitrary content slots. Builds a ui::TitleBarControl.

Example:

using ui::operator""_fr;
ui::Control<ui::NavigationFrame> frame;

window.set_content(ui::Grid{
    .rows    = {ui::AutoSize, 1_fr},
    .content = {
        ui::Cell{.row     = 0,
                 .content = ui::TitleBar{
                     .title               = "Contoso Mail",
                     .subtitle            = "Preview",
                     .icon                = ui::symbol_glyph(ui::Symbol::Mail),
                     .back_button_visible = true,
                     .on_back_requested   = [&] { frame->go_back(); },
                     .content             = ui::AutoSuggestBox{.placeholder_text = "Search", .width = 300.0f},
                 }},
        ui::Cell{.row = 1, .content = ui::NavigationFrame{.control = frame}},
    },
});

Every field, in the order a designated initializer names them:

ui::TitleBar{
    .title, .subtitle, .icon, .back_button_visible, .back_button_enabled,
    .pane_toggle_button_visible, .caption_insets, .active, .content_alignment, .style, .margin,
    .width, .height, .min_width, .min_height, .max_width, .max_height, .horizontal_alignment,
    .vertical_alignment, .visible, .tool_tip, .enabled, .accessibility, .on_back_requested,
    .on_pane_toggle_requested, .on_pointer_entered, .on_pointer_exited, .on_got_focus,
    .on_lost_focus, .on_key_down, .on_wheel, .control, .left_header_content, .content,
    .right_header_content
}

Fields, in declaration order (designators must follow it):

Field Type Default
title ui::Binding<std::string> Title: the window's name, after the icon.
subtitle ui::Binding<std::string> Subtitle: secondary text after the title, in the caption style.
icon ui::Binding<ui::IconSource> The icon left of the title: a Symbol, a glyph, a path or a picture; empty shows none.
back_button_visible ui::Binding<bool> {false} Show the back button at the far left.
back_button_enabled ui::Binding<bool> {true} False shows the back button grayed out.
pane_toggle_button_visible ui::Binding<bool> {false} Show the hamburger button (typically for a NavigationShell).
caption_insets ui::Binding<std::optional<ui::CaptionInsets>> The caption space to keep clear of the window's caption buttons. Unset follows the window the bar is in (Window::caption_insets()); a value or a binding wins, for a bar that is not its own window's caption.
active ui::Binding<std::optional<bool>> false dims the bar. Unset follows the window the bar is in (Window::active()); a value or a binding wins.
content_alignment ui::Binding<ui::Alignment> {ui::Alignment::Center} Where the content sits between the title and the right header (ui::TitleBarControl::set_content_alignment()).
on_back_requested ui::Callback<void()> The back button was clicked.
on_pane_toggle_requested ui::Callback<void()> The pane-toggle button was clicked.
on_pointer_entered ui::Action The pointer moved onto the control.
on_pointer_exited ui::Action The pointer left the control.
on_got_focus ui::Action Keyboard focus arrived on the control, or on anything inside it, from outside it. A control with nothing focusable in it never gets focus, and never raises this.
on_lost_focus ui::Action Keyboard focus left the control and everything inside it.
on_key_down ui::Callback<ui::Handling(const ui::KeyEvent&)> A key pressed while focus is on the control or inside it, before the control's own handling; ui::Handled stops it there (Element::on_key_down()).
on_wheel ui::Callback<ui::Handling(const ui::PointerEvent&)> A wheel turn over the control, before the control scrolls; ui::Handled stops it there (Element::on_wheel()).
control ui::ControlOut<ui::TitleBar> Out: the handle that receives the built control.
left_header_content ui::Spec The left slot, next to the buttons and title.
content ui::Spec Content: the middle slot, placed per content_alignment.
right_header_content ui::Spec The right slot; kept clear of the caption buttons via caption_insets.

Plus the common fields style, margin, width, height, min_width, min_height, max_width, max_height, horizontal_alignment, vertical_alignment, visible, tool_tip, enabled, accessibility every spec carries (conventions).

build

std::unique_ptr<Element> build()&&

Consumes the spec and returns the built ui::TitleBarControl; the caller owns it.

ui::TitleBarControl

Derives from ui::Element.

A chrome row with an icon, title and subtitle, optional back and pane-toggle (hamburger) buttons, and three content slots (left header, content, right header).

The bar itself takes no focus. The back and pane-toggle buttons (subtle Buttons) and whatever sits in the slots are each their own Tab stop. Use it either as a row at the top of a page, or as the window's caption in a window with WindowStyle::extend_content_into_title_bar. In a window, the bar follows it: it keeps the right header clear of the space Window::caption_insets() reports for the caption buttons, and dims while Window::active() is false. hit_region() then tells the host which parts drag the window. set_caption_insets() and set_active() override what the window says, for a bar drawn somewhere other than its own window's caption. The bar has no minimize, maximize or close buttons, since the window keeps its own. It doesn't set the window's title from title(), so an app sets both.

Example:

ui::Property<bool>                can_go_back{false};
ui::Control<ui::NavigationFrame>  frame;
ui::Control<ui::NavigationShell>  nav;

window.set_content(ui::VerticalStack{.content = {
    ui::TitleBar{
        .title                      = "Contoso Notes",
        .subtitle                   = "Preview",
        .icon                       = ui::IconGlyph{0xE70B},
        .back_button_visible        = can_go_back,
        .pane_toggle_button_visible = true,
        .content_alignment          = ui::Alignment::Stretch,
        .on_back_requested          = [&] { frame->go_back(); },
        .on_pane_toggle_requested   = [&] { nav->set_pane_open(!nav->is_pane_open()); },
        .content                    = ui::TextBox{.placeholder_text = "Search", .max_width = 360.0f},
    },
    // ... the page below the bar ...
}});
// Run the window with ui::WindowOptions{.style = {.extend_content_into_title_bar = true}} to make
// the bar the caption; without it the bar is an ordinary row under the standard title bar.

TitleBarControl

TitleBarControl(std::string title = "", std::string subtitle = "")

A bar showing title and, beside it in the secondary text color, subtitle. Both buttons start hidden, the slots empty.

title

const std::string& title() const

The primary caption text. Empty draws none.

set_title

void set_title(std::string title)

Replaces the title and updates the layout. Does not change the window's title.

subtitle

const std::string& subtitle() const

Dimmed secondary text after the title. Empty draws none.

set_subtitle

void set_subtitle(std::string subtitle)

Replaces the subtitle and updates the layout.

icon

const IconSource& icon() const

The icon left of the title: a glyph at 16 DIPs in the title text's color, or a path or picture fitted into the same 16x16 box. Empty (the default) shows none.

set_icon

void set_icon(IconSource icon)

Shows icon; an empty source removes it and the title moves left into the freed space. Updates the layout.

content_alignment

Alignment content_alignment() const

Where the content sits in the space between the title and the right header. Center, the default, keeps the content at its own width, centered. Stretch hands it the whole space, which a search box with a max width wants.

set_content_alignment

void set_content_alignment(Alignment alignment)

Changes the content slot's alignment and updates the layout.

is_back_button_visible

bool is_back_button_visible() const

Whether the back button is shown; false by default. A hidden button takes no space, is not a Tab stop and is not hit-tested.

set_back_button_visible

void set_back_button_visible(bool visible)

Shows or hides the back button and updates the layout.

is_back_button_enabled

bool is_back_button_enabled() const

Whether the back button is enabled; true by default. A disabled back button stays visible but grayed, cannot be clicked, and drags the window when the bar is the caption.

set_back_button_enabled

void set_back_button_enabled(bool enabled)

Enables or disables the back button (ButtonControl::set_enabled()); a disabled bar keeps it disabled either way.

on_back_requested

void on_back_requested(Callback<void()> handler)

Fired on a completed click (or Enter/Space) of the back button. It cannot fire while the button is hidden, since a hidden button is not hit-tested. Replaces any previous handler.

is_pane_toggle_button_visible

bool is_pane_toggle_button_visible() const

Whether the pane-toggle button is shown; false by default. Typically paired with a NavigationShell whose own pane button is hidden.

set_pane_toggle_button_visible

void set_pane_toggle_button_visible(bool visible)

Shows or hides the pane-toggle button and updates the layout.

on_pane_toggle_requested

void on_pane_toggle_requested(Callback<void()> handler)

Fired when the pane-toggle button is clicked. The bar keeps no pane state of its own; the handler opens or closes the pane. Replaces any previous handler.

set_left_header_content

template <typename T>
T* set_left_header_content(std::unique_ptr<T> element)

The left header: the slot after the buttons and before the icon. Each slot setter takes ownership of an arbitrary Element, replaces (and destroys) what was there, and hands back a non-owning observer, the same shape as ExpanderControl::set_content().

left_header_content

Element* left_header_content() const

The left header, or null when the slot is empty. Owned by the bar.

set_content

template <typename T>
T* set_content(std::unique_ptr<T> element)

The content: the middle slot, between the title and the right header, placed per content_alignment(), typically a search box.

content

Element* content() const

The middle slot's element, or null. Owned by the bar.

set_right_header_content

template <typename T>
T* set_right_header_content(std::unique_ptr<T> element)

The right header: the slot at the far end, kept clear of the caption buttons by caption_insets(), typically a PersonPicture or a settings button.

right_header_content

Element* right_header_content() const

The right header, or null. Owned by the bar.

caption_insets

const CaptionInsets& caption_insets() const

The caption space the bar keeps free, in DIPs: the value set with set_caption_insets(), otherwise what the window the bar is in reports (Window::caption_insets()), all zero outside a window. All zero is a TitleBar inside the content. Anything else also keeps a 48 DIP strip free at the right for dragging the window.

set_caption_insets

void set_caption_insets(std::optional<CaptionInsets> insets)

The caption space to leave alone, in place of the window's. std::nullopt (the default) follows the window again. A change updates the layout.

active

bool active() const

Whether the bar draws as an active window's caption: the value set with set_active(), otherwise Window::active() of the window the bar is in, true outside a window.

set_active

void set_active(std::optional<bool> active)

false dims the bar (to half opacity) the way a deactivated window's caption is dimmed, whatever the window says; std::nullopt (the default) follows the window again. Redraws only on a change.

hit_region

HitRegion hit_region(Point p) const override

Caption over the bar's own background. The visible back button (while enabled), the pane toggle and both headers take the press; in the content, the enabled focusable controls do and everything else drags, subject to Element::title_bar_drag_region().

for_each_child

void for_each_child(ChildVisitor visit) const override

Lists the back and pane-toggle buttons while they show, then the left header, the content and the right header when present. The icon is decoration, not a child: no tab stop, no popups, no automation node.

automation_role

AutomationRole automation_role() const override

UI Automation's Header control type.

automation_name_fallback

std::string automation_name_fallback() const override

The title, when no explicit accessible name is set.

measure_override

Size measure_override(Size available, const MeasureContext& context) const override

Implement these instead of measure() and arrange(). The base class has already applied margin, size limits and alignment by the time these run. measure_override() is pure (every concrete Element must size itself); arrange_override() defaults to a no-op since most leaves need nothing beyond having bounds() set, which the base already did.

arrange_override

void arrange_override(const Rect& final_rect, const ArrangeContext& context) override

Position children within the rect arrange() settled on (also in bounds(), window DIPs). Default: nothing, for a leaf.

render_override

void render_override(Renderer& renderer, const RenderContext& context) const override

What the control draws, in layout coordinates; render() has already applied the visual layer. A container draws its children through their render(), never their render_override().

attached

void attached() override

The element joined a window's tree: window() is now that window. Called for the root a window takes and for everything inside it, and for a subtree added to a tree already in a window. No-op by default.

detached

void detached() override

The element left a window's tree (moved out of it). No-op by default.

enabled_changed

void enabled_changed() override

is_enabled() flipped, through set_enabled() here or on an ancestor, or a move to another parent. Drop any press or hover state. The element is redrawn either way. No-op by default.

Tests

  • tests/test_title_bar.cpp