6. Cross-cutting features #
← Language and RTL · Contents · FAQ →
This chapter covers the services available whatever the component: tooltips, hosting PowerBuilder controls, drag and drop from Windows Explorer, keyboard focus, image export, and the non-visual utility objects.
6.1 Simple and rich tooltips #
Component tooltip #
uo_button.is_tooltip = "Save the file (Ctrl+S)"
The text accepts rich text markup: [b], [br], [color=…], [picture=…]…
Rich tooltip (title + text + image) #
For Office ribbon style contextual help:
uo_button.is_super_tooltip_title = "Save"
uo_button.is_super_tooltip_text = "Writes your changes to the server." &
+ "[br][br][size-=15]Shortcut: Ctrl+S[/size-=15]"
uo_button.is_super_tooltip_image = "img\help_save.png"
The three properties are independent: a title alone, or a title plus text with no image, are both valid. As soon as one is_super_tooltip_* property is set, it takes precedence over is_tooltip.
Item tooltip #
Every item class (n_pbt_item and its descendants) carries the same four properties:
uo_toolbar.of_item(/*keys*/ "main/save").is_tooltip = "Save"
uo_tab.of_page(/*key*/ "clients").is_super_tooltip_title = "Customers"
uo_tab.of_page(/*key*/ "clients").is_super_tooltip_text = "128 records, last synced at 09:12"
Behavior #
- The tooltip is a themed native window, identical across all components (it is not confined by the boundaries of the webview).
- It does not appear when your application does not have the focus: hovering over a background window triggers nothing.
- It disappears when you switch to another application, and after 10 seconds at the latest.
of_reset()clears the tooltips set on the instance and on its items.
6.2 Hosting real PowerBuilder controls #
Container components do not display HTML in place of your screens: they host real PowerBuilder controls (userobjects, DataWindows, control groups), reparented as Win32 child windows. Your existing screens are reused as they are.
// Tabs: each page is a PB userobject
uo_tab.of_add_page(/*key*/ "clients", /*title*/ "Customers", /*page*/ uo_page_clients)
uo_tab.of_add_page(/*key*/ "invoices", /*title*/ "Invoices", /*page*/ uo_page_invoices, /*closable*/ true)
// Dockable panels: same principle
uo_dock.is_main = "doc" // center area
uo_dock.of_add_panel(/*key*/ "doc", /*position*/ uo_dock.POSITION_STACK, /*relative_to*/ "", /*size*/ 0, /*title*/ "Document", /*content*/ uo_editor)
uo_dock.of_add_panel(/*key*/ "explorer", /*position*/ uo_dock.POSITION_START, /*relative_to*/ "", /*size*/ 240, /*title*/ "Explorer", /*content*/ uo_tree)
uo_dock.of_add_panel(/*key*/ "props", /*position*/ uo_dock.POSITION_END, /*relative_to*/ "", /*size*/ 260, /*title*/ "Properties", /*content*/ uo_props)
What is handled for you:
- the positioning and resizing of the hosted control when the tab or panel changes size;
- showing / hiding it when the active tab or panel changes;
- floating, docked or auto-hidden panels (dockcontainer);
- clean destruction when the window closes.
⚠️ A hosted control is a native window: it is painted on top of the web layer. That is by design (your DataWindow stays crisp and fast), but it means no web effect — shadow, transparency, animation — can be drawn over it.
6.3 Receiving files from Windows Explorer #
A component can become a drop target for files dragged from Windows Explorer. The library installs a native drop target: you receive the full paths, which an HTML drop cannot give you.
uo_editor.ib_allow_drop = true
// ue_drop_files event of uo_editor: (string as_files[])
long ll_i
for ll_i = 1 to UpperBound(as_files)
of_open_file(as_files[ll_i])
next
| Event | Raised when |
|---|---|
ue_drag_enter ( ) | A file drag enters the component |
ue_drag_leave ( ) | It leaves without dropping |
ue_drop_files (string as_files[]) | The files are dropped — full paths |
The component provides the hover visual feedback itself (the drop zone is highlighted). Published by the components where dropping makes sense: statictext, codeeditor.
6.4 Keyboard and focus #
A PBToolboxAI component takes part in the keyboard navigation of your window just like a native control:
- the Tab key reaches it in the window tab order;
- keystrokes (arrows, Enter, Esc, typing) are handled by the component that has the focus;
- window shortcuts (Enter = default button, Esc = cancel) keep working even when the focus is inside a component.
To explicitly give the focus to a component's content (for example after opening a search panel):
uo_editor.of_focus_webview()
6.5 Exporting the rendering as an image #
A reminder from the shared foundation: every component can be exported exactly as displayed.
uo_tiles.of_save_as_png(/*path*/ "C:\temp\accueil.png")
uo_tiles.of_save_as_jpg(/*path*/ "C:\temp\accueil.jpg")
Handy for attaching a screen to an email, feeding a report, or documenting a user incident.
6.6 Dialog boxes and notifications #
Two services that require no control on the window:
// Themed modal dialog box (synchronous return)
n_pbt_messagebox lnv_mb
lnv_mb = create n_pbt_messagebox
lnv_mb.is_title = "Delete"
lnv_mb.is_message = "Permanently delete [b]12 files[/b]?"
lnv_mb.is_icon = lnv_mb.ICON_QUESTION
lnv_mb.of_add_button(/*text*/ "Delete", /*default*/ false, /*cancel*/ false)
lnv_mb.of_add_button(/*text*/ "Cancel", /*default*/ true, /*cancel*/ true)
if lnv_mb.of_show(/*hwnd*/ Handle(this)) = 1 then of_delete()
destroy lnv_mb
// Non-blocking "toast" notification in a screen corner
n_pbt_toaster lnv_toast
lnv_toast = create n_pbt_toaster
lnv_toast.is_title = "Import complete"
lnv_toast.is_text = "1,240 rows imported."
lnv_toast.is_kind = lnv_toast.KIND_SUCCESS
lnv_toast.of_show()
destroy lnv_toast
Full details: messagebox and toaster.
6.7 Non-visual utility objects #
Shipped in the same PBL, with no webview and no rendering: they are simple wrappers around the DLL. Create them, use them, destroy them.
Base64, hashes, identifiers — n_pbt_crypto #
Base64 (text or file), SHA hashes and unique identifiers live in the crypto component: of_base64_encode, of_base64_encode_file, of_sha256, of_hash_file, of_uuid… One page for everything that computes without showing anything.
Regular expressions — u_pbt_regex #
ECMAScript syntax, which PowerScript does not provide.
Without a key covering it, u_pbt_regex runs in demo mode: the text searched is 2048 characters at most; beyond that the call fails and is_last_error says so. For the key that covers your application to apply, set ipo_owner to the object using it (lnv_re.ipo_owner = this), as for crypto.
u_pbt_regex lnv_re
string ls_found[]
lnv_re = create u_pbt_regex
if lnv_re.of_is_match(/*pattern*/ "^[\w.]+@[\w.]+\.\w{2,}$", /*input*/ ls_email) then …
ls_year = lnv_re.of_match(/*pattern*/ "(\d{4})-(\d{2})-(\d{2})", /*input*/ ls_date, /*group*/ 1)
ls_clean = lnv_re.of_replace(/*pattern*/ "\s+", /*input*/ ls_input, /*replacement*/ " ")
ll_nb = lnv_re.of_match_all(/*pattern*/ "[A-Z]{2}\d{6}", /*input*/ ls_text, /*matches*/ ls_found)
destroy lnv_re
| Method | Purpose |
|---|---|
of_is_match (pattern, text [, ab_ignore_case | al_flags]) | Does the text match? |
of_match (pattern, text, ai_groupe [, al_flags]) | First match (0 = whole match, 1.. = capturing group) |
of_match_all (pattern, text [, ai_groupe, al_flags], ref as_res[]) | All matches; returns the count |
of_replace (pattern, text, replacement) | Replaces every occurrence ($1… allowed) |
Options can be combined: FLAG_IGNORE_CASE (1), FLAG_SINGLELINE (2, . also matches line breaks).
ZIP archives — u_pbt_zip #
u_pbt_zip lnv_zip
lnv_zip = create u_pbt_zip
lnv_zip.of_compress(/*zip*/ "C:\temp\livraison.zip", /*src*/ "C:\appli\export") // file OR folder
lnv_zip.of_extract(/*zip*/ "C:\temp\livraison.zip", /*dest*/ "C:\appli\import") // folder created if missing
destroy lnv_zip
Both methods return a boolean.
Without a key covering it, u_pbt_zip runs in demo mode: what it compresses (a file or a folder, in total) and the archive it extracts are 2048 bytes at most; beyond that the method returns false and is_last_error says so. Set ipo_owner to the object using it (lnv_zip.ipo_owner = this) for the key of your application to apply.
6.8 Library information #
// DLL version: a long, major x 10000 + minor x 100 + patch
long ll_version
ll_version = PBT_GetVersion() // 40000 = 4.00.00
| Global function | Purpose |
|---|---|
PBT_GetVersion ( ) → long | Library version: 40000 = 4.00.00 (major × 10000 + minor × 100 + patch) |
PBT_CheckRuntime (ref string, long) | Version of the installed WebView2 runtime (≤ 0 = missing) |
PBT_GetLastErrorMessage (ref string, long) | Last error message for the process |
PBT_LicenseStatus ( ) | License state — see License |
6.9 Printing #
Every component knows how to print itself, as displayed, with no intermediate DataWindow.
// Silent: a PDF on disk, nothing to click
uo_grid.of_print_to_pdf(/*path*/ "C:\etats\ventes.pdf")
// Landscape, for a wide grid
uo_grid.of_print_to_pdf(/*path*/ "C:\etats\ventes.pdf", /*landscape*/ true)
// With a dialog: the user picks their printer and sees the preview
uo_grid.of_print()
// Straight to the system print dialog
uo_grid.of_print(/*system_dialog*/ /*dialogue systeme*/ true)
| Method | Purpose |
|---|---|
of_print_to_pdf (string as_path) · of_print_to_pdf (string, boolean ab_landscape) | Writes a PDF without showing anything. Returns only once the file is written (0 = done) |
of_print ( ) · of_print (boolean ab_system_dialog) | Opens the print dialog. Returns as soon as the dialog is up: what the user does with it is theirs |
⚠️ What is printed is what is RENDERED. A virtualized grid prints only the rows it holds: for a complete report, first switch to a layout that shows them all (pagination), or export the data rather than the rendering.
Return code -6 means the WebView2 runtime is too old to print (1.0.1108 for the PDF, 1.0.1587 for the dialog). Everything else follows the common return codes.