tab — u_pbt_tab #
← Component reference · Guide contents
Modern tabs hosting real PowerBuilder controls: repositionable tab strip, per-tab icons, closable tabs, mouse reordering.
▶ See it live — Demo application, Tab tile: the preview, the code behind it and this page, side by side.
At a glance #
| Userobject | u_pbt_tab |
| Item class | n_pbt_tab_page (one page = one tab) |
| Used for | Replacing a PowerBuilder tab with a themed strip while keeping your existing screens as pages |
| Opt-in options | ib_reorderable |
The key point: a page is not an HTML mockup, it is a PowerBuilder dragobject — a userobject, a DataWindow, a group of controls, or even another PBToolboxAI component. The component takes care of positioning, resizing and showing/hiding it when the tab changes. See Hosting real PowerBuilder controls.
Quick start #
// window open event : each page is a PB control already placed on the window
uo_onglets.of_add_page("clients", "Customers", uo_page_clients)
uo_onglets.of_add_page("factures", "Invoices", uo_page_factures, /*closable*/ true)
// One icon per tab, set on the page handle
uo_onglets.of_item("clients").is_icon = "mono:img\clients.svg"
uo_onglets.of_item("factures").is_icon = "mono:img\invoice.svg"
uo_onglets.of_select_page("clients")
// ue_selection_changed event of uo_onglets : (string as_from_id, string as_id)
of_charger_page(as_id)
Properties #
| Property | Type | Default | Purpose |
|---|---|---|---|
is_position | string | "top" | Placement of the strip: top, bottom (horizontal strip), start, end (vertical strip). Constants POSITION_TOP, POSITION_BOTTOM, POSITION_START, POSITION_END. START/END are logical and follow the writing direction |
is_overflow_mode | string | "menu" | What a band too small for its tabs does. menu (default) keeps every tab readable: those that no longer fit leave the band and the ··· button lists them all, the active tab always staying in view. compact keeps them all in the band and lets them shrink, down to the room an ellipsis needs. Constants OVERFLOW_MENU, OVERFLOW_COMPACT |
ib_reorderable | boolean | false | Opt-in: users can reorder the tabs by dragging their header with the mouse (raises ue_tab_reordered) |
ib_veto_selection | boolean | true | Ask before the active tab changes (raises ue_selection_changing, which can refuse). On by default: scripting nothing always lets the change happen. Set it to false to drop the round trip to PowerBuilder (~35 ms) where it would show |
ib_veto_close | boolean | true | Ask before closing a closable tab (raises ue_tab_closing, which can refuse). On by default, same rule |
ib_context_menu | boolean | true | Built-in context menu on a tab header: Close, Close others, Close to the right, Close all, greyed out when they have no target. On by default. The right-click selects the tab first: if the application refuses the change, no menu opens. Every close goes through ue_tab_closing, one question per page: the one holding unsaved work refuses on its own and stays open while its neighbours go. Set to false to show your own menu from ue_rclicked |
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 |
Properties of a page — n_pbt_tab_page #
Obtained through of_item(id), they can be changed on the fly, without rebuilding the strip.
| Property | Type | Default | Purpose |
|---|---|---|---|
is_title | string | "" | Caption of the tab. Accepts rich text markup |
is_icon | string | "" | Icon of the tab (accepted forms: path, mono:, tint:, DLL resource). Empty = no icon |
ib_enabled | boolean | true | false grays the tab out and prevents users from selecting it |
ib_visible | boolean | true | false hides the tab without removing the page; hiding the active tab activates a neighboring one |
ib_closable | boolean | false | Close cross on this tab. Rarely known when the page is added: it becomes true the moment the document it holds is saved, false while a job runs inside it |
is_shortcut | string | "" | Keyboard chord that activates this tab: "Ctrl+2", "Alt+F", "F6"… It answers wherever the user is in the window, the hosted page included — the chord is held by the DLL, not by the tab strip. Selecting this way is a click: ue_selection_changing is asked, ue_selection_changed follows. "" removes it |
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 |
Methods #
| Method | Purpose |
|---|---|
of_add_page (string as_id, string as_title, dragobject ado_page) | Adds a non-closable tab, without an icon, and hosts the control |
of_add_page (string as_id, string as_title, dragobject ado_page, boolean ab_closable) | Same, with a close cross if ab_closable |
of_add_page (string as_id, string as_title, dragobject ado_page, boolean ab_closable, string as_icon) | Same, with the icon set in the same call (the tab appears complete right away) |
of_insert_page (string as_id, string as_title, dragobject ado_page, integer ai_index) | Adds the page then moves it to position ai_index |
of_insert_page (string as_id, string as_title, dragobject ado_page, boolean ab_closable, string as_icon, integer ai_index) | Same, saying what of_add_page says : whether the tab closes, and its icon |
of_move_page (string as_id, integer ai_index) | Moves an existing tab; its hosted page and the selection are preserved |
of_select_page (string as_id) | Activates a tab — strictly equivalent to a user click (goes through ue_selection_changing then ue_selection_changed) |
of_remove_page (string as_id) | Removes the tab and hands the control back to its original window, hidden. The control remains usable and can be hosted elsewhere |
of_selected_key ( ) | Identifier of the active tab, "" if none |
of_get_layout ( ) | Reads the current arrangement back as JSON: the order of the tabs and the visibility of each. 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 no event — you supplied it |
of_item (string as_id) | n_pbt_tab_page handle of the page (created on first access) |
of_refresh_page (string as_id) | Refreshes the rendering of a page built off-screen, without flicker |
of_relayout ( ) | Republishes the page area so that the hosted control is repositioned (useful after a deferred display) |
of_preload_icons (string as_icons[]) | Warms up a batch of icons at startup: a tab opened later shows its icon instantly |
of_reset ( ) | Empties the strip: every hosted page is handed back to its original window, then the component returns to its pristine state |
of_set_redraw (boolean) | Groups a burst of changes into a single render |
of_save_as_png (string) · of_save_as_jpg (string) | Exports the rendering as an image |
of_add_pagereturns a negative code if the identifier is empty, already in use, or if the control passed in is not valid.
When there are too many tabs — they shrink to fit the band, but never below a readable width: the icon and the closing cross take their room on top of the title, never out of it. Those that no longer fit are taken out of the band, and a ··· button appears at its end: it opens the list of every visible tab, the active one ticked, and the choice switches to it. The active tab always stays on screen. The band does not scroll — that button is the way to the tabs beyond the fold. Nothing to code, the behaviour is automatic.
Events #
| Event | Raised when |
|---|---|
ue_selection_changed (string as_from_id, string as_id) | The active tab has changed — by click or through of_select_page. Same arguments as ue_selection_changing: the question and its outcome read the same way, and as_from_id is the page left |
ue_tab_closed (string as_id) | The user clicks the cross of a closable tab. The tab is already removed and the page handed back to its window |
ue_tab_reordered (string as_id, integer ai_index) | The user has finished dragging a header. ai_index is the new position, starting from 1 |
ue_layout_changed (string as_layout_json) | The arrangement changed — the user rearranged something, or your own code did. Carries the whole layout, not just what moved: persisting it is one assignment |
ue_selection_changing (string as_from_id, string as_id) → boolean | Cancelable, raised before the active tab changes. Raised by default; ib_veto_selection = false removes it. Return false to keep the user on their page |
ue_tab_closing (string as_id) → boolean | Cancelable, raised before a closable tab is closed. Raised by default; ib_veto_close = false removes it. Named to pair with ue_tab_closed: closing / closed. Return false to keep the tab (and its hosted page) open |
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 binder of business pages #
// open event : the page userobjects are already placed on the window
uo_onglets.of_set_redraw(false)
uo_onglets.of_add_page(/*id*/ "identite", /*title*/ "Identity", /*page*/ uo_identite)
uo_onglets.of_add_page(/*id*/ "adresses", /*title*/ "Addresses", /*page*/ uo_adresses)
uo_onglets.of_add_page(/*id*/ "compta", /*title*/ "Accounting", /*page*/ uo_compta)
uo_onglets.of_item("identite").is_icon = "mono:img\user.svg"
uo_onglets.of_item("adresses").is_icon = "mono:img\map.svg"
uo_onglets.of_item("compta").is_icon = "mono:img\euro.svg"
uo_onglets.of_set_redraw(true)
uo_onglets.of_select_page("identite")
Closable documents, opened on demand #
// Open a document in a new closable tab
uo_onglets.of_add_page(/*id*/ ls_cle, /*title*/ ls_libelle, /*page*/ uo_editeur, &
/*closable*/ true, /*icon*/ "mono:img\doc.svg")
uo_onglets.of_select_page(ls_cle)
// ue_tab_closed event of uo_onglets : (string as_id)
// The tab is already removed ; all that is left is releasing your business data.
of_liberer_document(as_id)
Mouse reordering #
uo_onglets.ib_reorderable = true
// ue_tab_reordered event of uo_onglets : (string as_id, integer ai_index)
// ai_index = new position, starting from 1 : we store the chosen order.
of_enregistrer_ordre(as_id, ai_index)
Vertical strip #
// Tab strip along the left side, useful when the captions are long
uo_onglets.is_position = uo_onglets.POSITION_START
Graying out or hiding a tab according to permissions #
// The path always goes through the page handle
uo_onglets.of_item("compta").ib_enabled = of_a_le_droit("compta")
uo_onglets.of_item("audit").ib_visible = gb_mode_expert
Reacting to the selection #
// ue_selection_changed event of uo_onglets : (string as_from_id, string as_id)
choose case as_id
case "identite" ; uo_identite.of_rafraichir()
case "adresses" ; uo_adresses.of_rafraichir()
case "compta" ; uo_compta.of_rafraichir()
end choose
Refusing a tab change or a close #
// Both questions are asked by DEFAULT: nothing to enable. These lines do the
// opposite, removing them where arbitration is useless.
uo_onglets.ib_veto_selection = false
uo_onglets.ib_veto_close = false
// ue_selection_changing event of uo_onglets : (string as_from_id, string as_id)
// Returning FALSE keeps the user on the page being left.
if of_page_modifiee(as_from_id) then
if MessageBox("Changes", "Leave without saving ?", Question!, YesNo!) = 2 then
return false
end if
end if
return true
// ue_tab_closing event of uo_onglets : (string as_id)
// Returning FALSE keeps the tab and its hosted page open.
return not of_page_modifiee(as_id)
Starting again from an empty strip #
// of_reset hands EVERY hosted page back to its original window before clearing
uo_onglets.of_reset()
uo_onglets.of_add_page("accueil", "Home", uo_accueil)
Best practices #
- The hosted controls must exist before the call: place them on the window at design time, the component takes care of hiding and repositioning them.
- Load the pages once in the
openevent, then drive the selection only: that is faster than continuously adding and removing pages. - Call
of_preload_iconsat startup if your tabs carry icons: without it, the first time a hidden tab is opened there is a short delay before the icon appears. - Both questions are asked by default: an event left empty always allows the action, you have nothing to do. Switch them off with
ib_veto_selection/ib_veto_closeset tofalseif arbitration is useless to you, since every question costs a round trip to PowerBuilder. - A hosted control is a native window: it draws on top of the web layer. No visual effect of the component can pass over it.
- Do not forget
of_reset()before rebuilding a strip: without it, reusing a page identifier fails and the previous controls stay on screen. - For resizable and detachable areas rather than exclusive pages, prefer dockcontainer.