PBToolboxAI v5 ← Site

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 #

Userobjectu_pbt_statusbar
Item classn_pbt_statusbar_panel (panel) · n_pbt_statusbar_menu_item (drop-down entry)
Used forShowing the state of the application at the bottom of the window: context, progress, discreet alerts
Opt-in optionsib_announce

Quick start #

// window open event
// of_add_panel(key, text, icon, alignment, width)
// the key finds the panel later ; width 0 = fitted to the text, -1 = takes the free room
uo_status.of_add_panel(/*key*/ "state", /*text*/ "Ready",          /*icon_file*/ "", /*align*/ uo_status.ALIGN_START, /*width*/ -1)
uo_status.of_add_panel(/*key*/ "pos",   /*text*/ "Line 12, Col 4", /*icon_file*/ "", /*align*/ uo_status.ALIGN_START, /*width*/ 0)
uo_status.of_add_panel(/*key*/ "clock", /*text*/ "12:00",          /*icon_file*/ "", /*align*/ uo_status.ALIGN_END,   /*width*/ 140)
// Update a panel at any time, through its identifier
uo_status.of_panel(/*key*/ "state").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:

// The panel shows a new text
uo_status.of_panel(/*key*/ "state").is_text = "3 records changed"

See Shared foundation · Items.


Properties #

