# toolbar — `u_pbt_toolbar` #

← Component reference · Guide contents

> Flat toolbar: several bars spread over bands, buttons, toggles, drop-down menus, input fields, automatic overflow and drag-by-handle repositioning.

> ▶ See it live — Demo application, Toolbar tile: the preview, the code behind it and this page, side by side.

## At a glance #

Userobject | `u_pbt_toolbar`
Item classes | `n_pbt_toolbar_bar` (bar) → `n_pbt_toolbar_item` (tool)
Used for | Replacing a classic toolbar with a modern, themed one spread over several rearrangeable rows
Height | Intrinsic: the bar always fits itself to its rows, nothing to enable — see Automatic height

## Quick start #

    // open event of the window
    n_pbt_toolbar_bar lnv_bar

    // of_bar() returns the default bar, always present (id = MAIN_BAR)
    lnv_bar = uo_toolbar.of_bar(uo_toolbar.MAIN_BAR)

    lnv_bar.of_add_button(/*id*/ "save", /*text*/ "", /*image*/ "mono:img\save.svg", /*tooltip*/ "Save")
    lnv_bar.of_add_button(/*id*/ "find", /*text*/ "", /*image*/ "mono:img\find.svg", /*tooltip*/ "Find")
    lnv_bar.of_add_separator()
    lnv_bar.of_add_button(/*id*/ "help", /*text*/ "Help",  /*image*/ "", /*tooltip*/ "About")

    // ue_clicked event of uo_toolbar : (string as_bar, string as_id)
    choose case as_bar + "/" + as_id
        case "main/save" ; of_enregistrer()
        case "main/find" ; of_rechercher()
        case "main/help" ; of_aide()
    end choose

## The model: bars, bands, tools #

A toolbar contains one or more bars. Each bar sits on a band (a row) and occupies a position within that row: two bars on the same band share the row, two bars on different bands stack up — exactly like classic Office toolbars.

Tools (buttons, toggles, menus, fields) belong to a bar. Their identifier is only unique inside their own bar, so access always goes through the full path.

    uo_toolbar.of_bar("main").of_item("save").ib_enabled = false

Additions live on the parent handle too: you add a tool to a bar, not to the toolbar. See Shared foundation · Items for the general principle.

## Properties #

Property | Type | Default | Purpose
--- | --- | --- | ---
`is_theme_style` | string | `fluent` | Visual style of the component (`THEME_STYLE_*` constants)
`is_theme_mode` | string | `light` | Light or dark variant (`THEME_MODE_*` constants)
`il_theme_accent` | long | `-1` | Accent color of this component (`-1` = the theme accent)
`is_tooltip` | string | `""` | Simple tooltip shown when hovering the component
`is_super_tooltip_title` | string | `""` | Title of the rich tooltip (takes precedence over `is_tooltip`)
`is_super_tooltip_text` | string | `""` | Text of the rich tooltip (rich markup accepted)
`is_super_tooltip_image` | string | `""` | Image of the rich tooltip
`ib_reorderable` | boolean | `false` | Lets the user rearrange the bars with the mouse: the move grips appear and a bar can be dragged to another band. Off by default — a toolbar taken apart by accident is a support call. Your code rearranges them either way (`of_set_layout`, `of_add_bar`): the switch restrains the user, never the application

### Constant #

Constant | Value | Purpose
--- | --- | ---
`MAIN_BAR` | `"main"` | Identifier of the default bar, always available without having to create it

## Methods #

Method | Purpose
--- | ---
`of_bar (string as_id)` → `n_pbt_toolbar_bar` | Returns the handle of a bar (created on first access)
`of_add_bar (string as_bar)` → `n_pbt_toolbar_bar` | Adds a bar and returns its handle
`of_add_bar (string as_bar, integer ai_band, integer ai_index)` → `n_pbt_toolbar_bar` | Adds a bar at a specific band and position (both counted from 0)
`of_remove_bar (string as_id)` | Removes one bar and its tools. The others keep their live state, and the bands close ranks behind the one that leaves. This is not hiding it (`ib_visible` on its handle): hiding keeps it in the model, ready to come back exactly where it was; removing forgets it
`of_get_layout ()` | Reads the current arrangement back as JSON: which bar on which band, at which rank, and visible or not. Store it, then hand it back with `of_set_layout`
`of_set_layout (string as_layout_json)` | Restores a bar layout previously received through `ue_layout_changed`
`of_clear ( )` | Removes every bar and every tool
`of_reset ( )` | Clears the content and restores the properties to their defaults

