# statusbar — `u_pbt_statusbar` #

← Component reference · Guide contents

> Status bar made of panels: rich text, icons, fixed or automatic widths, alignment to the left or to the right, clickable panels, a mini progress bar and colored states.

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

## At a glance #

Userobject | `u_pbt_statusbar`
Item class | `n_pbt_statusbar_panel` (panel)
Used for | Showing the state of the application at the bottom of the window: context, progress, discreet alerts
Opt-in options | —

## Quick start #

    // window open event
    // of_add_panel(id, text, icon, alignment, width)
    // empty id = purely informative panel ; width 0 = fitted to the text
    uo_statut.of_add_panel(/*id*/ "etat",  /*text*/ "Ready",           /*icon_file*/ "", /*align*/ uo_statut.ALIGN_START,  /*width*/ 0)
    uo_statut.of_add_panel(/*id*/ "",      /*text*/ "Line 12, Col 4", /*icon_file*/ "", /*align*/ uo_statut.ALIGN_START,  /*width*/ 0)
    uo_statut.of_add_panel(/*id*/ "heure", /*text*/ "12:00",          /*icon_file*/ "", /*align*/ uo_statut.ALIGN_END, /*width*/ 140)

    // Update a panel at any time, through its identifier
    uo_statut.of_item("etat").is_text = "Saving..."

## The model: panels with keys #

The bar is a sequence of panels, added in order. A panel is given an identifier when it is created: that is how you find it later to change its text, its icon or its state.

The identifier is an addressing key, not an interactivity switch:

- Identifier supplied: the panel can be found again — you change its content, you give it a tooltip. It stays inert: a status bar shows things above all, and a panel like `Line 12, Col 4` must not look pressable.
- Empty identifier: the panel is purely decorative. It cannot be found, cannot be clicked, and no tooltip can be attached to it. Give every panel an identifier: it costs nothing and keeps the door open.
- To make a panel clickable, ask for it: `of_item("id").ib_clickable = true`. A panel carrying a drop-down list (`of_set_panel_menu`) already is.

    uo_statut.of_item("etat").is_text = "3 records changed"

See Shared foundation · Items.

## Properties #

