listbar — u_pbt_listbar #
← Component reference · Guide contents
Side navigation bar: collapsible accordion sections holding entries with icons, an accent band on the current entry, and collapsing into an icon rail.
▶ See it live — Demo application, Listbar tile: the preview, the code behind it and this page, side by side.
At a glance #
| Userobject | u_pbt_listbar |
| Item classes | n_pbt_listbar_section (section) → n_pbt_listbar_item (entry) |
| Used for | Replacing a side menu improvised out of buttons with structured, themed, collapsible navigation |
| Opt-in options | ib_auto_width, ib_reorderable, ib_search |
ib_auto_width means something particular here: the collapsed rail is far narrower than the expanded bar. The common case is in fact already covered without enabling anything — ib_collapsed = true shrinks the bar down to the rail, and gives its width back when it expands; ib_auto_width always reads back what you set.
Quick start #
// window open event
// A section, then its entries : each entry is addressed by section/entry
uo_nav.of_add_section(/*key*/ "nav", /*text*/ "Navigation")
uo_nav.of_add_item(/*keys*/ "nav/home", /*text*/ "Home", /*image*/ "mono:img\home.svg")
uo_nav.of_add_item(/*keys*/ "nav/docs", /*text*/ "Documents", /*image*/ "mono:img\doc.svg")
uo_nav.of_add_item(/*keys*/ "nav/search", /*text*/ "Search", /*image*/ "mono:img\find.svg")
// A second section, with a single entry
uo_nav.of_add_section(/*key*/ "settings", /*text*/ "Settings")
uo_nav.of_add_item(/*keys*/ "settings/prefs", /*text*/ "Preferences", /*image*/ "mono:img\gear.svg")
// The selection is set on the ENTRY, by its address : the section, then the entry
uo_nav.of_select_item(/*keys*/ "nav/home")
// of_select_item raises ue_selection_changed, like a click : the first page opens there
// ue_selection_changed event of uo_nav : (string as_from_keys, string as_keys)
choose case as_keys
case "nav/home" ; of_open_home()
case "nav/docs" ; of_open_documents()
case "settings/prefs" ; of_open_preferences()
end choose
Two levels, one address #
An entry identifier is only unique within its section, so an entry is named by its address: the section, then the entry, joined by / — "nav/docs". The component is what takes it (of_item, of_select_item, of_remove_item…); a section is named by its key alone (of_section("nav")). See Hierarchies.
// A section by its key, an entry by its address
uo_nav.of_section(/*key*/ "nav").is_title = "Navigation"
uo_nav.of_item(/*keys*/ "nav/docs").is_text = "Documents"
The events also carry the full path, and the entry left with it: ue_selection_changed(as_from_keys, as_keys).
Properties #
| Property | Type | Default | Purpose |
|---|---|---|---|
ib_collapsed | boolean | false | true collapses the bar into an icon rail: section titles and labels give way to a thin rule between sections, and every entry stays visible, even in a folded section (the fold comes back on the way out). An entry with no icon shows its initial, and each entry tells its label on hover |
ib_auto_width | boolean | false | Opt-in: same for the width, including when expanded (the bar sizes itself to the longest label). Collapsing into the rail already shrinks by itself; ue_auto_width follows in both cases |
ib_reorderable | boolean | false | Opt-in: the user can drag an entry to another position. The move stays inside its section — an entry id is only unique there, so crossing would risk two identical keys (raises ue_item_reordered) |
ib_veto_selection | boolean | false | Opt-in: ask before the selection moves — a gesture of the user and of_select_item / of_clear_selection (raises ue_selection_changing, which can refuse; a refused order returns -4). Off by default, like every cancelable event of the library: set it to true when you need to refuse a move (unsaved changes). Each question of a gesture costs a round trip to PowerBuilder (~35 ms) |
ib_search | boolean | false | Opt-in: a search field at the top of the bar. What the user types filters the entries (is_filter, ue_filter_changed); Ctrl+F reaches the field, Escape empties it, Down goes to the first entry, Enter picks the first match. Not drawn in the rail (no room): is_filter still applies there |
is_filter | string | "" | The text filtering the bar, read live: the entries whose label — or whose section title — holds it, case and accents ignored. A section holding a match is shown open, its fold untouched; a section with none disappears while the filter stands. Set by your code it raises nothing (a value, like SetFilter); typed by the user, ue_filter_changed. While it filters, a title does not fold and an entry cannot be dragged. In demo mode the six entries drawn are counted without the filter: a search never reaches a seventh |
is_theme_style | string | "" | Visual style of the component (THEME_STYLE_* constants); empty = the application's, followed at every change |
is_theme_mode | string | "" | Light or dark variant (THEME_MODE_* constants); empty = the application's, followed at every change |
il_theme_accent | long | -1 | Accent colour of this component (-1 = the application accent, or the theme's) |
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 |
Properties of a section — n_pbt_listbar_section #
| Property | Type | Default | Purpose |
|---|---|---|---|
is_title | string | "" | Section title. Accepts rich text markup. An empty title shows no header row at all: the section becomes a plain invisible grouping; a gap separates it from the section above (a thin rule in the rail), and it can no longer be folded — nobody could open it again |
ib_collapsed | boolean | false | Accordion: true collapses the entries of this section. The header stays visible and its chevron rotates; ue_section_toggled follows, as for a click on the header |
ib_pinned | boolean | false | true pins the section at the bottom of the bar (Settings, Help): a footer that stays in view while the rest of the bar scrolls, pushed to the bottom when there is room to spare, and at the bottom of the rail too. Several pinned sections stack in their order. Only the drawing changes: the layout (of_get_layout) keeps the order of the sections, and the Down arrow goes from the last entry at the top to the first one of the footer |
is_image | string | "" | Icon before the section title (accepted forms: path, mono:, tint:, DLL resource). An empty string removes it |
is_tooltip | string | "" | Plain tooltip shown when hovering the section title |
is_super_tooltip_title | string | "" | Title of the rich tooltip of the section title (takes precedence over is_tooltip) |
is_super_tooltip_text | string | "" | Text of the rich tooltip of the section (rich markup accepted) |
is_super_tooltip_image | string | "" | Image of the rich tooltip of the section |
il_accent | long | -1 | Accent of this section, i.e. of its title and nothing else: its entries keep their own (-1 = the component's) |
il_back_color | long | -1 | Background of the section title, as RGB(r,g,b) (-1 = the component's) |
il_text_color | long | -1 | Text colour of the section title (-1 = the component's) |
il_back_color_hover | long | -1 | Background of the title under the pointer (-1 = the component's) |
il_text_color_hover | long | -1 | Text colour of the title under the pointer (-1 = the component's) |
Properties of an entry — n_pbt_listbar_item #
| Property | Type | Default | Purpose |
|---|---|---|---|
is_text | string | "" | Entry label, which can be changed on the fly without rebuilding the bar. Accepts rich text markup |
is_image | string | "" | Icon, changeable on the fly (accepted forms: file path, mono:, tint:, DLL resource) |
ib_enabled | boolean | true | false greys the entry: it stays in place and answers nothing — no click, no Enter, no right click, and the arrows skip it; its tooltip still shows, to say why. Your code cannot select it either (of_select_item returns -5); greying the entry already selected keeps it selected |
ib_visible | boolean | true | false hides the entry without removing it from the bar. A hidden entry cannot become the selection (of_select_item returns -5); hiding the one that is selected keeps it selected, and showing it again gives it back as it was |
is_badge | string | "" | Count pill at the end of the entry ("12" unread), in the theme's badge colours; in the rail, on the corner of the icon. Any short text, markup accepted. An empty string removes it |
of_is_selected ( ) → boolean | — | — | Is this entry the selected one? Read only, and deliberately: the selection is one state of the whole bar, not a flag per entry. To set it, of_select_item on the component |
is_tooltip | string | "" | Simple tooltip shown when hovering the item |
is_super_tooltip_title | string | "" | Title of the item rich tooltip (takes precedence over is_tooltip) |
is_super_tooltip_text | string | "" | Text of the item rich tooltip (rich markup accepted) |
is_super_tooltip_image | string | "" | Image of the item rich tooltip |
il_accent | long | -1 | Accent of this entry: the bar that marks it when it is selected takes it (-1 = the component's). For "this entry in red", il_back_color / il_text_color |
il_back_color | long | -1 | The entry's own background, as RGB(r,g,b) (-1 = what the theme gives) |
il_text_color | long | -1 | Text colour of the entry (-1 = what the theme gives) |
il_back_color_hover | long | -1 | Background of the entry under the pointer (-1 = what the theme gives) |
il_text_color_hover | long | -1 | Text colour of the entry under the pointer (-1 = what the theme gives) |
Methods #
On the component #
| Method | Purpose | |
|---|---|---|
of_add_section (string as_key, string as_text) | Adds a section at the end of the bar. An empty text draws no title line. The handle comes from of_section("nav") when you want to set a property. Returns 0 once applied, -5 when the key is empty, already taken or holds a / or a ` | , -2` when the component is not created |
of_insert_section (string as_key, string as_text, integer ai_index) | Adds a section at the rank you choose (first position = 1) rather than at the end. The index counts sections, not rows. A name already taken is refused: two sections under the same name would make the address of each of their entries ambiguous. Returns 0 once applied, -5 when the key is empty, already taken or holds a / or a ` | , -2` when the component is not created |
of_move_section (string as_key, integer ai_index) | Moves a section to rank ai_index, carrying its entries with it. Moving the header alone would drop its entries into whatever section then stands above them. Returns 0 once applied, -5 for a section this bar never added, -2 when the component is not created | |
of_remove_section (string as_key) | Removes a section and everything it holds. Emptying it while keeping its entries would orphan them: they would carry a section identifier that names nothing. If the selection was in it, ue_selection_changed says there is none any more. Returns 0 once applied, -5 for a section this bar never added, -2 when the component is not created | |
of_section (string as_key) | Handle of one section, by its key: its title, icon, fold and colours are set through it (created on first access). An entry is reached from the component instead, by its address (of_item) | |
of_add_item (string as_keys, string as_text, string as_image) | Adds an entry at its address, "nav/home": the section it lands in, then its own identifier. An overload omits the icon. Returns 0 once added, -5 when the section was not added by this bar, when the address is already taken or when the key holds a ` | , -2` when the component is not created |
of_item (string as_keys) | Handle of one entry, by its address (created on first access). An address that is not section/entry gives a handle that names nothing: what is written through it does nothing (a section is reached by of_section) | |
of_insert_item (string as_keys, string as_text, integer ai_index) | Inserts an entry at position ai_index within its section (first position = 1). Returns 0 once added, -5 when the section was not added by this bar, when the address is already taken or when the key holds a ` | , -2` when the component is not created |
of_insert_item (string as_keys, string as_text, string as_image, integer ai_index) | Same, with the entry's icon: of_add_item takes one, so inserting must too. Returns 0 once added, -5 when the section was not added by this bar, when the address is already taken or when the key holds a ` | , -2` when the component is not created |
of_move_item (string as_keys, integer ai_index) | Moves an existing entry within its section, preserving its state. Returns 0 once applied, -5 for an address this bar never added, -2 when the component is not created | |
of_remove_item (string as_keys) | Removes one entry, designated by its address. If it was the selection, ue_selection_changed says there is none any more. Returns 0 once applied, -5 for an address this bar never added, -2 when the component is not created | |
of_clear ( ) | Empties the bar: every section and every entry. If an entry was selected, ue_selection_changed says there is none any more (an empty address); ue_layout_changed is not raised, you are rebuilding. Returns 0 once applied, -2 when the component is not created | |
of_select_item (string as_keys) | Selects an entry, like a click on it: with ib_veto_selection on, ue_selection_changing is asked first; then ue_selection_changed follows, as for a click (right after your script, from the event queue), and of_selected_key() read right after already answers the new address. The entry already selected: nothing is asked nor raised. Returns 0 once selected (or already selected), -4 when your ue_selection_changing refused (nothing moves), -5 for an address this bar never added, a hidden entry, a greyed entry or an entry the demo cap does not draw — read BEFORE the question is asked —, -2 when the component is not created. ue_item_invoked is not raised: it answers a gesture of the user | |
of_get_layout ( ) | Reads the current arrangement back as JSON: the sections in order, each with its entries in order and whether it is folded. Store it (file, database, registry) and hand it back with of_set_layout at the next start. The same pair carries the same names on every component that can be rearranged | |
of_set_layout (string as_layout_json) | Restores an arrangement read with of_get_layout or received with ue_layout_changed. What the layout does not name keeps its place at the end: a layout saved yesterday must not make what was added since disappear. Applying it raises ue_layout_changed once when it changes something, never ue_section_toggled: a fold restored is a state given back, not a fold. Returns 0 once applied, -5 for an empty text or one that is not a JSON object (a truncated file): nothing is sent; the fold of a section that is not there is ignored, -2 when the component is not created | |
of_clear_selection ( ) | Leaves no entry selected, like any move: with ib_veto_selection on, ue_selection_changing is asked first, then ue_selection_changed follows with an empty address. Nothing selected already: nothing is asked nor raised. Returns 0 once applied, -4 when your ue_selection_changing refused (the entry stays selected), -2 when the component is not created | |
of_from_datastore (datastore ads) | Builds the bar from the rows of a DataStore — or a DataWindow, a DataWindowChild, the same call: one row = one entry, the menu of an application read from its table of rights. A column plays a role when it carries its name (section, section_text, key, text, image, badge, enabled) or when of_map says so; ROLE_SECTION is required. The load replaces the entries: the sections you declared keep their title, icon, fold and pin, and a section the rows name that the bar does not hold is added at the end, in the order of the rows, titled by ROLE_SECTION_TEXT (else by its key). Without ROLE_KEY, the key of an entry is its row number. ROLE_ENABLED greys an entry on 0, N, F, false, no or off. A row whose section or key is empty, holds a / or a ` | , or repeats an address is left out: the call still returns 0 and of_get_last_error names it. A selection whose entry does not come back leaves, with ue_selection_changed. Returns 0 once loaded, -5 when the DataStore is not valid or has no column, -2` when the component is not created |
of_map (string as_role, string as_column) | Gives a column of the DataStore a role when its name does not say it: of_map(u_pbt_listbar.ROLE_SECTION, "menu_group"). Roles: ROLE_SECTION, ROLE_SECTION_TEXT (the title of a section the rows create), ROLE_KEY, ROLE_TEXT, ROLE_IMAGE, ROLE_BADGE, ROLE_ENABLED. Before or after of_from_datastore: after, the rows are read again. An empty column gives the role back to the column that carries its name. Returns 0 once set, -5 when the role is not one of the ROLE_ constants or the column is not one of the DataStore of the last load, -2 when the component is not created | |
of_selected_key ( ) | Address of the selected entry — "nav/docs", "" if there is none. It is exactly what ue_selection_changed hands you: a comparison is a comparison, not a reassembly. Always the current one, read from the component: after a click, and right after of_select_item too | |
of_reset ( ) | Empties the bar, then brings the component back to its brand-new state. If an entry was selected, ue_selection_changed goes with an empty address; ue_layout_changed is not raised. Returns 0 once applied, -2 when the component is not created | |
of_set_redraw (boolean) | Groups a burst of changes into a single render. Returns 0 once applied, -2 when the component is not created | |
of_save_as_png (string) · of_save_as_jpg (string) | Exports the rendering as an image. Returns 0 once the image is written, -4 when writing fails, -2 when the component is not created |
On a section — n_pbt_listbar_section #
| Method | Purpose |
|---|---|
of_count ( ) | Number of entries in the section, hidden ones included |
of_keys_at (long al_index) | Address of the entry at rank al_index (from 1) — "nav/docs", ready to pass back to of_item |
of_has (string as_key) | Does the section hold this entry? |
Events #
| Event | Raised when |
|---|---|
ue_selection_changed (string as_from_keys, string as_keys) | The selection has moved: the user picked an entry (click, Enter, Space), your code moved it (of_select_item, of_clear_selection — an order raises it too, like SelectItem raises SelectionChanged), or the selected entry left (of_remove_item, of_remove_section, of_clear, of_reset — as_keys is then empty). Same arguments as ue_selection_changing: the question and its outcome read the same way, and the as_from_* pair designates the entry left (empty if there is none) |
ue_section_toggled (string as_key, boolean ab_collapsed) | The user folds or opens a section: a click on its title, Enter on the title, or the Left / Right arrows (the other way round right to left), or your code setting a section's ib_collapsed |
ue_item_reordered (string as_keys, integer ai_index) | The user finished dragging an entry. ai_index is its new rank inside its section, starting from 1, like of_move_item. Keep that order to give the user their bar back as they left it |
ue_layout_changed (string as_layout_json) | The arrangement to store has changed: an entry or a section moved, a section was folded or opened, something was removed, a layout was restored — by the user or by your code. Raised once per real change: a move to the rank already held raises nothing; of_set_layout raises it once when the layout it applies changes something. Carries the whole layout, not just what moved: persisting it is one assignment. Adds, the rail, of_clear and of_reset do not raise it: they are construction, not arrangement |
ue_item_rclicked (string as_keys, long al_x, long al_y) | The user right-clicked an entry — or pressed the Menu key, Shift+F10 on it: its address, "nav/docs", and al_x, al_y in screen pixels, the ones of_show of a contextmenu takes. One event per right click, and the entry is not selected. A PowerBuilder menu opens at the pointer of your window: m_menu.PopMenu(parent.PointerX(), parent.PointerY()). A greyed entry raises nothing |
ue_item_invoked (string as_keys) | The user invoked an entry — a click, Enter, Space, Enter in the search field —, the entry already selected included: open (or bring back) its page here, and a sheet closed by its cross opens again from the bar. A gesture only: of_select_item never raises it. After ue_selection_changed when the selection moved; never for a greyed entry, nor for a move ue_selection_changing refused |
ue_filter_changed (string as_text) | The user typed in the search field (ib_search): as_text now filters the bar; Escape empties it. is_filter set by your code is applied without this event |
ue_selection_changing (string as_from_keys, string as_keys) → boolean | Cancelable, asked before the selection moves — a gesture of the user, of_select_item or of_clear_selection — and only when ib_veto_selection = true (off by default). Return false to stay where you are: nothing moves, and the order of your code returns -4 |
ue_auto_width (long al_width) | The component has recomputed its ideal width — requires ib_auto_width = true |
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 #
A complete side menu #
// The whole bar in a single render
uo_nav.of_set_redraw(/*on*/ false)
// First section and its three entries
uo_nav.of_add_section(/*key*/ "folders", /*text*/ "Folders")
uo_nav.of_add_item(/*keys*/ "folders/recent", /*text*/ "Recent", /*image*/ "mono:img\clock.svg")
uo_nav.of_add_item(/*keys*/ "folders/customers", /*text*/ "Customers", /*image*/ "mono:img\user.svg")
uo_nav.of_add_item(/*keys*/ "folders/archives", /*text*/ "Archives", /*image*/ "mono:img\box.svg")
// Second section and its two entries
uo_nav.of_add_section(/*key*/ "tools", /*text*/ "Tools")
uo_nav.of_add_item(/*keys*/ "tools/import", /*text*/ "Import", /*image*/ "mono:img\import.svg")
uo_nav.of_add_item(/*keys*/ "tools/export", /*text*/ "Export", /*image*/ "mono:img\export.svg")
// One render for everything, then the first entry is selected
uo_nav.of_set_redraw(/*on*/ true)
uo_nav.of_select_item(/*keys*/ "folders/recent")
A collapsible rail that frees up space #
// Collapse into an icon rail : the bar shrinks by itself (and gets its width
// back when you expand it).
uo_nav.ib_collapsed = true
// ue_auto_width event of uo_nav : (long al_width)
// The bar has just adopted its ideal width : realign whatever sits to its right.
uo_content.x = uo_nav.x + al_width
uo_content.width = parent.width - uo_content.x
Accordion: collapsing a section #
// Collapse the folders section, which is rarely used
uo_nav.of_section(/*key*/ "folders").ib_collapsed = true
// ue_section_toggled event of uo_nav : (string as_key, boolean ab_collapsed)
// Remember the fold the user chose
of_save_preference("section_" + as_key, String(ab_collapsed))
Settings and Help pinned at the bottom #
A section without a title, pinned: no header line, two entries that stay at the foot of the bar even when the navigation, longer than the window, scrolls. A click there selects as anywhere else: ue_selection_changed receives foot/settings.
// A section WITHOUT a title (no header line), pinned at the bottom of the bar :
// it stays in view while the entries above it scroll
uo_nav.of_add_section(/*key*/ "foot", /*text*/ "")
uo_nav.of_add_item(/*keys*/ "foot/settings", /*text*/ "Settings", /*image*/ "mono:img\gear.svg")
uo_nav.of_add_item(/*keys*/ "foot/help", /*text*/ "Help", /*image*/ "mono:img\help.svg")
uo_nav.of_section(/*key*/ "foot").ib_pinned = true
Responding to navigation #
// ue_selection_changed event of uo_nav : (string as_from_keys, string as_keys)
// The address carries both levels : two sections may both have a "list" entry.
// Local variables
n_pbt_utils lnv_utils // autoinstantiate : nothing to create, nothing to destroy
string ls_keys[]
// Split the address : [1] = the section, [2] = the entry
if lnv_utils.of_split_path(/*keys*/ as_keys, /*out*/ ls_keys) < 2 then return
choose case ls_keys[1]
case "folders" ; of_open_folder(ls_keys[2])
case "tools" ; of_run_tool(ls_keys[2])
end choose
Opening the page of the current entry again #
ue_selection_changed only tells moves: clicking the entry already selected again does not move it. In an MDI application whose user closed the sheet, that is still the gesture that must open it again — ue_item_invoked answers every click, the current entry included.
// event ue_item_invoked of uo_nav : (string as_keys)
// Every click opens the page of the entry, even when already selected :
// a sheet closed by its cross opens again from the bar
of_open_sheet(as_keys)
Your own menu on the right click of an entry #
// event ue_item_rclicked of uo_nav : (string as_keys, long al_x, long al_y)
// Local variables
m_entry lm_menu
// A PowerBuilder menu opens at the pointer of the WINDOW ; al_x, al_y (screen
// pixels) serve a menu of the library : uo_menu.of_show(al_x, al_y)
lm_menu = create m_entry
lm_menu.m_root.PopMenu(parent.PointerX(), parent.PointerY())
destroy lm_menu
Searching the bar #
// A search field at the top of the bar : Ctrl+F reaches it, Escape empties it,
// Enter picks the first match
uo_nav.ib_search = true
// The code filters too : only the entries holding the text stay
uo_nav.is_filter = "inv"
Building the menu from a table of rights #
// Local variables
datastore lds_rights
// The table of rights of the user : one row = one entry of the menu
lds_rights = create datastore
lds_rights.DataObject = "d_menu_rights"
lds_rights.SetTransObject(SQLCA)
lds_rights.Retrieve()
// The columns that do not carry the name of a role are named by of_map
uo_nav.of_map(/*role*/ u_pbt_listbar.ROLE_SECTION, /*column*/ "menu_group")
uo_nav.of_map(/*role*/ u_pbt_listbar.ROLE_SECTION_TEXT, /*column*/ "group_title")
uo_nav.of_map(/*role*/ u_pbt_listbar.ROLE_KEY, /*column*/ "menu_code")
uo_nav.of_map(/*role*/ u_pbt_listbar.ROLE_ENABLED, /*column*/ "allowed")
// The entries are replaced ; a row left out is told by of_get_last_error
uo_nav.of_from_datastore(/*ads*/ lds_rights)
destroy lds_rights
Refusing a change of selection #
// The question is off by DEFAULT : switch it on to be able to refuse a move
uo_nav.ib_veto_selection = true
// ue_selection_changing event of uo_nav :
// (string as_from_keys, string as_keys)
// Returning FALSE keeps the user on the entry being left.
if of_has_unsaved_changes(as_from_keys) then
MessageBox("Entry", "Finish the current record before navigating away.")
return false
end if
return true
Updating an entry on the fly #
// Local variables
n_pbt_listbar_item lnv_entry
// The entry is named by its address : the section, then the entry
lnv_entry = uo_nav.of_item(/*keys*/ "folders/recent")
lnv_entry.is_text = "Recent (12)"
lnv_entry.is_image = "mono:img\clock_full.svg"
// Gray out or hide according to permissions, without rebuilding the bar
uo_nav.of_item(/*keys*/ "tools/import").ib_enabled = of_is_allowed("import")
uo_nav.of_item(/*keys*/ "tools/export").ib_visible = gb_expert_mode
Selection driven from code #
// Move the selection elsewhere : the accent band follows, and
// ue_selection_changed goes as for a click
uo_nav.of_select_item(/*keys*/ "folders/customers")
// Or clear it completely
uo_nav.of_clear_selection()
Rebuilding the bar #
// of_clear empties sections and entries ; of_reset also restores the component defaults
uo_nav.of_clear()
of_build_menu_for_profile()
Best practices #
- An entry is added by its address:
of_add_item("nav/home", ...). The section handle is now only for setting a property, andof_section("nav")hands it over on first access. - Use
mono:for icons: they recolor with the theme, light and dark alike, and stay legible once the bar is collapsed into a rail. - An empty section title works as a discreet grouping: a gap separates its entries from the ones above (a thin rule in the rail) without any header row appearing. Such a section does not fold.
- With the keyboard: the Up / Down arrows walk the titles and entries, Left / Right fold and open a section (the other way round right to left), Enter or Space picks, a letter jumps to the next line that starts with it (the same letter again goes to the next one), the Menu key or Shift+F10 opens the right click of the entry.
ib_collapsedshrinks the userobject down to the rail, but it does not move your other controls: handleue_auto_widthto take up the freed space, otherwise it just stays empty.- In rail mode only the icons remain: each entry tells its label on hover, and an entry without an icon shows its initial. Give each one an icon anyway — one initial is hard to tell from another.
- The question is only asked once you switch it on (
ib_veto_selection = true): do so only where you need to refuse a move, since every question costs a round trip to PowerBuilder. Once on, it is asked forof_select_itemandof_clear_selectiontoo, which then return-4on a refusal: set the initial selection before switchingib_veto_selectionon. - Prefer
ib_enabled = falseoverib_visible = falsewhen the entry will become available again: the menu does not change shape under the user's eyes.
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.
| Members | Role | Detailed in |
|---|---|---|
of_count · of_keys_at · of_has | Walk what the component holds | 3.2 Items |
of_reset | Put the component back to zero | 3.6 Resetting a component: of_reset() |
of_register_shortcut · of_clear_shortcuts | The component's keyboard chords | 3.5 Keyboard shortcuts |
of_is_created · of_is_ready · of_get_last_error | Whether it was born, whether it is ready, what failed | 3.7 Diagnostics |
of_save_as_png · of_save_as_jpg | Export the rendering as an image | 3.8 Exporting the rendering as an image |
of_set_redraw | Group changes into a single repaint | 3.10 Best practices |
of_preload_icons | Icons shown with no delay | Instant display: of_icon |
of_set_translation | Translate one of the component's labels | 5.2 Adapting a label: of_set_translation |
of_focus_webview | Give the component the focus | 6.4 Keyboard and focus |
of_print · of_print_to_pdf | Print, or write a PDF | 6.9 Printing |
of_set_property · of_get_property · of_component_name | Driving a property by its name | 3.1 The property engine |
is_accessible_name · is_accessible_description | The name and description a screen reader says | 3.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.