PropertyTypeDefaultPurpose
ib_show_resize_gripbooleanfalseShows the resize grip in the corner at the end of the bar; dragging it resizes the window that carries the bar — an MDI sheet, not its frame — from its bottom-right corner (bottom-left in a right-to-left layout). It is drawn only when the bar sits in the bottom corner of that window and the window can be resized: maximized, without a sizing border or with the bar placed higher, it disappears and the property stays set
ib_announcebooleanfalseFor screen readers: at true, a text or a state that changes on a drawn panel is read out (a polite live region, which lets the reader finish its sentence). A message of of_flash_panel is always read out, whatever this property says: a message is meant to be read. Off by default: a clock ticking every second would talk all day
is_helpstring""The MicroHelp of a PowerBuilder frame: what SetMicroHelp used to write. Shown OVER the text of the panel is_help_panel names — its is_text still reads its own text —; "" gives the panel its text back. A message of of_flash_panel shows in front of it while it lasts
is_help_panelstring""Key of the panel that shows is_help — usually the first one, the Ready panel. Empty = no help panel: is_help waits for one to be named
is_theme_stylestring""Visual style of the component (THEME_STYLE_* constants); empty = the application's, followed at every change
is_theme_modestring""Light or dark variant (THEME_MODE_* constants); empty = the application's, followed at every change
il_theme_accentlong-1Accent colour of this component (-1 = the application accent, or the theme's)
is_tooltipstring""Simple tooltip shown when hovering the component
is_super_tooltip_titlestring""Title of the rich tooltip (takes precedence over is_tooltip)
is_super_tooltip_textstring""Text of the rich tooltip (rich markup accepted)
is_super_tooltip_imagestring""Image of the rich tooltip

Methods #

MethodPurpose
of_add_panel (string as_key, string as_text, string as_icon_file, string as_align, integer ai_width)Adds a panel at the end of the bar; a width of -1 makes it a spring that takes the free room (ib_spring). Returns 0 once applied, -5 when the key is already taken, contains / or ` or starts with __ (an empty key adds a decorative panel), -2` when the component is not created
of_add_sep ( )Adds a group break at the current position. Panels already separate themselves with a thin line : this one is wider, so the panels before and after it read as two groups. Call it between two of_add_panel ; it opens the group of the panel after it (on the side of the one before when it comes last). A separator is not a panel : it counts neither for of_count / of_keys_at nor in positions. Returns 0 once applied, -2 when the component is not created
of_insert_panel (string as_key, string as_text, string as_icon_file, string as_align, integer ai_width, integer ai_index)Inserts a panel at a given position, counted in panels from 1 (0 or less = first, past the last = at the end). Same refusals as of_add_panel. Returns 0 once applied, -5 when the key is already taken or contains / or `, -2` when the component is not created
of_move_panel (string as_key, integer ai_index)Moves an existing panel to another position, counted in panels from 1. Returns 0 once applied, -5 on an empty key or one the bar was never given, -2 when the component is not created
of_remove_panel (string as_key)Removes a single panel, with its dropdown ; the others keep their state. Returns 0 once applied, -5 on an empty key or one the bar was never given, -2 when the component is not created
of_panel (string as_key) → n_pbt_statusbar_panelReturns the handle to a panel (created on first access). An empty key (a decorative panel), an address or a list designates nothing: its handle writes nowhere and reads back empty
of_flash_panel (string as_key, string as_text, long al_ms)Shows a message for al_ms milliseconds, then shows the panel's text again (al_ms ≤ 0 = 2 seconds). The message is shown OVER the text: is_text always reads back as the panel's text, never as the message. Returns 0 once applied, -5 on an empty key, an address or a key the bar was never given, -2 when the component is not created
of_add_menu_item (string as_keys, string as_label) · (as_keys, as_label, as_image)Adds an entry to the dropdown of a panel : as_keys has two levels, the panel then the entry ("enc/utf8"). From the first entry on, the panel becomes a chooser : a click opens the list, and the pick comes back through ue_panel_menu_clicked with the same address. An empty label falls back to the key. Returns 0 once applied, -5 on an address that does not have two levels, a panel the bar was never given or an entry already taken, -2 when the component is not created
of_insert_menu_item (string as_keys, string as_label, integer ai_index) · (as_keys, as_label, as_image, ai_index)Inserts an entry at a given position, counted in entries from 1: a separator is not one. Returns 0 once applied, -5 on an address that does not have two levels, a key starting with __, a panel the bar was never given or an entry already taken, -2 when the component is not created
of_add_menu_separator (string as_key)Separator line in the list of panel as_key. A separator has no address: only of_clear_menu removes it, and a list made of separators alone is no list (no chevron, no click). Returns 0 once applied, -5 on an empty key or a panel the bar was never given, -2 when the component is not created
of_remove_menu_item (string as_keys)Removes a single entry; the last one gone, the panel goes back to the behaviour ib_clickable gives it. Returns 0 once applied, -5 on an address the bar was never given, -2 when the component is not created
of_move_menu_item (string as_keys, integer ai_index)Moves an entry to another position in its list, counted in entries from 1: a separator is not one. Returns 0 once applied, -5 on an address the bar was never given, -2 when the component is not created
of_menu_item (string as_keys) → n_pbt_statusbar_menu_itemReturns the handle to an entry (created on first access), to grey it, tick it or rename it. An address without two levels designates nothing : its handle writes nowhere and reads empty
of_clear_menu (string as_key)Removes the whole drop-down list; the panel goes back to the behaviour ib_clickable gives it. Returns 0 once applied, -5 on an empty key, an address or a panel the bar was never given, -2 when the component is not created
of_dock_bottom ( )Places the bar along the BOTTOM edge of what carries it (a window, a tab page, a userobject), over its whole width; its height does not change. Call it from the resize event of the window. In an MDI frame, size mdi_1 to the room that is left: WorkSpaceHeight() - uo_status.Height. Returns 0 once placed, -5 when the bar is neither on a window nor on a userobject, -2 when the component is not created
of_clear ( )Empties the bar: every panel and every separator. Returns 0 once applied, -2 when the component is not created
of_reset ( )Empties the bar and resets every property to its default. Returns 0 once applied, -2 when the component is not created

The arguments of of_add_panel #

ArgumentValuesEffect
as_keyfree, or ""Key of the panel, the one you find it by later. Empty = a decorative panel, neither addressable nor clickable
as_texttextContent of the panel. Rich text markup is accepted
as_icon_fileimage path, or ""Icon displayed before the text (accepted forms)
as_alignALIGN_START (default) or ALIGN_ENDSide 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_widthpixels, 0 or -1Fixed width. 0 = the panel fits its content; -1 = a spring that takes all the free room of the bar (ib_spring)

On a panel — n_pbt_statusbar_panel #

MemberTypeDefaultPurpose
is_textstring""Text of the panel, rich text markup accepted
is_imagestring""Icon of the panel, which can be changed at any time
ib_enabledbooleantruePanel grayed out and not clickable
ib_visiblebooleantruePanel hidden, without being removed from the bar
ii_progressinteger—Mini progress bar inside the panel, beside the text, from 0 to 100 (above 100 the bar is full and reads back 100); a negative value makes it disappear. Reads back -1 when the panel has no bar (and 0 for a bar at 0 %)
is_statestring""Semantic state of the panel, which colors its text and marks its leading edge: see the constants below. Any other value means no state and reads back empty; a disabled panel is greyed, state mark included
ib_indeterminatebooleanfalseBar animated without a value, for a task whose duration is unknown. Independent of ii_progress, which stays the exact percentage
ib_clickablebooleanfalseDoes 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
ib_springbooleanfalseSpring: the panel takes all the free room of the bar, and its text is the first to elide when the window narrows — the Ready panel that grows with the window. Several springs share the room; a fixed width gives way
is_tooltipstring""Tooltip of the panel (rich text markup accepted). It also shows on a greyed panel, to say why. Without a tooltip, a panel whose text is cut shows its whole text when hovered
is_super_tooltip_titlestring""Title of the rich tooltip of the panel (takes precedence over is_tooltip)
is_super_tooltip_textstring""Text of the rich tooltip of the panel (markup accepted)
is_super_tooltip_imagestring""Image of the rich tooltip of the panel
il_accentlong-1Accent of this panel: the fill of its mini progress bar. -1 = the component's accent
il_back_colorlong-1Background of this panel at rest. -1 = the bar's
il_text_colorlong-1Text colour of this panel at rest. A state (is_state) or a greyed panel paints its own colour first: the meaning wins over the colour. -1 = the bar's
il_back_color_hoverlong-1Background under the pointer — only a clickable panel (ib_clickable) answers the pointer. -1 = the theme's
il_text_color_hoverlong-1Text colour under the pointer, on a clickable panel. -1 = the theme's

On a drop-down entry — n_pbt_statusbar_menu_item #

Obtained through of_menu_item(/*keys*/ "enc/utf8"): the address has two levels, the panel then the entry. The list is a native menu: a property changed while it is open shows at the next opening.

MemberTypeDefaultPurpose
is_labelstring""Text of the entry
is_imagestring""Image before the text, which can be changed at any time
ib_enabledbooleantrueEntry greyed out: shown, but impossible to pick
ib_checkedbooleanfalseCheck mark before the entry, for the value in use
ib_visiblebooleantrueEntry left out of the list without being removed: showing it again needs nothing more

The properties every item handle inherits — is_tooltip, is_super_tooltip_title, is_super_tooltip_text, is_super_tooltip_image, il_accent, il_back_color, il_text_color, il_back_color_hover, il_text_color_hover — have no effect on an entry: the list is a native menu, which shows neither a tooltip nor the colours of one entry. They are ignored and read back empty (-1 for a colour). A position in a list counts entries: a separator is not one, and of_count("enc"), of_keys_at("enc", n), of_has("enc/utf8") walk the entries of a panel. A key that starts with __ is reserved to the component.

// The menu of the encoding panel, entry by entry
uo_status.of_add_menu_item(/*keys*/ "enc/utf8", /*label*/ "UTF-8")
uo_status.of_add_menu_item(/*keys*/ "enc/ansi", /*label*/ "ANSI")
uo_status.of_menu_item(/*keys*/ "enc/utf8").ib_checked = true     // the current value
uo_status.of_menu_item(/*keys*/ "enc/ansi").ib_enabled = false    // not available here

State constants #

ConstantValueUse
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:

// The panel takes the colors of a warning
uo_status.of_panel(/*key*/ "state").is_state = n_pbt_statusbar_panel.STATE_WARNING

Events #

EventRaised when
ue_panel_clicked (string as_key)A clickable panel (ib_clickable) is clicked
ue_panel_double_clicked (string as_key)A clickable panel was double-clicked — the classic shortcut behind Line 12, Col 4 that opens a "Go to line". Never on a panel carrying a drop-down list: its first click opened the list
ue_panel_rclicked (string as_key, long al_x, long al_y)A panel with a key received a right click — clickable or not (a context menu is not an activation), never when it is disabled. One event per right click. al_x and al_y are screen pixels ; for a PowerBuilder menu, PopMenu(PointerX(), PointerY()) of your window. From the keyboard, the Menu key or Shift+F10 on the focused panel raises it too, at the bottom-left corner of the panel
ue_panel_menu_clicked (string as_keys)An entry of a panel drop-down list was picked (see of_add_menu_item). as_keys carries both levels: the panel, then the entry — "enc/utf8". From the keyboard, Enter, Space, Up or Down arrow open the list. A panel removed, disabled or hidden while its list is open closes it, and nothing is raised
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.

KeyEffect
ArrowsMove to the previous / next interactive panel, wrapping around; display panels and disabled panels are skipped
Home / EndFirst / last interactive panel
Enter or SpaceTriggers the panel — that is, ue_panel_clicked, or the opening of its dropdown if it has one
Up / DownOn a panel carrying a drop-down list: open it, above the panel
Menu key or Shift+F10ue_panel_rclicked of the focused panel, at its bottom-left corner

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. A greyed clickable panel still shows its tooltip — the one that says why it is greyed — and a panel with a list says whether its list is open.

A screen reader hears the messages of of_flash_panel without the focus moving: that is what a status bar is for (a polite live region). The other changes — a new text, a new state — are read out only with ib_announce = true; otherwise a clock ticking every second would talk all day.

The focus survives the rebuild of the bar: a panel added, removed, hidden or greyed rebuilds it, and the focus comes back to the same panel (to the next one when it can no longer take it). A text, a state or a progress changes in place, without rebuilding anything — a clock ticking every second never steals the focus.


Examples #

Fixed widths and automatic widths #

// Width 0 : the panel takes exactly the room its text needs
uo_status.of_add_panel(/*key*/ "", /*text*/ "Panel fitted to its content", /*icon_file*/ "", /*align*/ uo_status.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_status.of_add_panel(/*key*/ "pos", /*text*/ "Line 1, Col 1", /*icon_file*/ "", /*align*/ uo_status.ALIGN_START, /*width*/ 150)

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

Icons and clickable panels #

// A key makes the panel addressable ; ib_clickable makes it clickable
uo_status.of_add_panel(/*key*/ "save", /*text*/ "Saved", /*icon_file*/ "mono:img\save.svg", /*align*/ uo_status.ALIGN_START, /*width*/ 0)
uo_status.of_add_sep()   // separator line between two groups of panels
uo_status.of_add_panel(/*key*/ "conn", /*text*/ "Connected",   /*icon_file*/ "mono:img\plug.svg", /*align*/ uo_status.ALIGN_START, /*width*/ 0)
uo_status.of_add_panel(/*key*/ "user", /*text*/ "Alex Martin",  /*icon_file*/ "mono:img\user.svg", /*align*/ uo_status.ALIGN_END, /*width*/ 160)

// Only these two answer a click (ue_panel_clicked)
uo_status.of_panel(/*key*/ "conn").ib_clickable = true
uo_status.of_panel(/*key*/ "user").ib_clickable = true
// ue_panel_clicked event of uo_status
choose case as_key
    case "conn" ; open(w_connection_settings)
    case "user" ; open(w_profile)
end choose

Rich text in a panel #

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

// A welcome on the left
uo_status.of_add_panel(/*key*/ "", /*text*/ "Welcome [b]to[/b] [accent]PBToolboxAI[/accent]", &
                       /*icon_file*/ "", /*align*/ uo_status.ALIGN_START, /*width*/ 0)

// The connection state on the right
uo_status.of_add_panel(/*key*/ "", /*text*/ "[green]Online[/green] [picture=mono:img\plug.svg,14,14]", &
                       /*icon_file*/ "", /*align*/ uo_status.ALIGN_END, /*width*/ 0)
// Local variables
long ll_changed

// Rich text works for updates too
ll_changed = dw_list.ModifiedCount()
uo_status.of_panel(/*key*/ "state").is_text = "[b]" + String(ll_changed) + "[/b] records changed"

Following a long operation #

// Local variables
n_pbt_statusbar_panel lnv_import

// The import panel, then its handle to follow it
uo_status.of_add_panel(/*key*/ "import", /*text*/ "Import", /*icon_file*/ "", /*align*/ uo_status.ALIGN_START, /*width*/ 220)
lnv_import = uo_status.of_panel(/*key*/ "import")
// Local variables
long ll_percent

// Inside the processing loop : the mini bar follows the progress
for ll_percent = 0 to 100 step 25
    lnv_import.ii_progress = ll_percent
    lnv_import.is_text     = "Import " + String(ll_percent) + " %"
next
// At the end : hide the mini bar and report the result
lnv_import.ii_progress = -1                       // negative value = bar hidden
lnv_import.is_text     = "Import complete"
lnv_import.is_state    = lnv_import.STATE_SUCCESS

Raising a discreet alert #

// Local variables
n_pbt_statusbar_panel lnv_panel

// The connection panel
lnv_panel = uo_status.of_panel(/*key*/ "conn")

// Offline : the panel shows an error ; connected : it looks normal again
if not ib_connected then
    lnv_panel.is_text  = "Offline"
    lnv_panel.is_state = lnv_panel.STATE_ERROR
else
    lnv_panel.is_text  = "Connected"
    lnv_panel.is_state = lnv_panel.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_status.of_panel(/*key*/ "user").ib_visible = ib_user_signed_in

// Gray it out when the matching action makes no sense
uo_status.of_panel(/*key*/ "save").ib_enabled = ib_document_open

// Reorder : move the status panel to the front (positions counted from 1)
uo_status.of_move_panel(/*key*/ "state", /*index*/ 1)

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

The resize grip #

// Dragging the corner grip resizes the window that carries the bar (hidden while it is maximized)
uo_status.ib_show_resize_grip = true

A panel that takes the free room #

Like the « Ready » panel of every Windows status bar: it grows with the window, pushes the end panels to the far end, and its text is the first to elide when room runs short.

// A width of -1 : the panel takes all the free room
uo_status.of_add_panel(/*key*/ "state", /*text*/ "Ready", /*icon_file*/ "", /*align*/ uo_status.ALIGN_START, /*width*/ -1)
uo_status.of_add_panel(/*key*/ "clock", /*text*/ "12:00", /*icon_file*/ "", /*align*/ uo_status.ALIGN_END, /*width*/ 140)

// The same thing on a panel that already exists
uo_status.of_panel(/*key*/ "state").ib_spring = true

Placing the bar at the bottom of the window #

of_dock_bottom sets it along the bottom edge of what carries it, over the whole width: one call in the resize event is enough. In an MDI frame, the sheets share what is left above it.

// resize event of the window : the bar follows the bottom edge
uo_status.of_dock_bottom()
// resize event of an MDI frame : the bar at the bottom, the sheets above it
uo_status.of_dock_bottom()
mdi_1.Resize(WorkSpaceWidth(), WorkSpaceHeight() - uo_status.Height)

Taking over the MicroHelp #

In an MDI frame with MicroHelp, PowerBuilder writes the help of the menu item under the pointer, and SetMicroHelp writes yours. The bar takes that role over: name once the panel that shows the help (is_help_panel), then write is_help where you used to call SetMicroHelp. The help shows over the text of the panel, and "" gives the text back.

// Once : the panel that shows the help (the first one, usually)
uo_status.is_help_panel = "state"

// Where you called SetMicroHelp : the help shows over Ready
uo_status.is_help = "Saving the order..."

// "" gives the panel its own text back
uo_status.is_help = ""
// ue_item_hover event of a menubar (uo_menu) : the help of the entry under the pointer
uo_status.is_help = this.of_item(/*keys*/ as_keys).is_tooltip
// getfocus event of a field (sle_name) : its Tag is its help
uo_status.is_help = sle_name.Tag

Best practices #

Inherited from the common base #

These members exist on every visual component — they are not specific to this one. They are detailed once, in the transverse chapters; this table only says where to read them.

MembersRoleDetailed in
of_count · of_keys_at · of_hasWalk what the component holds3.2 Items
of_resetPut the component back to zero3.6 Resetting a component: of_reset()
of_register_shortcut · of_clear_shortcutsThe component's keyboard chords3.5 Keyboard shortcuts
of_is_created · of_is_ready · of_get_last_errorWhether it was born, whether it is ready, what failed3.7 Diagnostics
of_save_as_png · of_save_as_jpgExport the rendering as an image3.8 Exporting the rendering as an image
of_set_redrawGroup changes into a single repaint3.10 Best practices
of_preload_iconsIcons shown with no delayInstant display: of_icon
of_set_translationTranslate one of the component's labels5.2 Adapting a label: of_set_translation
of_focus_webviewGive the component the focus6.4 Keyboard and focus
of_print · of_print_to_pdfPrint, or write a PDF6.9 Printing
of_set_property · of_get_property · of_component_nameDriving a property by its name3.1 The property engine
is_accessible_name · is_accessible_descriptionThe name and description a screen reader says3.4 Optional properties and events

Two helpers are not inherited: of_icon and of_escape_markup live on n_pbt_utils. Declare one — n_pbt_utils lnv_utils, nothing to create — and call them on it.


← Component reference · Guide contents