Property | Type | Default | Purpose
--- | --- | --- | ---
`ib_show_resize_grip` | boolean | `false` | Shows the resize grip in the corner at the end of the bar
`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

## Methods #

Method | Purpose
--- | ---
`of_add_panel (string as_id, string as_text, string as_icon_file, string as_align, integer ai_width)` | Adds a panel at the end of the bar
`of_add_sep ( )` | Adds a vertical separator line between two groups of panels
`of_insert_panel (as_id, as_text, as_icon_file, as_align, ai_width, ai_index)` | Inserts a panel at a specific position (counted from 0)
`of_move_panel (string as_id, integer ai_index)` | Moves an existing panel to another position
`of_remove_panel (string as_id)` | Removes a single panel; the others keep their state
`of_item (string as_id)` → `n_pbt_statusbar_panel` | Returns the handle to a panel (created on first access)
`of_flash_panel (string as_id, string as_text, long al_ms)` | Shows a message for `al_ms` milliseconds, then puts the previous text back (`al_ms` ≤ 0 = 2 seconds)
`of_set_panel_menu (string as_id, string as_item_ids[], string as_labels[])` | Turns the panel into a chooser: clicking it opens a drop-down list, and the choice comes back through `ue_panel_menu_clicked`. An identifier of `-` inserts a separator; an empty label falls back to the identifier
`of_clear_panel_menu (string as_id)` | Removes the drop-down list; the panel goes back to the behaviour `ib_clickable` gives it
`of_clear ( )` | Empties the bar: every panel and every separator
`of_reset ( )` | Empties the bar and resets every property to its default

### The arguments of `of_add_panel` #

Argument | Values | Effect
--- | --- | ---
`as_id` | free, or `""` | Key of the panel, the one you find it by later. Empty = a decorative panel, neither addressable nor clickable
`as_text` | text | Content of the panel. Rich text markup is accepted
`as_icon_file` | image path, or `""` | Icon displayed before the text (accepted forms)
`as_align` | `ALIGN_START` (default) or `ALIGN_END` | Side the panel is pushed toward. Logical values: `START` = start of the reading direction (left in left-to-right writing). The physical aliases `"left"` / `"right"` are still accepted
`ai_width` | pixels, or `0` | Fixed width. `0` = the panel fits its content

### On a panel — `n_pbt_statusbar_panel` #

Member | Type | Default | Purpose
--- | --- | --- | ---
`is_text` | string | `""` | Text of the panel, rich text markup accepted
`is_image` | string | `""` | Icon of the panel, which can be changed at any time
`ib_enabled` | boolean | `true` | Panel grayed out and not clickable
`ib_visible` | boolean | `true` | Panel hidden, without being removed from the bar
`ii_progress` | integer | — | Mini progress bar inside the panel, from `0` to `100`; a negative value makes it disappear
`is_state` | string | `""` | Semantic state of the panel, which colors it: see the constants below
`ib_indeterminate` | boolean | `false` | Bar animated without a value, for a task whose duration is unknown. Independent of `ii_progress`, which stays the exact percentage
`ib_clickable` | boolean | `false` | Does the panel answer the click. Opt-in: a panel stays inert until you ask, while keeping its key — it is driven and carries a tooltip. A panel with a drop-down list is clickable already

### State constants #

Constant | Value | Use
--- | --- | ---
`STATE_NONE` | `""` | No state: normal appearance
`STATE_INFO` | `"info"` | Information
`STATE_WARNING` | `"warning"` | Warning
`STATE_ERROR` | `"error"` | Error
`STATE_SUCCESS` | `"success"` | Success

As with every property that takes predefined values, use the constant rather than the string:

    n_pbt_statusbar_panel lnv_panneau
    lnv_panneau = uo_statut.of_item("etat")
    lnv_panneau.is_state = lnv_panneau.STATE_WARNING

## Events #

Event | Raised when
--- | ---
`ue_panel_clicked (string as_id)` | A panel whose identifier is supplied is clicked
`ue_panel_double_clicked (string as_id)` | A clickable panel was double-clicked — the classic shortcut behind `Line 12, Col 4` that opens a "Go to line"
`ue_panel_rclicked (string as_id, long al_x, long al_y)` | A panel received a right click. `al_x` and `al_y` are screen pixels: pass them straight through to open a context menu where the user aimed
`ue_panel_menu_clicked (string as_id, string as_item_id)` | An entry of a panel drop-down list was picked (see `of_set_panel_menu`)
`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`)

## Keyboard #

The bar is a single tab stop: only the panels meant to be clicked enter it, and the arrows walk them.

Key | Effect
--- | ---
Arrows | Move to the previous / next interactive panel, wrapping around; display panels and disabled panels are skipped
Home / End | First / last interactive panel
Enter or Space | Triggers the panel — that is, `ue_panel_clicked`, or the opening of its dropdown if it has one

A panel that merely displays is not a control: it is neither focusable nor announced as one. A clickable but disabled panel, on the other hand, stays announced as unavailable rather than passing for text. A progress bar announces its value, and an indeterminate one announces none — that absence is the meaning of the word.

The focus survives the rebuild of the bar: it is redrawn on every text change, and without this the focus would drop every second on a bar showing a clock.

## Examples #

### Fixed widths and automatic widths #

    // Width 0 : the panel takes exactly the room its text needs
    uo_statut.of_add_panel(/*id*/ "", /*text*/ "Panel fitted to its content", /*icon_file*/ "", /*align*/ uo_statut.ALIGN_START, /*width*/ 0)

    // Fixed width in pixels : useful when the text changes often,
    // so that the neighboring panels do not move on every update
    uo_statut.of_add_panel(/*id*/ "pos", /*text*/ "Line 1, Col 1", /*icon_file*/ "", /*align*/ uo_statut.ALIGN_START, /*width*/ 150)

    // A panel pushed to the opposite end
    uo_statut.of_add_panel(/*id*/ "heure", /*text*/ "12:00", /*icon_file*/ "", /*align*/ uo_statut.ALIGN_END, /*width*/ 140)

### Icons and clickable panels #

    // A non-empty identifier makes the panel clickable
    uo_statut.of_add_panel(/*id*/ "save", /*text*/ "Saved", /*icon_file*/ "mono:img\save.svg", /*align*/ uo_statut.ALIGN_START, /*width*/ 0)
    uo_statut.of_add_sep()   // separator line between two groups of panels
    uo_statut.of_add_panel(/*id*/ "conn", /*text*/ "Connected",   /*icon_file*/ "mono:img\plug.svg", /*align*/ uo_statut.ALIGN_START, /*width*/ 0)
    uo_statut.of_add_panel(/*id*/ "user", /*text*/ "Guillaume",  /*icon_file*/ "mono:img\user.svg", /*align*/ uo_statut.ALIGN_END, /*width*/ 160)

    // ue_panel_clicked event of uo_statut
    choose case as_id
        case "conn" ; open(w_parametres_connexion)
        case "user" ; open(w_profil)
    end choose

### Rich text in a panel #

Panels accept rich text markup: styles, colors and small images right inside the text.

    uo_statut.of_add_panel(/*id*/ "", /*text*/ "Welcome [b]to[/b] [accent]PBToolboxAI[/accent]", &
                           /*icon_file*/ "", /*align*/ uo_statut.ALIGN_START, /*width*/ 0)

    uo_statut.of_add_panel(/*id*/ "", /*text*/ "[green]Online[/green] [picture=mono:img\plug.svg,14,14]", &
                           /*icon_file*/ "", /*align*/ uo_statut.ALIGN_END, /*width*/ 0)

    // Rich text works for updates too
    uo_statut.of_item("etat").is_text = "[b]" + String(ll_modifies) + "[/b] records changed"

### Following a long operation #

    n_pbt_statusbar_panel lnv_avance

    uo_statut.of_add_panel(/*id*/ "import", /*text*/ "Import", /*icon_file*/ "", /*align*/ uo_statut.ALIGN_START, /*width*/ 220)
    lnv_avance = uo_statut.of_item("import")

    // Inside the processing loop : the mini bar follows the progress
    lnv_avance.ii_progress = ll_pourcentage
    lnv_avance.is_text     = "Import " + String(ll_pourcentage) + " %"

    // At the end : hide the mini bar and report the result
    lnv_avance.ii_progress = -1                       // negative value = bar hidden
    lnv_avance.is_text     = "Import complete"
    lnv_avance.is_state    = lnv_avance.STATE_SUCCESS

### Raising a discreet alert #

    n_pbt_statusbar_panel lnv_panneau
    lnv_panneau = uo_statut.of_item("conn")

    if not ib_connecte then
        lnv_panneau.is_text  = "Offline"
        lnv_panneau.is_state = lnv_panneau.STATE_ERROR
    else
        lnv_panneau.is_text  = "Connected"
        lnv_panneau.is_state = lnv_panneau.STATE_NONE   // back to the normal appearance
    end if

### Adapting the bar to the context #

    // Hide a panel without deleting it : it will find its place again later
    uo_statut.of_item("user").ib_visible = ib_utilisateur_identifie

    // Gray it out when the matching action makes no sense
    uo_statut.of_item("save").ib_enabled = ib_document_ouvert

    // Reorder : move the status panel to the front (positions counted from 0)
    uo_statut.of_move_panel(/*id*/ "etat", /*index*/ 0)

    // Remove a panel that is no longer needed
    uo_statut.of_remove_panel(/*id*/ "import")

### The resize grip #

    // On a resizable window, the corner grip is a familiar landmark
    uo_statut.ib_show_resize_grip = true

## Best practices #

- Give a fixed width to panels whose text changes often (cursor position, counters): the neighboring panels will stop jumping on every refresh.
- Leave the identifier empty for a purely informative panel: that avoids a click that does nothing.
- Save the right-hand side for stable information (time, user, connection) and the left-hand side for the current context.
- Use `is_state` rather than colors in the text: the state follows both the light and the dark theme.
- Remember to set `is_state` back to `STATE_NONE` and `ii_progress` back to a negative value as soon as the alert or the operation is over.
- A status bar is not a log: beyond five or six panels, prefer a toaster notification.
- If the progress deserves more than a panel-sized mini bar, move up to the progressbar.

← Component reference · Guide contents