### On a bar — `n_pbt_toolbar_bar` #

Member | Type | Default | Purpose
--- | --- | --- | ---
`ii_band` | integer | `0` | Band (row) carrying the bar; `0` = first row
`ii_index` | integer | `0` | Position of the bar within its band
`ib_visible` | boolean | `true` | Shows or hides one bar. A hidden bar keeps its band, its rank and its items: showing it again puts it back exactly where it was, with nothing to rebuild — that is the difference with `of_clear`, which would lose them
`of_item (string as_id)` → `n_pbt_toolbar_item` |  |  | Returns the handle of a tool of this bar
`of_add_button (as_id, as_text, as_image, as_tooltip)` → `n_pbt_toolbar_item` |  |  | Action button; clicking it raises `ue_clicked`
`of_add_toggle (as_id, as_text, as_image, as_tooltip)` → `n_pbt_toolbar_item` |  |  | Two-state toggle; clicking it raises `ue_toggled`
`of_add_dropdown (as_id, as_text, as_image, as_tooltip)` → `n_pbt_toolbar_item` |  |  | Drop-down menu button; picking an entry raises `ue_menu_selected`
`of_add_label (as_id, as_text)` → `n_pbt_toolbar_item` |  |  | Informational label, not clickable
`of_add_textbox (as_id, as_text, ai_width, as_tooltip)` → `n_pbt_toolbar_item` |  |  | Input field; Enter or loss of focus raises `ue_text_changed`. `ai_width` in pixels
`of_add_datepicker (as_id, as_date, as_tooltip)` → `n_pbt_toolbar_item` |  |  | Date picker; picking a date raises `ue_date_changed`. Dates in `"yyyy-mm-dd"` format
`of_add_separator ( )` |  |  | Vertical separator rule
`of_insert_button (as_id, as_text, as_image, as_tooltip, ai_index)` → `n_pbt_toolbar_item` |  |  | Button added at the position you choose (first = 0) instead of at the end
`of_move_item (string as_id, integer ai_index)` |  |  | Moves a tool to another position within this bar (first = 0). It keeps its kind, its menu and its live state: the tool changes place, it is not rebuilt. Works for every kind, so the `of_add_*` without an `of_insert_*` insert by adding then moving
`of_remove_item (string as_id)` |  |  | Removes one tool from this bar. The bar stays, and so does everything else in it, live state included — only the named tool goes. To make it come and go instead, `ib_visible` on its handle keeps it in the model

### On a tool — `n_pbt_toolbar_item` #

Member | Type | Default | Purpose
--- | --- | --- | ---
`ib_visible` | boolean | `true` | Shows or hides one item. It keeps its rank in the bar: showing it again puts it back exactly where it was, not at the end. Not to be confused with `ib_enabled`, which leaves it in place, greyed — hide what does not apply, grey what is momentarily unavailable
`ib_enabled` | boolean | `true` | Tool active or grayed out
`ib_checked` | boolean | `false` | State of a toggle (no effect on the other types)
`is_text` | string | `""` | Label, rich markup accepted
`is_shortcut` | string | `""` | Keyboard shortcut (`"Ctrl+S"`), shown in the tooltip and active
`of_add_menu_item (as_id, as_label, as_image)` |  |  | Entry in the menu of a drop-down button
`of_add_menu_item (as_parent, as_id, as_label, as_image)` |  |  | Cascading entry, filed under an existing entry
`of_add_menu_separator (as_id)` |  |  | Separator rule inside the menu

Images accept every form the library recognizes: path, `mono:`, `tint:`, DLL resource.

## Events #

Event | Raised when
--- | ---
`ue_clicked (string as_bar, string as_id)` | A button is clicked (or triggered by its shortcut)
`ue_toggled (string as_bar, string as_id, boolean ab_checked)` | A toggle changes state; `ab_checked` carries the new state
`ue_menu_selected (string as_bar, string as_dropdown, string as_id)` | A drop-down menu entry is picked
`ue_text_changed (string as_bar, string as_id, string as_text)` | An input field is validated (Enter or loss of focus)
`ue_date_changed (string as_bar, string as_id, string as_date)` | A date is picked (`"yyyy-mm-dd"`)
`ue_bar_reordered (string as_id, integer ai_band, integer ai_index)` | The user dragged a bar by its grip to another band or another rank (needs `ib_reorderable`). Says which bar moved and where it landed, where `ue_layout_changed` carries the whole arrangement. Band and rank start at 1
`ue_layout_changed (string as_layout_json)` | The arrangement changed — the user dragged a bar by its grip, or your own code moved or hid one. Carries the whole layout, not just what moved
`ue_auto_height (long al_height)` | The ideal height has changed and the bar has adjusted to it — always active: the number of rows dictates the height
`ue_ready ( )` | The component has finished loading; everything sent beforehand has been replayed
`ue_runtime_missing ( )` | The WebView2 runtime is missing: the component stays empty
`ue_bg_color (long al_color)` | The component has computed its theme background color; the userobject has already adopted it (`backcolor`)

## Examples #

### Pre-checked toggles and a grayed-out tool #

    n_pbt_toolbar_bar lnv_bar
    lnv_bar = uo_toolbar.of_bar(uo_toolbar.MAIN_BAR)

    lnv_bar.of_add_toggle(/*id*/ "bold",   /*text*/ "", /*image*/ "mono:img\bold.svg",   /*tooltip*/ "Bold")
    lnv_bar.of_add_toggle(/*id*/ "italic", /*text*/ "", /*image*/ "mono:img\italic.svg", /*tooltip*/ "Italic")
    lnv_bar.of_add_separator()
    lnv_bar.of_add_button(/*id*/ "undo",   /*text*/ "", /*image*/ "mono:img\undo.svg",   /*tooltip*/ "Undo")

    // Every tool is driven through its handle, obtained from ITS bar
    lnv_bar.of_item("bold").ib_checked = true    // toggle checked up front
    lnv_bar.of_item("undo").ib_enabled = false   // nothing to undo yet

    // ue_toggled event of uo_toolbar
    if as_id = "bold" then of_appliquer_gras(ab_checked)

### Cascading drop-down menu #

    n_pbt_toolbar_bar  lnv_bar
    n_pbt_toolbar_item lnv_menu

    lnv_bar = uo_toolbar.of_bar(uo_toolbar.MAIN_BAR)

    // The menu is filled through the handle returned by of_add_dropdown
    lnv_menu = lnv_bar.of_add_dropdown(/*id*/ "export", /*text*/ "Export", &
                                       /*image*/ "mono:img\save.svg", /*tooltip*/ "Export the file")
    lnv_menu.of_add_menu_item(/*id*/ "exp_pdf", /*label*/ "PDF",  /*image*/ "")
    lnv_menu.of_add_menu_item(/*id*/ "exp_csv", /*label*/ "CSV",  /*image*/ "")
    lnv_menu.of_add_menu_separator(/*id*/ "sep1")

    // Submenu : name the parent entry as the first argument
    lnv_menu.of_add_menu_item(/*id*/ "exp_img", /*label*/ "Image...", /*image*/ "")
    lnv_menu.of_add_menu_item(/*parent*/ "exp_img", /*id*/ "exp_png", /*label*/ "PNG", /*image*/ "")
    lnv_menu.of_add_menu_item(/*parent*/ "exp_img", /*id*/ "exp_jpg", /*label*/ "JPEG", /*image*/ "")

    // ue_menu_selected event of uo_toolbar : (as_bar, as_dropdown, as_id)
    if as_dropdown = "export" then of_exporter(as_id)

### Search field and date picker #

    n_pbt_toolbar_bar lnv_bar
    lnv_bar = uo_toolbar.of_bar(uo_toolbar.MAIN_BAR)

    lnv_bar.of_add_button(/*id*/ "save", /*text*/ "", /*image*/ "mono:img\save.svg", /*tooltip*/ "Save")
    lnv_bar.of_item("save").is_shortcut = "Ctrl+S"    // shortcut active and displayed
    lnv_bar.of_add_separator()

    // 160 px wide ; the text is reported on validation
    lnv_bar.of_add_textbox(/*id*/ "search", /*text*/ "", /*width*/ 160, /*tooltip*/ "Find (Enter to validate)")

    lnv_bar.of_add_label(/*id*/ "lbl_due", /*text*/ "Due date:")
    lnv_bar.of_add_datepicker(/*id*/ "due", /*date*/ "2026-07-11", /*tooltip*/ "Due date")

    // ue_text_changed event of uo_toolbar
    if as_id = "search" then of_filtrer(as_text)

    // ue_date_changed event of uo_toolbar : as_date in "yyyy-mm-dd" format
    if as_id = "due" then id_echeance = Date(as_date)

### Two bars and a remembered layout #

The user can grab a bar by its handle (to the left of the bar) and move it to another band or another position. Keep the layout when the window closes, and restore it when it opens.

    n_pbt_toolbar_bar lnv_std, lnv_fmt

    lnv_std = uo_toolbar.of_bar(uo_toolbar.MAIN_BAR)
    lnv_std.of_add_button(/*id*/ "new",  /*text*/ "New", /*image*/ "", /*tooltip*/ "")
    lnv_std.of_add_button(/*id*/ "open", /*text*/ "Open",  /*image*/ "", /*tooltip*/ "")

    // Second bar, placed on band 1 (second row), in first position
    lnv_fmt = uo_toolbar.of_add_bar(/*bar*/ "format", /*band*/ 1, /*index*/ 0)
    lnv_fmt.of_add_toggle(/*id*/ "bold", /*text*/ "B", /*image*/ "", /*tooltip*/ "Bold")

    // A bar can also be moved at any time through its properties
    uo_toolbar.of_bar("format").ii_band  = 0
    uo_toolbar.of_bar("format").ii_index = 1    // second bar of the first row

    // ue_layout_changed event of uo_toolbar : keep the chosen layout
    is_disposition = as_layout_json

    // Next time the window opens : restore what the user had arranged
    if is_disposition <> "" then uo_toolbar.of_set_layout(is_disposition)

### Overflow: "More tools" #

When the width is no longer enough, the tools that do not fit are folded into a More tools menu marked by a chevron at the end of the bar. This is automatic and requires no code: the folded tools stay clickable and raise the same events. A folded field — text box or date picker — stays a real field: you type in it, you pick a date in it, and the event is raised exactly as if the tool had stayed in the bar.

### Automatic height #

Nothing to enable: the number of rows (`of_set_layout`) dictates the height, the bar adjusts to it on its own and notifies you.

    // ue_auto_height event of uo_toolbar
    uo_contenu.y      = uo_toolbar.y + al_height
    uo_contenu.height = parent.workspaceheight() - uo_contenu.y

See Shared foundation.

## Best practices #

- Use `MAIN_BAR` for as long as a single bar is enough: it already exists, there is no need to add it.
- Always reach a tool through its path — `of_bar(...).of_item(...)` — never through its identifier alone: that is what makes identifiers reusable from one bar to the next.
- Group related tools with `of_add_separator()` rather than with extra bars.
- On icon-only buttons, always fill in the tooltip: it is the only label the user has.
- If you let users rearrange the bars, remember `ue_layout_changed` and replay `of_set_layout` at startup, otherwise their effort is lost every time the application launches.
- Call `of_reset()` before rebuilding the toolbar from scratch for another context.
- When the commands become too numerous for a bar, move up to the ribbon, organized into tabs and groups.

← Component reference · Guide contents