PBToolboxAI v4 ← Site

11. Migrating from 3.0 to 4.0 #

← Migrating from 2.0 to 3.0 · Contents · Release notes →


4.0 removes two methods from the thirty-two components of 3.0 — of_set_data of the crosstab, renamed of_from_datastore (11.33), and of_transform of n_pbt_xml, which goes with XSLT (11.31) — and changes signatures the compiler shows you line by line: three properties renamed (id_minimum and id_maximum of the progress bar, 11.6; il_doc_line of the code editor, 11.28), event arguments added or renamed (ue_tile_moved, 11.6; ue_failed of the REST client, 11.6, and of the video, 11.26; ue_error of the picture, 11.25; ue_cell_double_clicked, 11.33; ue_closed of the command palette, 11.36; the toaster's ue_toast_clicked, ue_toast_action and ue_toast_dismissed, 11.38), of_count and of_keys_at in long (11.34) and methods that now return a code (of_refresh, of_select, of_show, of_scroll_to_end, of_set_header…). 11.6 also aligns two counts, one event and the path of the JSON tree with the rest of the library: that is where, with the sections cited, code that compiled needs touching. What changes is concentrated on the ribbon (ribbon), which went through a complete review. Two changes ask you to read your code again: the questions asked before a ribbon gallery tile and before a side bar (listbar) selection (11.1) and the handles freed by a removal (11.2). The others fix behaviours you may have worked around (11.3) — among them the right-click on an item, which now produces a single event in every component, and the breadcrumb (breadcrumb), whose addresses and refusals were reviewed. Then come the n_pbt_menu2ribbon generator (11.4) and what was added (11.5). The tabs (tab) follow the same rule: their two questions, before a tab change and before a close, are now optional (11.1). Finally, themes follow the application (11.7): the application accent survives a change of theme, and a theme axis left empty follows the application's. The security review of crypto (11.8) closes a JWT algorithm confusion, replaces several silences with a refusal that states its reason and sets the rule for the relative paths of files.

The complete review of 28/09/2026 read every shipped component again; what it changes has one section per component: restclient (11.9), speechout (11.10), statictext and rich text (11.11), breadcrumb (11.12), buttonbar and the window keyboard (11.13), button (11.14), menubar (11.15), ribbon (11.16), toolbar (11.17), statusbar (11.18), dockcontainer (11.19), tab (11.20), listbar (11.21), tilesbox (11.22), stepbar (11.23), progressbar (11.24), picture (11.25), video (11.26), pdfviewer (11.27), codeeditor (11.28), jsontree and xmltree (11.29, 11.30), xml (11.31), json (11.32), crosstab (11.33), shellexplorer (11.35), commandpalette (11.36), radialmenu (11.37), toaster (11.38) and messagebox (11.39); the common base has two sections, 11.34 and 11.40. One rule runs through them, PowerBuilder's own (SelectTab raises SelectionChanged): a change requested by your code raises the same event as a user gesture, and asks the veto question if you switched it on; the method returns -4 when you refuse. It holds for a selection (of_select_*), an opening or a folding (of_expand, of_collapse, of_open), a zoom, a full screen, a chapter; nothing is raised when nothing changes, and of_reset stays silent. Setting a VALUE (a check mark, a text, a volume, a filter) stays silent, like SetItem. ue_layout_changed is raised in both cases: it says that a layout to store has changed.

11.1 The questions before a change are now opt-in #

In 3.0 the ribbon asked the ue_gallery_selection_changing question by default before keeping a tile: ib_veto_gallery was true. In 4.0 it is false, like every cancelable event of the library: each question costs a round trip to PowerBuilder, and only an application that answers it should pay for it. An application that did not script the event sees no difference. New: of_select_gallery_item raises ue_gallery_selection_changed like a click and, with the question switched on, asks it too; a refusal makes it return -4, the tile does not change.

// 4.0 : the question is only asked on request. If your ue_gallery_selection_changing
// returns false for some tiles, switch it on where the ribbon is built.
uo_ribbon.ib_veto_gallery = true

The recipe: search your ribbon descendants for ue_gallery_selection_changing; wherever it is scripted, add ib_veto_gallery = true to the build. Wherever you wrote ib_veto_gallery = false to switch the question off, the line is now useless and can go.

The side bar (listbar): ib_veto_selection #

Same rule: in 3.0 the listbar asked ue_selection_changing by default before every selection change (ib_veto_selection was true). In 4.0 it is false. If you scripted ue_selection_changing to refuse a move, set ib_veto_selection = true: without it the event is no longer raised and the selection moves without asking you. When switched on, the question is asked for a selection requested by your code too (of_select_item, of_clear_selection), as in 3.0; a refusal now makes them return -4, and the selection does not move. ue_selection_changed fires as before.

// 4.0 : the listbar only asks on request. If your ue_selection_changing
// returns false (unsaved changes), switch the question on where the bar is built.
uo_nav.ib_veto_selection = true

The recipe: search your u_pbt_listbar descendants for ue_selection_changing; wherever it returns false, add ib_veto_selection = true to the build. Wherever you wrote ib_veto_selection = false to switch the question off, the line is now useless and can go.

The tabs (tab): ib_veto_selection and ib_veto_close #

Same rule, two questions: in 3.0 the tab component asked ue_selection_changing by default before every tab change and ue_tab_closing before every close (ib_veto_selection and ib_veto_close were true). In 4.0 both are false. If you scripted ue_selection_changing or ue_tab_closing to refuse, set ib_veto_selection = true or ib_veto_close = true: without it the event is no longer raised and the tab changes or closes without asking you. When switched on, the question is asked for of_select_page too, as in 3.0, but before the tab changes: a refusal makes it return -4 and nothing moves; an agreement changes the tab at once, ue_selection_changed fires as before, and of_selected_key() read right after already returns the new key.

// 4.0 : the tab only asks on request. If your ue_selection_changing or
// ue_tab_closing returns false (unsaved changes), switch the questions on.
uo_tabs.ib_veto_selection = true
uo_tabs.ib_veto_close     = true

The recipe: search your u_pbt_tab descendants for ue_selection_changing and ue_tab_closing; wherever one returns false, add the matching line to the build. Wherever you wrote = false to switch a question off, the line is now useless and can go.

11.2 Adds say what they refuse, removals free their handles #

In 3.0, an add the ribbon ignored — a key already taken, a menu entry under a plain button — still returned 0, and nothing appeared. In 4.0 it returns -5, and the code of the send (-2 when the component is not created) instead of a fixed 0. And a removal frees the handles of what leaves: a variable holding the handle of a removed control is no longer valid.

What3.04.0
Adding a key already taken (tab, group, control)0, ignored-5
Menu entry, combo choice or tile under a control of another kind0, ignored-5
of_add_tab, of_insert_tab, of_add_group, of_add_contextual_*, of_add_qat on a component not created0-2
A handle kept after of_remove_tab, of_remove_group, of_remove_item or of_clearstayed in memory until the ribbon was destroyedfreed: IsValid() returns false
// 4.0 : after a removal or of_clear, ask the component for the handle again
uo_ribbon.of_clear()
uo_ribbon.of_add_tab(/*key*/ "home", /*title*/ "Home")
uo_ribbon.of_add_group(/*keys*/ "home/clip", /*title*/ "Clipboard")
uo_ribbon.of_add_button(/*keys*/ "home/clip/paste", /*label*/ "Paste", /*image*/ "")
uo_ribbon.of_item(/*keys*/ "home/clip/paste").ib_enabled = false

The recipe: do not keep a handle beyond a removal or an of_clear — ask for it again with of_tab, of_group, of_item or of_menu_item, which costs nothing. Where a build error matters to you, test the code the add returns.

11.3 Other behaviours you may notice #

When the window is narrow, the ribbon folds its groups into a button that opens a panel. That panel is where the most visible differences come from:

What3.04.0
Button, or main part of a split, clicked in the panel of a folded groupue_menu_selected("", false)ue_clicked(address)
Menu entry chosen in that panelincomplete addressfull address, cascade included
Greyed control, or hidden entry, in that panelanswered / was showndoes not answer / is not shown
Big split shrunk for lack of rooma click only opened the menutwo parts: the command and the menu arrow
Menu entry carrying the key of its control (home/clip/paste/paste)ue_clicked("home/clip/paste")ue_menu_selected("home/clip/paste/paste", …)
of_menu_item("…/exp/pdf") when a cascade also holds pdfcould aim at …/exp/more/pdfaims at the entry the address names
as_from_keys of ue_gallery_selection_changed with no previous tilethe gallery's address""
Reading back a Quick Access Toolbar handle, il_color of a colour picker, ii_visible_items, is_keytip of a tab, il_color of a contextual group, of_count() of a galleryempty, -1 or 0the value on screen
of_keys_at of a combo"home/font/cb/cb:Arial""home/font/cb/Arial": a choice's key is its label
of_ctx_group(k).il_color when a tab also carries the key kcoloured the tabcolours the banner only
Item colours (il_back_color…) and is_tooltip of a tab or a groupno effectapplied; a group's tooltip shows on its caption
of_remove_item("save") on a Quick Access Toolbar button-5the button is removed
of_add_app_menu_separator("saveas/s1")a line at the root of the menuthe line in the submenu of saveas; a key without / does not change
of_clear() while a tab was activeno eventue_selection_changed("")
Spinner with a decimal step (0.1)0.300000000000000040.3
Alt shortcut (keytip) on a greyed control, ribbon minimizedthe ribbon stayed openit closes, nothing runs
toolbar: greyed combo, spinner or gallery in the chevron panelanswereddo not answer
All: right-click on an item (ue_item_rclicked, ue_panel_rclicked, ue_row_rclicked…)the page also sent the component's event, which no item component passed on to PowerBuilderone event, the item's; a component's ue_rclicked, where there is one, fires outside the items only
All: a file drop (ib_allow_drop)ue_drag_leave then ue_drop_files; paths cut at 260 charactersue_drop_files alone, whole paths; of_reset removes the drop target
breadcrumb: of_path() with a hidden segment, or in the demo versionthe displayed keys: the hidden one skipped, the first segments cutthe full address of where you are: of_truncate(of_path()) lands; the typed field starts from the same
breadcrumb: of_keys_at(n); of_has("computer/C:")the bare key; looked among the menu branchesthe segment's address; looks among the segments
breadcrumb: reading a deep address handle, of_item("a/b/c").is_textread the last level alone: empty on a repeated keythe segment at that address
breadcrumb: of_truncate, of_remove_item, of_clear_children, of_remove_child on an unknown address or a repeated bare key; of_add_child under a missing segment or on a branch already taken; of_add_item of an address that would land elsewhere0, ignored (or the last key alone kept)-5
breadcrumb: a handle kept after of_clear, of_remove_item, of_truncate, of_clear_children or of_remove_childstayed in memoryfreed: IsValid() returns false
breadcrumb: typed field left unchanged, or window switched (Alt+Tab) while typingue_path_entered raisednothing; the typing stays when the window changes
breadcrumb: greyed segment; Enter on a segmentits chevron opened and it took a drop; Enter also pressed the window's default buttonentirely inert; Enter clicks the segment only
buttonbar: Enter while another button of the bar has the keyboard focusfired the default button (is_default)fires the focused button, as Windows does; the default fires when no button of the bar has the focus
buttonbar: greyed or hidden default or cancel button; is_shortcut of a greyed or hidden buttonEnter, Escape or Alt+letter swallowed with no effect, even in a PowerBuilder control of the windowthe key stays with the window
buttonbar: multi-letter is_shortcut ("Save")Alt+S only worked while the bar had the focusAlt+S works from anywhere in the window (the first letter, as documented)
buttonbar: of_add_button / of_insert_button on a key already taken or holding / or a vertical bar; of_remove_button / of_move_button on an unknown key0, ignored-5
buttonbar: of_remove_button, of_clear_buttonsthe button's Alt+letter shortcut, colours and tooltip stayed (a button re-created under the same key took them back); the handle stayed in memorythey leave with the button; the handle is freed: IsValid() returns false
codeeditor: reading back is_text of a CRLF textin LF (in CRLF only while the editor was folded): is_text = ls_original was falsewith the line endings it was given, folded or not
codeeditor: reading back is_syntax after SYNTAX_CSHARP, SYNTAX_CPP, SYNTAX_JAVA, SYNTAX_XMLthe grammar in use ("c", "html")the value written
codeeditor: of_insert_text while read-only or foldedinserted while read-only; refused while folded, but 0-4, nothing is inserted
codeeditor: ii_doc_linea band only, often out of view, absent in wrap mode; an integerthe line comes to the middle of the view (a folded region hiding it opens), band in wrap mode too; caret and selection untouched; a long — renamed il_doc_line in 4.0 (section 11.28)
codeeditor: #regionopen with ib_foldingrewritten as #region in the text (is_text, and after folding)never touched; only the folded display shows #region
codeeditor: ue_find_resulttwice per of_find, on every keystroke, and (0, 0) when the bar closed; raised even with ib_search_enabled = falseonce per search or step; while typing when the count changes; never on closing; of_find without effect when the search is disabled
codeeditor: ue_caret_changedsilent while typing; folded, the DISPLAYED lineraised while typing too; folded, the gutter's line
codeeditor: Ctrl+Z after Enter or Tab; Enter / Tab while foldedthe undo history was wiped; folded, the key changed the display and was lost afterwardsevery keystroke can be undone; folded, the key has no effect
codeeditor: ib_folding = true with ib_line_numbers = falsethe regions stayed closed, with no marker to open thema narrow gutter keeps the markers
commandpalette: of_add_command on a key already taken or holding / or a vertical bar; of_remove_command on an unknown key0 (a key taken gave two twin commands)-5
commandpalette: of_open / of_register_shortcut with neither ipo_owner nor ipo_receiver0, and no event ever arrived-5
commandpalette: the chord pressed while the palette is openno effectcloses the palette
commandpalette: a palette object destroyed (page or window closed)its chord stayed registered and swallowed the key in the window, with no event at allthe chord leaves with the object, and a palette it had opened closes
commandpalette: chord shown on a row, on a non-Latin keyboard, with a digit and Shift, Ctrl+Space, Ctrl+Up, Ctrl+Del, or an F key alonedid not answer; Win + the letter ran the command whose chord was that single letteranswers: the physical key, read the way the DLL reads it; Win is never a modifier
commandpalette: a command whose chord is Ctrl+C, Ctrl+V, Ctrl+Z or Ctrl+A, while typingthe command ran instead of the copy, the paste…the search box keeps these four keys
commandpalette: of_resetkept is_recent, an open palette and the chord already registeredempties them, closes it, and brings the registered chord back to SHORTCUT_DEFAULT
commandpalette: pbm_custom02 of the ipo_owner windowtriggered at every palette eventnever: only an ipo_receiver that is set still receives it
All: reading back a long text value (is_text of a large script)slower and slower with the size (several seconds); cut without warning beyond a million charactersproportional to the size; whole
n_pbt_utils.of_dir_has_subfolderscounted hidden and system folders and junctions; unreadable beyond 260 charactersignores them, as the Explorer does (the ab_include_hidden overload counts them); reads long paths
crosstab: thousands separator of of_set_value_formatapplied to ALL the measures; "," not recognised ($1 234 in French)a setting of THIS measure; ",", ".", " " recognised; "" = default of the Options menu
crosstab: of_set_member_filter(field, "")no member kept: empty tableremoves the filter, like of_set_member_order("")
crosstab: is_show, is_conditional_formatting, of_set_value_format before of_add_value_fieldlost without a wordkept, read back as set, applied when the field enters Values
crosstab: sorting a column or folding a group with the mouseno eventue_layout_changed, like everything of_get_layout saves
crosstab: dates of a DataStore on a non-French machine (3/15/2025, 15.03.2025)grouped into a wrong or empty month; dates sorted on the year alonethe right month; the column read back as yyyy-mm-dd, sorted in time order
crosstab: numbers shown in German, Italian, Spanish, Portuguesethe American way (1,234.5)in the display language (1.234,5 in German)
crosstab: of_export_csvnumbers with a dot (1436.5), read as text by a French Excel; a =… label ran when the file was openeddecimal separator of the display language; an apostrophe before = + - @
crosstab: of_export_xlsxhidden totals and subtotals exported anyway; top grand total ignored; a symbol « before » moved afterthe table as on screen
crosstab: « Add to values » from the menu of a field already in Valuesreplaced its valuesadds a value, like a drag
crosstab: « Remove » from the menu of a filtered fieldthe filter stayed active, unseenthe filter leaves with the field
crosstab: AGG_DISTINCT_COUNT on a column with empty cellsempty counted as a valueignored, as in Excel
crosstab: formula naming a calculated measureaccepted, was worth 0refused through ue_calc_field_error; its messages are in English
crosstab: ue_layout_changed with a brace in a member; ad_value of ue_cell_double_clickedlayout cut short; value 0 when a row field is named value, exponent lostread whole
crosstab: ib_enabled = false, from the keyboardEnter opened the menu of a chip, Ctrl+C copiedsilent
crosstab: keyboardthe focus fell back to the top of the page after each action; no way to move a fieldthe focus stays on the element used; the menu of a chip moves it (another zone, left, right)
crosstab: dark themenegatives and icon arrows hard to read; selected colour-scale cell unreadablea palette of its own in dark mode
crypto: of_jwt_sign with JWT_EDDSAthe header said "alg":"EDDSA", which other libraries (jose, PyJWT, jjwt) refuse"alg":"EdDSA", the registered name; a 3.0 token still verifies
crypto: of_generate_keypair of an unknown kind or size ("ec_p256", 1024)0 and an RSA 2048 pair, without a word-5, both PEM empty, is_last_error names the accepted KEY_*
crypto: of_hash_file("SHA-256", …); unknown algorithmempty, and is_last_error said "file cannot be read" of a perfect filethe name is read as of_hash reads it (SHA-256 = HASH_SHA256); an unknown algorithm is named as such, apart from an unreadable file
crypto: of_base64_encode_file of a file too large (over 64 MB in 32 bits, 512 MB in 64 bits); of a missing file in demo"file cannot be read"; "demo limit""too large for this process"; "file cannot be read" — is_last_error gives the real reason
crypto: is_last_error after a successful native call (of_crc32, of_hash in MD5, of_hash_file…)kept the error of the previous callempty
crypto: empty HMAC key; non-numeric exp/nbf of a JWT; of_password_verify of a value over 10 000 000 rounds; of_random_password of an unknown setan obscure engine error; a token valid forever; minutes of computing; CHARSET_ALL taken without a wordrefused at once, is_last_error says why
crypto: of_base64_decode_to_file("", path)-50, an empty file
dockcontainer: of_add_panel with an unknown as_relative_to, a control already hosted, a key holding / or a vertical bar, or POSITION_STACK on the main panel0: the panel docked against the root, the control was hosted twice, or the panel stacked on the main one stayed invisible-5, and the control is not touched
dockcontainer: of_move_panel to an unknown target, or POSITION_STACK on the main panel0: the panel went against the root, or nothing moved-5
dockcontainer: of_remove_panel, of_resetthe control came back to its window hidden, at the dock's position and sizeit comes back in its place, at its size and with the visibility it had before of_add_panel
dockcontainer: is_main set before of_add_panel; is_main = ""; is_main of a stacked panelsilently ignored; nothing was sent; its stack neighbours became invisiblekept and applied when the panel is added; the main panel is removed; it leaves its stack, the others stay visible
dockcontainer: of_set_layout of a layout that knows none of the panels, or of a text that is not a layoutthe dock was emptied, or the whole message was rejected; 0the panels stay, placed next to the others (never on the main one); -5 for a text that is not a layout
dockcontainer: ue_layout_changedraised on drag, resize and docking only: a layout saved on this event forgot a closed panel or the active tabalso raised when a panel is closed, hidden, selected or renamed, and when the main panel changes
dockcontainer: click on the tab (or header) of the panel already activeue_panel_selected on every clicknothing: the event reports a CHANGE of active panel
dockcontainer: unpinned panel (ib_pinned = false)its strip only opened on a click (the doc said "on hover")it opens on hover (400 ms) or on a click; opened on hover, it folds back when the mouse leaves it
dockcontainer: standard PB control (MultiLineEdit, button…) in a floating panelits notifications (modified, clicked, colours) no longer arrivedthey arrive as when it is docked
dockcontainer: hiding a floating panel (ib_visible = false)its window stayed openit closes; shown again, the panel comes back docked in its place
dockcontainer: floating windowits useful area was smaller than the panel (title bar taken from it), it could leave the screen, and a title with markup showed the tagsthe useful area has the size of the panel, the window stays on screen, the title is plain text
dockcontainer: splitter from the keyboardthe first arrow made it jump to 50 %it starts from its displayed position
json: null value given to of_set_number, of_add_number, of_set_boolean, of_add_boolean, or null or empty value given to of_set_raw / of_add_rawthe whole document became null (of_text returned null) or stopped being JSON; a null boolean was written falsenull is written, the rest of the document is intact
json: writing into a LOADED document whose key is written "k" : v or with an escaped character, followed by a line break, or whose root is an array; of_set_members on such a documentthe key was added a second time (reading it back gave the old value), the write returned -4 wrongly, or the document was replaced by the fragmentthe value is replaced or added as on a built document; of_set_members returns -4 when the root is not an object
json: of_set_*("items/1/qty", …) — a NEW key in an object held in an array-40: the key is added to the first element; an array is crossed by an index that exists, only an object key is created
json: path with an empty segment (/name, a//b, a/)keys named "" were created; of_remove could remove another value-5, nothing is written; a reader answers "absent"
json: of_load of a badly written number (-, 01, +1, .5, 1., 1e)0, and of_is_valid true-4, like any invalid JSON
json: of_get_number of a number with an exponent (1e-7, 2.5E+3) or more than 9 decimalsexponent ignored (2.5E+3 read as 2.5), decimals cut at 9exponent resolved, 17 significant digits kept
json: reading or editing a big document (an 80 KB API response, 2000 rows)seconds per call (20 s for of_count), the application looked frozena time proportional to the size; reading the rows one by one (rows/1, rows/2…) or walking the keys with of_name no longer starts over from the beginning
listbar: of_add_section / of_insert_section on a taken key or one holding / or a vertical bar; of_add_item / of_insert_item on a taken address or under a section the bar did not add; of_remove_item, of_move_item, of_select_item, of_remove_section, of_move_section on an unknown address0, ignored; an entry under an unknown section showed under the last section-5; of_add_section also returns -2 when the component is not created
listbar: a handle held after of_remove_item, of_remove_section or of_clearstayed in memoryfreed: IsValid() returns false
listbar: removing or emptying what holds the selection (of_remove_item, of_remove_section, of_clear)the selection left without an event; after of_clear, of_selected_key still returned the old oneue_selection_changed(old, "")
listbar: ue_layout_changedsilent when a section folds and when an entry is removed; raised on entering the railraised on a fold (click or a section's ib_collapsed) and on a removal; no longer for the rail, which is not part of the layout
listbar: rail (ib_collapsed = true)the entries of a folded section disappeared; an entry without an icon showed an empty square; of_reset left the bar narrowevery entry, a thin rule between sections, the initial of an entry without an icon, the label on hover; of_reset gives the bar its width back
listbar: a section's is_image; a colour set on section k when an entry also has the key kno effect; the entry took the colour toothe icon is drawn before the title; only the section takes the colour
listbar: keyboardafter a label change or a selection, the arrows stopped answering; with no selection the bar was out of the Tab order; section titles only folded with the mousethe focus stays on the entry; one Tab stop; the titles are reached with the arrows and fold with Enter or Space
listbar: disabled entry (ib_enabled = false); ib_auto_width on an expanded barfaded label, hard to read in light themes; reported the width it already hadthe theme's disabled colour; the width its longest line asks for
listbar: dragging an entry (ib_reorderable)button released outside the window: the entry still followed the mouse; the closing click could select the dropped entrythe gesture ends when the mouse comes back; the end of a drag selects nothing
menubar: ue_menu_openingraised beside the dropdown: what it changed (greying Paste…) only showed at the next openingthe dropdown waits for the event to return: the change shows in this opening
menubar: of_item, of_remove_item on an item whose key also exists in another cascade (file/export/pdf, file/print/pdf)could reach the other item (the first one found); the shortcut shown on one fired the otherreach the item the address names; a key alone no longer searches the cascades
menubar: of_add_menu, of_add_item, of_add_separator on a key already taken, holding / or a vertical bar, or under an unknown menu or parent; of_remove_item / of_remove_menu of an unknown address0; an item with a mistyped parent landed at the root of the menu, a key already taken made twin items-5
menubar: a handle or a shortcut after of_remove_item, of_remove_menu or of_clearthe handle stayed in memory; the shortcut stayed armed and swallowed the key, which no longer reached the applicationhandle freed: IsValid() returns false; shortcut disarmed
menubar: item greyed, hidden or removed while its dropdown is open; shortcut of an item under a greyed cascadethe click or the key still raised ue_item_selectednothing
menubar: item colours (il_back_color…) of a menu or an item; is_tooltip of a menuno effect; a colour read back -1applied: on the menu title, on the item in the dropdown; the tooltip of a menu shows on its title, an item of the dropdown shows none
menubar: keyboardthe arrows got stuck on a greyed menu; in RTL, left and right were swapped; Alt+letter of a hidden menu hid the same letter of another menu; after Alt+letter and a pick, the next Alt switched offgreyed menus are skipped; the arrows follow the writing direction; only a shown menu answers its letter; the next Alt lights the bar again
menubar: a cascade whose items are all hidden; of_count("file"), of_keys_atwas picked like a command (file/export); separators were counted (__sep0)greyed; only items are counted
menubar: of_item("file") — an address of one levelread the menu back, and wrote to a "file" item that does not existnames nothing: use of_menu("file")
Any text read from a component or a JSON: \b and \f escapesstayed written as they weredecoded (backspace, form feed)
messagebox: a dialog that could not open (WebView2 runtime missing, engine not ready)of_show and the shortcuts (of_info…) returned 0, like a dismissal: of_info let the caller believe a dialog had been shown-4 (-6 when the runtime is missing), the reason through of_get_last_error(); of_choose and of_prompt return "" and of_get_last_error says so. A = 1 test for "confirmed" stays right
messagebox: Esc or Alt+F4 while a cancel button still counts down (of_add_button_timed)closed the dialog (0)do nothing until the delay is over: it is there to make the user read
messagebox: the text of the message and of the instructioncould not be selected; pressing on it moved the dialogcan be selected and copied (Ctrl+C copies the selection, or the whole dialog with nothing selected); the dialog moves by its title, its icon, its margins
messagebox: an input longer than about 900 characters (of_prompt, ib_input)came back empty, like a cancelcomes back whole
messagebox: al_hwnd = 0, a dialog opened from another one, a dialog moved then enlargedthe whole desktop was disabled; the window came back to life while the first dialog was still waiting; the dialog jumped back to the centrenothing is disabled without an owner; the window stays disabled while any dialog waits on it; the dialog keeps its place
progressbar: empty or inverted range (id_maximum = 0 then id_value = 0)0 % whatever the value100 % once the value reaches the maximum (an empty batch is finished), 0 % before
progressbar: the percentage shown, the ringrounded to the nearest (99.5 % showed "100%"); ring fixed at 128 px; while waiting, the ring spun with the arc of the value (empty at 0: nothing moved)rounded down: 100 % means finished; the ring follows the size of the control; while waiting, a fixed arc spins; fewer animations asked by Windows: the waiting state pulses
Any double property read back (id_value…) on a machine with a decimal commathe decimal part was misread (0.5 did not read back as 0.5)read back exactly, whatever the separator of the machine
speechout: a new of_speak (or of_close) during a readingthe cut reading raised no ue_stoppedevery reading that started ends on a ue_stopped
speechout: il_timeout_ms = 0cut the synchronous reading after 5 sno limit (24 hours at most)
speechout: a voice that could not be created (WebView2 runtime missing, page refused)of_speak, of_speak_from, of_pause, of_resume, of_enqueue, of_clear_queue returned -1, of_open the raw DLL code-2 (component not created, as everywhere), is_last_error says why; -4 when the page did not answer
speechout: of_speak_sync called from an event raised during another of_speak_synccut the outer call's reading, which still returned 0refused: -4, is_last_error = "already speaking synchronously"
speechout: of_speak_from past the last sentence0, nothing was read-5
speechout: of_is_speaking() right after of_speakfalse until the voice list had arrivedtrue from of_speak on
speechout: of_pick_voice("", gender), or the voice list unreadablepicked in the page's language; a failure emptied is_voice"" = is_lang; a failure keeps is_voice and fills is_last_error
speechout: of_voice_used, of_duration, of_spoken_text right after changing is_lang or the dictionarycould answer with the previous settingsanswer with the settings just set
speechout: script error in the voice's pagesilentue_error("script error: …")
statictext: il_back_color with ii_corner_radiusthe background filled the whole rectangle, only the border line was roundedthe background follows the corners; outside them, the color of the object the block sits on, which the userobject takes too
statictext: is_marquee_direction with a right-to-left reading directionreversed: MARQUEE_START scrolled towards the end, "left" towards the rightMARQUEE_START scrolls towards the start of reading; "left" / "right" stay physical
statictext: reading back il_back_color, ii_marquee_speed, is_marquee_direction, ib_marquee_overflow_only, ib_marquee_pause_on_hover, is_font_family-1, 0, "", false whatever was set; a family with a space in quotesthe value that was set
statictext: ii_marquee_speed = 0kept the previous speeddefault speed (60)
statictext: of_reset after a bordercolor and thickness kept: the next border inherited themcleared
statictext: ib_track_mouse, component disabled while hoveredue_mouse_leave never raisedue_mouse_leave raised on leaving
statictext: scrolling (ib_marquee)a [foldarea] no longer folded afterwards; with ib_marquee_overflow_only, a HEIGHT change restarted from the beginning; with ib_wrap = false, a wrong ue_text_overflow(true)fixed: the text is no longer copied, only a real new WIDTH is re-measured, and the scrolling shows the whole text
statictext: ib_hover_effect and hovered links on a dark themedarkened (lower contrast)lightened
Rich text: [symbol=clock]shown as it isa monochrome glyph; an unknown name stays text
statusbar: of_add_panel / of_insert_panel on a taken key or one holding / or a vertical bar; of_move_panel, of_remove_panel, of_clear_menu, of_add_menu_separator on an unknown panel; of_add_menu_item / of_insert_menu_item under an unknown panel or on a taken entry; of_remove_menu_item, of_move_menu_item on an unknown address0, ignored-5
statusbar: of_insert_panel(…, 0)appended at the end of the barfirst, like every position (0 or less = first)
statusbar: a separator (of_add_sep) in of_count, of_keys_at and the positions of of_insert_panel / of_move_panelcounted as a panel with an empty keyignored: a position counts panels
statusbar: a separator between two ALIGN_END panelsdrawn at the end of the start groupbetween the two: it opens the group of the panel after it
statusbar: right-click on a non-clickable panelno eventue_panel_rclicked (a disabled panel stays mute)
statusbar: of_panel(k).ii_progress read on a panel without a bar0, like a bar at 0 %-1
statusbar: of_menu_item("k"), a one-level addresswrote to the panel k (ib_enabled greyed it)designates nothing: writes nowhere, reads empty
statusbar: colours and tooltip of a panel after of_clear, then a panel added under the same keytaken over by the new panelforgotten: the panel starts clean
statusbar: is_state in a dark theme; disabled panellight-theme colours, hard to read; greyed by transparency, background lost on hovera readable dark palette; greyed by the theme colour, background kept
statusbar: a panel whose list entries are all hiddenhand cursor and chevron, click without effecta plain panel, clickable or not as ib_clickable says
statusbar, toolbar: Left / Right arrows in right-to-left reading (RTL)the wrong way round: Right went to the left of the screenthe way the screen goes, like the tab and the ribbon
stepbar: setting ii_currentnothing was raisedue_step_changed when the bar moves, as the documentation promised
stepbar: removing, inserting or moving a step before the current stepthe current step changed silently (it was a rank)the same step stays current; if that step is removed, the bar moves on to the next one it can land on and raises ue_step_changed
stepbar: ii_current set before the steps; of_clear_stepsbrought back to the first step; the current step survived the clearkept and applied as soon as that step exists; clearing starts again on the first step
stepbar: of_add_step / of_insert_step on a taken key or one holding / or a vertical bar; of_move_step, of_remove_step on an unknown key0, ignored-5
stepbar: of_insert_step(…, 0)appended at the end of the barinserted first, like every position in the library (of_add_step appends)
stepbar: left / right arrows in a right-to-left languagethe right arrow went to the next step, drawn on the leftswapped: the left arrow goes to the next step
tab: of_add_page / of_insert_page with a key holding / or a vertical bar; a send that fails0; the page stayed hosted and its key taken-5; a failed send returns its code, the page goes back to its window and the key stays free
tab: of_select_page of a hidden or disabled page, or one past the demo cap0, and the page showed under a band with no active tab-5, nothing moves
tab: cross, shortcut (is_shortcut), “Close all / others / to the right” on a disabled or hidden tabthe tab was activated or closed, and PB released its pageno effect: a tab one can neither see nor reach is neither activated nor closed
tab: the active tab is closed, removed or hiddenthe next or the first took over, even hidden or disabled; the as_from_key of ue_selection_changed was emptythe selectable neighbour takes over (right, otherwise left); as_from_key names the tab really left, even gone
tab: no visible tab left (all hidden, or a layout that hides them)the page of the old tab stayed on screen, without an eventthe page is hidden and ue_selection_changed(old, "") is raised; showing a tab again selects it
tab: a handle held after of_remove_page or a close; its shortcutstayed; a page reopened under the same key got back its colour, tooltip and old shortcutfreed: IsValid() returns false, the page comes back fresh
tab: of_set_layout with a text that is not JSON0, and the message went out broken-5, nothing is sent
tab: keyboard in the tab stripEnter lost the focus; the arrows stopped on disabled or overflowed tabs; no Up/Down on a vertical band and no swap in a right-to-left languagethe focus stays on the tab; the arrows follow the axis of the band and skip what does not answer; Delete closes a closable tab
tab: text of a disabled tab; tooltip of a tab with a truncated titlefaded as a whole (unreadable on hover in office2007 dark); the native full name hid your tooltiptext in the theme's disabled colour, readable; your tooltip comes first
tilesbox: of_get_layoutalways returned "": a saved layout was never restoredthe whole layout, the same text as ue_layout_changed; it also keeps the size and visibility of each tile and the visibility of each group
tilesbox: of_set_layout naming a tile under another group than its own; a text that is not JSONthe first tile with that key, in any group, was moved there; 0a tile is found back by its group and its key: the entry is ignored; -5 on a text that is not JSON
tilesbox: of_add_group on a key already taken; of_add_tile / of_insert_tile on a taken key or under a group never added; an empty key, or one with / or ``0; a group added again was renamed, a tile under an unknown group created it under its key-5, nothing is added: add the group first with of_add_group
tilesbox: of_move_tile, of_remove_tile, of_add_live_item, of_clear_live_items on a tile that does not exist; of_remove_group on a group that does not exist0-5
tilesbox: a handle held after of_remove_tile, of_remove_group, of_clear, or a tile dragged into another groupstayed in memory; a tile re-created at the same address took back the old one's colors and tooltip, a dragged tile lost themthe handle is released (ask again with of_tile / of_group); colors and tooltip follow the dragged tile and leave with what is removed
statusbar, stepbar, crosstab: a handle held after of_remove_panel, of_remove_menu_item, of_clear_menu, of_clear (statusbar), of_remove_step, of_clear_steps (stepbar), of_remove_calc_field or of_remove_calc_measure (crosstab)stayed in memory until the component was destroyedfreed: IsValid() returns false; ask again with of_panel, of_menu_item, of_step or of_field
Every component with items: reading back il_back_color_hover / il_text_color_hover of an item never coloured0, black-1, "the theme's colour", like the three other item colours
tilesbox: reading back ib_enabled / ib_visible of a group, or ii_badge_sizefalse, false, 0 whatever was setthe value in force
tilesbox: disabled group (ib_enabled = false)its header still folded it (click, Enter) and raised ue_group_toggled; a dragged tile could land in itinert: no fold, no event, no drop
tilesbox: item colors (il_back_color, il_text_color, il_accent…) set on a groupno effect; the color of a group a could paint a tile a of another groupthey paint the group header (background, title, hover, accent rule), never its tiles
tilesbox: of_tile(""), or an address with one or three levelssetting a property stopped the application ("Array boundary exceeded") or wrote on the groupan inert handle: it writes nothing and reads empty
tilesbox: keyboard and screen readerthe focus was lost on every update coming from PB (a badge) and the size menu closed; size menu by mouse only; in RTL, arrows the wrong way; labels read with their markupfocus and menu kept; size menu by keyboard (arrows, Enter, Esc); arrows in reading order; plain text
toaster: il_max_visibledefault 5, sent by EVERY toaster: a toaster that had set 10 fell back to 5 as soon as another one showed a toastdefault 0 = nothing is sent; the cap is SHARED by the application and the last value set wins (5 until someone sets it)
toaster: of_show of a toast waiting for roomreturned 0: the toast could not be closed by of_close, and its events carried an id never received; full waiting line (200): 0 too, toast lost without a worda real identifier, at once; of_close takes it out of the line; full waiting line: -4
toaster: two toasts without is_key carrying a button with the same keythe second REPLACED the first (the button key was taken for the toast key) and of_show returned the id of the firsttwo toasts, two identifiers; only is_key updates in place
toaster: events of several toastersfiled by WINDOW: two toasters anchored to the same window took each other's events, and those of a destroyed toaster went to the next oneeach toaster receives those of ITS toasts; a destroyed toaster receives nothing more, its toasts stay on screen, silent
toaster: pbm_custom02 of the ipo_owner windowthe anchor window received a pbm_custom02 at each toast event, even without ipo_receiveronly a window set in ipo_receiver gets it; if you had mapped your window's pbm_custom02 for the toasts, set ipo_receiver, or better, remove that code: the events arrive on their own
toaster: displaya toast stayed put when its window moved; a toast taller than 800 px (a long error trace) NEVER appeared; width not scaled (180 px at 200 %); updating a hovered toast restarted the whole stackit follows its window; the text scrolls inside the capped toast; width scaled to the screen; the stack stays paused under the pointer
toolbar: adds, of_add_separator, of_move_item, of_remove_* on a taken key, one holding / or ``, an unknown bar or an unknown address0, ignored — a typo in a bar name created a phantom bar-5; only main needs no of_add_bar
toolbar: of_clear, of_remove_bar, of_remove_itemhandles stayed, shortcuts stayed armed, and a tool recreated under the same key got the old one's colours backhandles freed, shortcuts disarmed, colours and tooltips forgotten; ask for a handle again after a removal
toolbar: of_item on a three-level address (a menu entry)acted on the bar's TOOL that bore the entry's namedesignates nothing; an entry is driven through of_menu_item(address)
toolbar: entry chosen in a drop-down folded into the chevronue_menu_selected without the cascade's parent, under another drop-down, or emptythe full address, as in the bar
toolbar: ii_index of a barnever moved a bar to the rightthe bar takes the requested rank
toolbar: of_add_bar(key, band, rank) on a rank already held, or a band beyond the othersundefined order; the band read back as given (5 for the second row)it is inserted before the bar that held the rank; the bands close ranks
toolbar: of_count, of_keys_at of a bar, rank of of_move_itemcounted the separators, under an empty keythe tools only: a separator has no key and holds no rank
toolbar: Enter in a text boxvalidated the field AND pressed the window's default buttonvalidates the field only; Escape gives it back its validated text
toolbar: is_shortcut read back; ii_band / ii_index of an unknown barctrl+s (normalised); 1Ctrl+S, as written; 0
toolbar: colour or tooltip set on a BAR (of_bar)no effect, or painted the tool bearing the same nameapplied to the bar, and to it alone
toolbar: shortcut of a hidden tool or one past the demo capswallowed the keythe key goes to the next tool holding the same chord
webbrowser: a page that does not load (unknown host, network, certificate, of_stop)raised ue_load_completed, like a successraises ue_load_failed(as_url, al_status) instead
webbrowser: words in is_address ("invoice 2026")went out as https://invoice 2026 and no navigation happened after thatrefused with ue_error, what follows loads; a search when is_search_url is set
webbrowser: a disk path (C:\temp\x.html) or //host/x in is_addressprefixed with https://, so not foundfile:///C:/temp/x.html; https://host/x; javascript: refused
webbrowser: is_address read back after a followed link or Backthe last address setthe page on screen
webbrowser: of_go_back, of_go_forward, of_stop right after is_addresscould run BEFORE the address; of_stop let the waiting pages start againplay in the order written; of_stop also drops the waiting pages
webbrowser: of_save_as_png, of_save_as_jpg, of_print_to_pdf, of_printcaptured or printed the address barthe site on screen
webbrowser: target=_blank link, window.open after a clicknothing happenedthe page opens in the view, ue_new_window says so
webbrowser: of_execute_javascript answer longer than 4,095 characterscut without a wordwhole
webbrowser: F5, Ctrl+R, Ctrl+wheel in the address barreloaded or zoomed the bar itselfno effect in the bar; active in the site
webbrowser: right-click in a text field with ib_context_menuBack / Forward / ReloadCut / Copy / Paste
webbrowser: Tab or of_focus_webview without an address bar; file dropped on the site with ib_allow_dropfocus lost in a 0 px bar; file opened by the browserthe focus goes to the site, Tab leaves it for your window; ue_drop_files
pdfviewer: is_source read back, and as_source of ue_load_completedthe internal address /_file/?p=…what you wrote (doc\notice.pdf)
pdfviewer: missing file, not a PDF, site unreachable or refusing the frameue_load_completed on an error page, or no event at allue_load_failed(as_source, as_reason) (REASON_* constants), the viewer stays empty
pdfviewer: http://, data: of a type other than PDF, https:// web pageshown (or an empty frame for http://)refused: ue_load_failed with REASON_INSECURE or REASON_NOT_PDF
pdfviewer: a # in is_sourcecut the path (Quote #12.pdf not found); the fragment of a URL got mixed with ii_page / ii_zooma # is part of the file name; the fragment of a URL is ignored, page and zoom go through the properties
pdfviewer: ii_page set before is_sourcelost: the document opened at page 1kept for that document; with no page set since the last is_source, a new document opens at page 1
pdfviewer: PDF regenerated at the same path then of_refresh; PDF of 32 MB or more; path longer than 260 charactersthe old version for 5 minutes; nothing; not foundthe file as it is on disk; shown (served in chunks); shown
pdfviewer: link followed in the document; of_printthe site replaced the document, with no way back; printed the page framing the documentthe document stays, ue_link_clicked(as_url) gives you the address; of_print prints the document (-4 without a document)
picture, button, tilesbox: il_badge_color0 = the theme's color; black could not be reached; unset, read back 0-1 = not set (the theme's color), like every other color; 0 is black. Replace il_badge_color = 0 with -1
picture: id_zoom set before ib_zoomableread back as set, while the picture stayed fittedignored: id_zoom stays 1.0 while ib_zoomable is false; set ib_zoomable first
picture: ib_mirror on a picture turned a quarter; is_align / is_valign with a rotationflipped vertically on screen; the placement turned with the picturemirror and placement are read on screen, whatever the rotation
picture: dragging the zoomed picture; wheel; disabled picture (disabled statictext too)ue_clicked on every move; zoom towards the centre, the picture could leave its frame; file drops were still acceptedno click after a move; zoom towards the pointer, the picture always covers its frame; disabled, it refuses every drop; ue_error names the source
xml: a read (of_get_value, of_count, of_is_valid…) or a change after a buildacted on the last LOADED document, or on nothingthe built document is loaded and becomes THE document; the next of_start_element starts a new one
xml: a failed of_load (empty, malformed), of_start_element after a loadthe previous document stayed readable and editable (of_is_valid true); a half-built document stayed in of_xmlnothing stays
xml: of_xml, of_pretty, of_get_values of a large documentcut without a word beyond a few MBwhole
xml: of_get_value of a value XPath (count(//line), sum(//@qty))emptythe value as text (3, true)
xml: an XPath whose prefix is declared below the root (a SOAP answer)empty without of_register_namespaceresolved
xml: is_last_error after an invalid XPath, a page that does not answer, a refusal; codes of of_register_namespace / of_clear_namespacesempty, or "no node matched" whatever the cause; changes returned -4 when the page was missing; always 0; a page without an answer returned the text nullthe real reason; -2 when the page does not answer; an empty URI is refused (-5); empty rather than null
xml: of_prettylost CDATA, comments, processing instructions and the XML declaration, and reshaped mixed texteverything is kept; an element holding text stays on one line
xml: building — a line break or tab in an attribute, a CR in a text, a control character, an invalid nameread back altered; malformed document; name written as isread back identical; control character dropped; name refused (is_last_error), the element left out
xml: a document holding a <parsererror> element; message of a malformed XMLrefused as malformed; message framed by "This page contains the following errors"loaded; the error line alone
xml: of_remove of nested nodes, of the root, of an attribute; of_set_value("/"); of_add_child under an attributecounted twice; emptied the document; the attribute stayed; 0 with no effect; "parent not found"the real count; the root stays (0 and is_last_error); the attribute is removed; -4; "the node cannot have children"
xml: of_close then a read; ipo_owner set after the first readthe loaded document was lost; ignored until of_closethe document is kept and loaded back; passed on at the next read
metro-light and metro-dark themes: text on the accentwhite on the #25a0da blue in both themes (panel headers, active tab, ribbon application button, tiles): hard to readmetro-light: the accent becomes the deeper blue #1b76a1, text still white; metro-dark: the accent stays #25a0da, the text on it becomes near-black (#1b1b1b)
office-dark, office2003-dark, office2003-light, office2007-light themes: accent and header coloursin dark mode, the accent was hard to read on the page (step bullet, badge), and so was white text on the accent hover and the crosstab column heads; in light mode, the top of the gradients of the application button and panel headers was too pale for white textoffice-dark: accent #1178d1, hover #0f6cbd; office2003-dark: accent #4574cb, hover #2f5aa8; crosstab heads darker on hover and on totals; office2003-light and office2007-light: deeper gradient top (#3e73cf, #4275c5), office2007-light panel headers more opaque. Text still white, at least 4.5:1
text on a colour chosen by the application (il_accent, an item colour, a panel header, a badge)white up to a fairly light colour: a medium blue such as #25a0da got white text at 2.95:1white or near-black (#1b1b1b), whichever reads better (WCAG contrast ratio): #25a0da now gets dark text
jsontree: of_clearemptied the tree AND put ib_wrap and ib_search_enabled back to their defaultsempties the tree only; the settings stay (of_reset is the one that puts everything back)
jsontree: is_json = ""raised ue_error "Invalid JSON"empties the tree without an error, as the documentation promised
jsontree: numbers and keys shown, is_json read backrewritten by the engine: 12345678901234567890 shown 12345678901234567000, 1.10 → 1.1, the first of two identical keys lost; is_json read back compacted, and empty after invalid JSONas received, both keys included; is_json reads back exactly as it was set
jsontree: of_match_count, al_count of ue_search_resultcounted LINES; two occurrences on one line were both "current"count OCCURRENCES, only one is current and of_search_next moves from one to the other
jsontree: blocks opened by a searchALL those holding a match, for good: searching "e" opened the whole documentonly those hiding the CURRENT occurrence; of_clear_search gives back the folds from before the search
jsontree: of_select_patha line hidden in a folded block stayed invisible; an unknown path returned 0the blocks hiding it open; an unknown path returns -5 and the selection does not move
jsontree: folding the block that holds the selectionthe selection became invisible, and the next arrow started over from the topthe selection moves onto the folded block; on a user gesture, ue_selection_changed reports it
xmltree: of_clearemptied the tree AND put ib_wrap and ib_search_enabled back to their defaultsempties the tree only; the settings stay (of_reset is the one that puts everything back)
xmltree: is_xml = ""raised ue_error "Invalid XML" and showed a red errorempties the tree without an error, as the documentation promised
xmltree: is_xml relure-serialised by the engine: quotes changed, line breaks lost, empty after an invalid XMLreads back exactly as it was set, an invalid text included
xmltree: ce qui s'affichea CDATA was invisible (the element showed empty), comments, processing instructions and the declaration were missing, the text of mixed content was glued together before the children, a text was trimmed of its blanks, an attribute value holding a quote was shown as it wasevery node in its place, in document order; the line stays XML (quote chosen or &quot;, &#10;, &amp;, &lt;); significant blanks stay
xmltree: of_match_count, al_count de ue_search_resultcounted LINES; a match straddling two pieces (id="4152") was counted without being highlightedcount OCCURRENCES, each highlighted, only one is current
xmltree: éléments ouverts par une rechercheALL those holding a match, for goodonly those hiding the CURRENT occurrence; of_clear_search gives back the folds of before the search
xmltree: of_select_patha line hidden in a folded element stayed invisible; an unknown path returned 0the elements hiding it open; an unknown path returns -5 and the selection does not move
xmltree: ue_selection_changedraised also without a change (End on the last line); folding the element that held the selection made it invisible, silentlyraised only when the selection changes; on a fold, the selection moves onto the folded element and the event says so
xmltree: ue_errorthe raw text of the engine's error page; a <parsererror> element of the document passed for an errorthe error line alone ("error on line N at column M: …"); a <parsererror> of the document is an element like any other
restclient: cookies of a synchronous requestnone kept: each of_get / of_post opened its own session; ib_keep_cookies only applied to async callsone session per object, shared by synchronous and async calls: a synchronous login keeps you logged in for the calls that follow
restclient: ib_keep_cookies changed after the first requestno effect until of_closeapplied at once; at false, the cookies kept so far are forgotten
restclient: redirection to another host or portonly Authorization, Cookie and the Basic authentication were dropped: an API key (X-Api-Key…) went to the third partynone of the headers set by of_set_header follows, only Content-Type stays
restclient: of_set_bearer("") after of_set_basic; of_set_basic then of_set_bearer; of_set_basic("", "")the Basic password kept leaving; two Authorization at once; Basic Og== sentone authentication at a time: each replaces the other, an empty token or user removes it
restclient: HEAD, or a 204 / 304 answer announcing a size-4 "connection lost", and replayed when asyncthe status (200, 304…)
restclient: a body with one invalid byte; charset=iso-8859-1 or utf-16the whole body re-read as Windows-1252; charset ignoredonly the invalid byte becomes a replacement character; the announced charset is read
restclient: a text body of several hundred MBkept three times in memory: a 32-bit process could die-4 "body too large : use of_download" beyond 64 MB
restclient: a hundred async requests sent at oncea hundred threads in parallelsix at most at the same time, the next ones wait their turn
restclient: an async request sent more often than every 50 ms (a telemetry timer)no answer delivered while the stream lastedthe answers arrive as they come
restclient: of_cancel of an unknown id; of a request whose answer has arrived-1; 0 (but ue_response was what arrived)-5; -4, too late
restclient: of_response_text() after of_download; after an of_download(url, "")the number of bytes written; the previous answerempty: that body is in the file; empty
restclient: reading an async answer during ue_progressmemory being written: a possible crashempty, of_status(id) = 0 while the request runs
video: changing film (is_source, of_play(source))the segment, subtitles, markers and chapters of the old film stayedthey go with it (set before any film, they wait for the first one); a playlist film keeps its subtitles. Set them after is_source
video: of_go_chapter, of_next_chapter, of_previous_chapterraised ue_seeked; of_go_chapter past the number of chapters returned 0 and did nothingue_seeked and ue_chapter_changed, like a click on the bar; -5
video: of_add_source while an is_source is setthe playlist never started, of_next went to the first filmthe first entry loads at once (unless the film set is PLAYING: the playlist follows it when it ends)
video: ue_started after of_stop or the end of the film, then of_playnever: once per sourceat every new playback (not when a pause resumes)
video: of_play on a film in error; of_reset; ii_rate at the next film"playing" forever, no event; the old film stayed loaded (of_duration, of_capture); back to normal speedue_failed again; the film is unloaded; the speed holds
video: of_capture to a file that cannot be written; to "\x" or "C:x"-4 (the doc said -5); written according to the current folder-4, and no truncated file is left; -5: give a full path
video: full screen then Alt+Tab to another applicationthe film stayed above everythingthe other application comes in front; coming back to yours puts the film above everything again
pdfviewer: a plain click on a link of the PDFnothing: only Ctrl+click raised ue_link_clickedue_link_clicked(url), the document stays on screen
webbrowser: a data: page (is_address = "data:text/html,…")shown, but no ue_load_completedue_load_completed with the requested address
webbrowser: a new window asked for by the site towards file:, data: or mailto:opened in the view, ue_new_window raisedrefused: ue_error, no ue_new_window; only http and https open
webbrowser: a site asking for the camera, the microphone, the position, notificationsbrowser prompt to the user, answer rememberedrefused unless ue_permission_requested returns true; nothing is remembered
webbrowser: cookies, sessions and storage of the sitesin the profile shared with the componentsin a browsing profile of their own: a session signed in with 3.0 is not carried over (sign in once more); of_clear_browsing_data clears it
webbrowser: is_search_url without %s, without http(s):// or in javascript:accepted: every search failed or lost the typed textrefused: ue_error, the engine in force stays
webbrowser: gestion/ or [::1]/x in is_address"not an address" (or a search)https://gestion/, https://[::1]/x; the word alone stays text
webbrowser: right-click on a selection, a link, an image with ib_context_menuBack / Forward / Reload onlyin addition: Copy, Open link, Copy link address, Copy image
tab: closable tab disabled (ib_enabled = false)the cross vanished but its room stayed emptyno cross and no empty room: the tab is as wide as a non-closable one
video: of_chapters on a title holding `` or a tabsplit into two chaptersread as it is
video: a .srt in Windows-1252 or UTF-16unreadable accents; no subtitle at allread correctly
commandpalette: of_open without the WebView2 runtime; toaster: of_show without the runtime-1-6, like the message box
crypto, xml, restclient: of_open when the page or the client cannot be createdthe raw code of the DLL (-1, -3…)-2, and is_last_error says why

If you had worked around one of these defects — for instance by handling ue_menu_selected with an empty address to catch the clicks of a folded group — remove the workaround: the documented event now arrives.

11.4 The n_pbt_menu2ribbon generator #

In 3.0, the code written by n_pbt_menu2ribbon used handle methods that no longer existed: it did not compile without edits. In 4.0 it writes the current API — every add by its address, through the component —, carries submenus over at any depth, keeps greyed and checked items as they are, and its router covers ue_clicked, ue_toggled and ue_menu_selected. The keys change: each one is now the ClassName of the menu item (m_fichier/g1/m_ouvrir) instead of the dotted path (m_principal.m_fichier.m_ouvrir).

The recipe: a ribbon already built by hand, or from generated code you then fixed, has nothing to change. To start again from the generator, run it again and paste the new router too: the old one read dotted keys.

11.5 What was added #

None of the following asks you to change your code. The new components of 4.0 are presented in the release notes.

ObjectAdded
ribbonof_add_menu_separator accepts the address of an entry: the line goes into the cascade under it
ribbonof_add_menu_item and of_add_menu_check accept a cascade of any depth, and every level is driven through of_menu_item(address)
menubarof_add_separator takes the address of an item (file/export): the line goes into its cascade; of_menu(...) carries the colours (il_accent, il_back_color…) and the tooltip of the title
crosstabof_set_label_filter(as_field, as_type, as_a, as_b) on text (LABEL_CONTAINS, LABEL_BEGINS, LABEL_ENDS) and the constants LABEL_GT, LABEL_LT, LABEL_BETWEEN: the form with two double could only pass a number
cryptoKEYFORMAT_TEXT, KEYFORMAT_HEX, KEYFORMAT_BASE64 and an overload of of_hmac, of_jwt_sign and of_jwt_verify that takes them (a key as BYTES: a secret handed out encoded, AWS SigV4); an optional entropy for of_protect and of_unprotect
cryptoof_hmac_verify (a received HMAC — a webhook — compared in constant time); of_jwt_header and an of_jwt_sign overload that adds members to the header (kid); of_totp_verify(…, ref al_step), the accepted step, to refuse a code already used; il_password_hash_rounds
messageboxof_get_last_error(): why the last dialog could not open (an empty string when it did)
n_pbt_utilsof_dir_has_subfolders(as_dir, ab_include_hidden): the same question, counting hidden and system folders and junctions too
n_pbt_utilsof_dw_date_order(): the order in which a DataWindow writes a date on this workstation (dmy, mdy, ymd)
speechoutof_is_paused(): true while the reading is paused
Rich textthe [symbol=name] tag: eighteen monochrome glyphs that follow the text colour (chapter 4)
toolbarof_menu_item(address) → n_pbt_toolbar_menu_item: every drop-down menu entry has its own state (ib_enabled, ib_visible, ib_checked, is_text, is_shortcut), designated by its full address
toolbaris_date on a date picker; of_clear_menu; of_remove_item of a menu entry; of_add_menu_separator on an entry (cascade); cascades of any depth
pdfviewerue_load_failed(as_source, as_reason) and the REASON_* constants; ue_link_clicked(as_url); file:/// addresses; ib_allow_save and ib_allow_print: the Save and Print commands of the reader's toolbar
pictureis_alt_text (text alternative read by a screen reader); a keyboard for the zoom: +, -, 0, and the arrows to move around; n_pbt_utils.of_json_get_str_array
webbrowseris_search_url (search engine for words that are not an address); ib_veto_new_window and ue_new_window; ib_veto_downloads and ue_download_starting; ue_load_failed; ue_error; of_show_html; of_clear_browsing_data and ib_private; ib_veto_navigation and ue_navigating; is_title and ue_title_changed; ue_permission_requested and the PERMISSION_* constants; the LOADSTATUS_* constants of ue_load_failed; F5, Ctrl+R, Alt+arrows and Esc in the address bar
jsontreeof_selected_text returns the DECODED value of the selected leaf (a string without quotes or escapes); of_has_selection tells the root, whose path is "", from "nothing selected"; the keyboard skips the closing braces (they are no longer selected)
xmltreeevery attribute is a target: a click gives its path (/order/@id), of_select_path selects it, of_selected_value returns its value; a large document draws only the lines on screen (8,000 sibling elements: 12 s → 0.15 s, an arrow key in a few ms)
videoue_muted_changed(ab_on): the user muted or unmuted the sound (the bar's speaker, the M key), as volume and speed already had their event
ribbonof_remove_menu_item, of_clear_menu, of_remove_combo_item, of_clear_combo, of_remove_gallery_item, of_remove_app_menu_item, of_remove_contextual_group: the removals under a control — a “Recent files” list is updated without recreating the control
ribbonof_app_menu_item(address): the handle of an application menu entry (is_label, is_image, ib_enabled, ib_checked, ib_visible); of_app_menu_item("") walks the menu
ribbonkey tips of several letters (is_keytip = "FP"), typed one after the other; accessible names (Quick Access Toolbar, tiles) for screen readers
toolbarof_add_split_button: a split button (action + menu); is_group: exclusive toggles (alignment); is_image on a tool and on a menu entry; ue_item_rclicked(address): the right click of a tool

11.6 Names and counts aligned with the rest of the library #

Seven places spoke differently from the rest of the library. 4.0 aligns them rather than keeping the gap: two names change — the compiler shows you each line to fix —, two counts now start at 1, like every position in the product, the path the JSON tree hands back becomes the path of n_pbt_json, the XML tree's under a default namespace an XPath that works, and an ambient event becomes opt-in. These are the only changes in 4.0 that ask you to touch code that used to compile.

What3.04.0
progressbar: bounds of the scaleil_minimum, il_maximum, longsid_minimum, id_maximum, doubles: a 0..1 scale or a byte count over 2 GB is valid; the prefix says the type
json: array index in a pathfrom 0: lines/0 is the first linefrom 1: lines/1 is the first line; lines/0 addresses nothing — a read finds it absent, a write returns -5
jsontree: path of a node (ue_node_clicked, ue_selection_changed, of_selected_path, of_select_path)levels joined by a dot, array index from 0: lines.0.sku — a dialect n_pbt_json did not readthe path of n_pbt_json: levels joined by /, index from 1 (lines/1/sku), "" for the root — to hand as it is to of_get_string on the same text
xmltree: path of an element under a DEFAULT namespace (xmlns="urn:…")/Envelope/Body — an XPath that finds nothing/*[local-name()='Envelope']/*[local-name()='Body'] — to hand as it is to n_pbt_xml.of_get_value; an element without a namespace or with a prefix keeps its path
toolbar: band and index of the layout (of_get_layout, of_set_layout, ue_layout_changed)from 0, a string called “opaque” while ii_band, ii_index and ue_bar_reordered counted from 1from 1, like ii_band, ii_index and ue_bar_reordered; a read → restore round trip gives the same string
statictext: ue_cycleraised on every scrolling loop, with no subscription — and also by an animation inside the textan ambient event, hence opt-in: raised only when ib_track_cycle = true, like ue_mouse_enter with ib_track_mouse; only on each full pass of the text
tilesbox: 2nd argument of ue_tile_movedas_to_keys, while it carries ONE key: the destination groupas_to_key: the name says what it carries, as everywhere else (as_key = one level, as_keys = an address)
restclient: ue_failedue_failed (long al_id, string as_error)ue_failed (long al_id, long al_code, string as_error): -4 failure, -5 URL or file — no need to parse the text any more
restclient: of_json_valuea key, first occurrence at any depth; a text value only — a number returned the name of the next keyan n_pbt_json path ("json/customer", "items/1/qty"), any value: "4152", "true". A nested key is named with its parents

The recipe: (1) recompile — the compiler flags il_minimum, il_maximum and as_to_keys: rename them id_minimum, id_maximum and as_to_key; (2) look for json paths that address an array and add 1 to each index — a loop for i = 0 to n - 1 over "lines/" + String(i) becomes for i = 1 to n; (3) a toolbar layout saved by 3.0 counts from 0: add 1 to the band and index of each bar before handing it to of_set_layout, or let the user rearrange once — read back as it is, it would put its first two bands together; (4) where ue_cycle is scripted, set ib_track_cycle = true at construction; (5) where you read a path from the JSON tree (ue_node_clicked, of_selected_path) or gave one (of_select_path), replace the dots with / and add 1 to each array index — lines.0.sku becomes lines/1/sku, which goes as it is to n_pbt_json.of_get_string.


11.7 Themes follow the application #

The theme review made every component hold the rule chapter 4 already announced: the application decides the theme and the accent, a component departs from them only as far as it is asked to. No method is renamed; what changes shows on screen, when reading the properties back, and in two return codes.

What3.04.0
PBT_SetDefaultThemechanging theme wiped the accent set by PBT_SetDefaultThemeAccent — even when setting the same theme again — and flattened the gradient background of office2007 and office2003the application accent stays from one theme to the next; each theme keeps its background
PBT_GetDefaultThemeAccentthe theme accent when the application had set none-1 as long as the application has set no accent
native return codesPBT_SetDefaultTheme with an unknown name returned 0 and left the next components without a theme; PBT_SetDefaultThemeAccent with a PowerBuilder system colour painted it black-5 in both cases; the previous theme or accent stays
one theme axis set alonefroze the other axis on the application theme of the momentthe empty axis follows the application theme at every change of it; both axes empty = the application theme, entirely
is_theme_style · is_theme_mode · il_theme_accentread back "", "" and -1, whatever was setthe style and mode shown, and the component's own accent (-1 when it has none)
an unknown axis value ("office2010") or a badly written one (" Metro ")the component lost its theme and stopped listening to the application's until of_reset()spaces and case ignored; an unknown value is ignored and the axis keeps its value
accent of a component with a local themeit kept its theme's accent, even when the application set oneit follows the application accent as long as it has none of its own
a custom accentonly the accent changed: hover and pressed accent buttons, tinted selections, checked ribbon buttons kept the theme accenteverything derived from the accent follows it
tilesbox : il_badge_color0 and -1 = the theme colour; read back 0 with no colour set; black was unreachable-1 = the theme colour (read back -1); 0 is black, as for every colour
a PowerBuilder system colour (ButtonFace…) in il_theme_accentpainted blackcounted as -1: the application accent
webbrowser · pdfviewerthird-party content followed the Windows light/dark mode, and a white rectangle came before its first paintit follows the application default theme, over the theme background (4.11)
accent of office-dark and office2003-darka dark blue, read at 3:1 when it coloured text (links, [accent])a lighter blue (#479ef5, #7ea1e8), with dark text on it
datagrid: rules between rowsmissing in eight themes out of tenpresent in all ten themes, in the theme's grid colour
Windows high contrastmonochrome icons and rings drawn as shadows disappearedsupported (4.10)

The recipe: (1) if you set the accent again after each PBT_SetDefaultTheme, it is no longer needed — and if you relied on a change of theme to clear the accent, call PBT_SetDefaultThemeAccent(-1); (2) test the return code of PBT_SetDefaultTheme when the name comes from user input or a file; (3) where you set both axes on every component to follow the application, leave them empty; (4) a tile whose badge went back "to the theme" with il_badge_color = 0 is now written -1.


11.8 crypto: what security now refuses, and where relative paths go #

The security review of crypto shuts a real door — a JWT forged with the PUBLIC key passed when the algorithm was missing — and replaces several silences with a refusal that states its reason. No method is removed; an ECDSA signature changes form, and the relative paths of files follow one rule.

What3.04.0
of_jwt_verify, of_jwt_signan EMPTY algorithm meant JWT_HS256, and a PEM key was accepted as the HS256 secret: verified with the PUBLIC key as the secret, a forged token passed; the header's algorithm was compared without case, a crit header was ignoredthe algorithm is REQUIRED and compared EXACTLY; a PEM key is never an HMAC secret (of_hmac too); crit is refused — empty string and is_last_error
of_jwt_signan exp given in the claims was silently replaced by the al_expires_seconds durationboth at once are refused: keep one or the other
of_encrypt, of_decryptan EMPTY password encrypted — anyone read the value backrefused, as of_encrypt_file already did; of_password_hash("") stays allowed
il_pbkdf2_roundsa value outside 1,000 to 10,000,000 fell back to 100,000 in silencethe call is REFUSED: empty string for a text, -5 for a file, and is_last_error states the bounds
of_password_hashil_pbkdf2_rounds (100,000 rounds)il_password_hash_rounds, 600,000 by default (the OWASP figure); the values already stored carry their rounds and still verify
of_base64_decode, of_base64url_decode, of_hex_decode, of_decrypt…bytes that are not a text (a PDF, a picture) came back as replacement characters, without an errorempty string and is_last_error, which points to of_base64_decode_to_file or of_decrypt_file
of_sign (EC key)an IEEE-P1363 signature (64 bytes), which openssl dgst and Java refusea DER signature; of_verify reads both forms, a signature already stored still verifies; the ES256 JWT stays in P1363
of_totp_code, of_totp_verifya non-base32 character in the secret (a 0 typed for an O) was dropped in silence: ANOTHER secret, codes that never matchedrefused (spaces and = tolerated)
of_encrypt_file"whatever its size"up to 64 GB, the limit of one AES-GCM container: beyond, -7
relative paths: n_pbt_crypto, u_pbt_zip, of_capture of the video playerread in the CURRENT folder (which moves after a DirList or a file dialog), written next to the EXE (in the IDE: the PowerBuilder folder)READ in the current folder, then in the folder the application started in, then next to the EXE; WRITTEN in the folder the application started in — the same in the IDE and compiled

The recipe: (1) ALWAYS pass a JWT's algorithm as a constant (n_pbt_crypto.JWT_RS256), never read from the token nor from an INI that may lack it; (2) if a server verified your EC signatures in P1363 (.NET VerifyData by default), convert them or verify them in DER (DSASignatureFormat.Rfc3279DerSequence); (3) an il_pbkdf2_rounds set out of bounds must come back between 1,000 and 10,000,000; (4) a file a relative path wrote next to the EXE is now in the application's start folder — give a full path if you want another one.


11.9 restclient: headers that state their refusal, redirections that keep the request #

The review of restclient replaces two silent losses — a change never made behind a redirection, an authentication never sent behind a malformed header — with the right behaviour or a refusal that states its reason. No method is removed; three become functions, two properties arrive, and Windows authentication leaves only when you ask for it. One point holds for the five nonvisual components that raise events: an error in your event code now shows.

What3.04.0
of_set_header, of_set_bearer, of_set_basicsubroutines: nothing was returned, a malformed value was kept as it waslong functions: 0, or -5 and nothing changes — empty name, a name with a space, a colon or a control character, a value with a line break; an empty value removes the header
a request with a malformed headerleft WITHOUT ANY header — Authorization included —, with no error-5, and is_last_error = "invalid header: <name>"; nothing leaves
a 301 / 302 redirection of a PUT, PATCH or DELETEwent on as a GET without a body: the change was never made, and the call returned 200method and body kept; only a POST becomes a GET; a 303 goes on as a GET without a body (except HEAD)
a reused connection (the one of a redirection) that the server closes at the same moment-4 and "request failed: 12152", now and thenthe request is sent again ONCE, body included, on a new connection: the server had answered nothing to it
of_response_text ( ), of_response_headers ( ), of_response_header, of_json_valuethe last synchronous response of the PROCESS: a second n_pbt_restclient overwrote itthat of the OBJECT; of_close forgets it
body read as text, 32-bit applicationcapped at 64 MBcapped at 16 MB (64 MB in 64 bits): beyond, -4 "body too large" — read it with of_download
of_get_async, of_request_async… on an invalid URL or headera request id, then a ue_failed-5 at once, no event follows; is_last_error says why ("invalid URL: …")
Windows authentication (Negotiate / NTLM)the system answered an intranet server's challenge on its own with the session's credentialsoff by default: the credentials go to no server without ib_windows_auth = true (new property); is_client_certificate (new) sets a client certificate
of_cancel (long al_id), of_status (long al_id), of_response_text (long al_id)…the id of a request of ANOTHER object reached that requestonly the object's requests: of_cancel returns -5 and cancels nothing, the readers return 0 / ""
of_status (long al_id) in ue_failedcould return 200: the status of an answer received then lost0 for any failed request
ue_failed-4 failure, -5 URL or file-4 failure, -5 file or client certificate, -6 range request refused in demo
runtime error in an event of the application — restclient, speechout, soundplayer, toaster, commandpaletteswallowed without a word, and the next events were no longer deliveredreaches the application's SystemError event like any script error; the next events are delivered
il_max_retriesalso replayed a failure that repeats identically (too many redirections, an invalid or HTTPS → HTTP redirection, TLS, a file that cannot be written)those failures are no longer replayed; 429, 503 and a network failure still are
URLs and pathsthe #… fragment went to the server; a relative Location ?page=2 lost the last segment; a relative path of of_download followed the current folder of the momentthe fragment is no longer sent; Location resolved the way a browser does (RFC 3986); a relative path is resolved at the call, in the application's current folder when the library was loaded

The recipe: (1) test what of_set_header, of_set_bearer and of_set_basic return when the value comes from user input, an INI or a database — a call that ignores the return still compiles, but the -5 is now the only trace of a refused header; (2) an intranet API with Windows authentication that answered 200 now answers 401: set ib_windows_auth = true on THAT client; (3) a 32-bit application reading a body over 16 MB as text moves to of_download; (4) if you shared a request id between two objects, read the answer on the object that sent it; (5) an error that now shows in SystemError from ue_response or ue_failed already existed: it was swallowed.

11.10 speechout: live settings, a silent destruction #

The review of speechout keeps two promises the documentation made and the code did not — a setting changed during a reading applies to the next sentence, of_speak_from past the text returns -5 — and removes an event raised at the wrong moment. No method is removed. Added: of_add_abbreviation, of_clear_abbreviations, the ue_mark event and two tags, [mark=name] and [rate=150]…[/rate].

What3.04.0
of_speak_from (al_index)past the text, the reading under way was CUT, nothing started, and the return was 0-5, and the reading under way goes on
ii_rate, ii_pitch, ii_volume, is_lang, is_voicechanged during a reading, taken only at the NEXT reading (the documentation promised the next sentence)live properties: the next sentence takes them
ue_voice_fallbacksilent when only the REGION was missing (fr-CA asked, fr-FR voice)raised; language tags compare without case, and fr_FR is fr-FR
destroy (n_pbt_speechout)during a reading, raised ue_stopped — on a window already half destroyedno event; of_close() still raises ue_stopped
of_sentence_count, of_countthe count of the text read the PREVIOUS time (0 before any reading)the count of is_text as it is
of_pause, of_resume, of_clear_queuecreated the voice when it did not exist; a pause during the wait for the voices was lost0 without creating anything, -4 when the page did not answer; the pause holds until of_resume
ue_sentence (al_index)an abbreviation (Mr., Dr., e.g.), a spelled address or a version cut the sentence; a text without a full stop was one sentencethey no longer cut; a sentence longer than 250 characters is cut: the sentence numbers may change
[pause=ms]ignored at the END of the texta silence after the last sentence, before ue_stopped

The recipe: (1) test the -5 of of_speak_from when the index comes from a click; (2) if your ue_stopped did something when the window closed, call of_close() yourself in its close — the destruction no longer raises it; (3) do not keep sentence numbers from one version to the next: take them from ue_sentence, as the documentation always said.

11.11 statictext and rich text: markup read from left to right #

The review of statictext mostly fixed the shared markup text (4.7): these lines apply to the label of every component — tab title, status bar panel, toast, dialog. No method is removed. Added: of_refresh_parent_color(), and the [action], [hyperlink] areas and [foldarea] headers of a statictext are reachable with the keyboard (Tab, then Enter or Space).

What3.04.0
of_escape_markup (une donnée collée à une balise)an escaped ] right after a tag paired with the ] of the tag: "[b]" + of_escape_markup("] end") + "[/b]" showed [b]] end without boldthe data shows as written, and the formatting around it holds
[/b], [/i]…a closing tag closed the LAST open tag, whatever it was: [b]A[i]B[/b]C left C bold; [/br] closed something tooa closing tag closes its own tag only (the styles opened inside go on); a closing tag without an opener, or that of a self-closing tag, is ignored
[picture=…]cut at the first comma: a data URI (what of_icon() returns) or a file name with a comma did not showthe dimensions are read at the end of the value: both show
[picture=\\server\share\x.png]the image was read from the network sharerefused: a markup image never reaches a network share. Go through of_icon(), which embeds it, or a picture
[hyperlink=HTTPS://…]a scheme in capitals was not openedopened: the scheme is read case-insensitively; the list stays http, https, mailto
ii_padding0 = the theme's margin: a label could never line up with the edge of a neighbouring field-1 = the theme's margin (new default), 0 = no margin
ue_clicked, ue_double_clickedraised when folding a [foldarea] (three events for a double click), and when releasing the mouse after selecting textno longer raised: folding is a reading matter
[foldarea:… [action=…]]raised ue_action AND folded the blockue_action only, the block stays as it is
ii_line_spacing, is_marquee_directiona value outside 50-400 was ignored and read back 0; an unknown direction ("UP") scrolled towards the start but read back as writtenbrought back to the nearest bound, read back as applied; an unknown direction is and reads back MARQUEE_START
of_scroll_to_endreturned nothingreturns 0, or -2 when the component is not created

The recipe: (1) an ii_padding = 0 written to mean "the theme's margin" becomes -1 — or goes; (2) a markup image stored on a network share goes through of_icon(); (3) a ue_clicked that reacted to a section being folded will no longer be called: folding is not scripted.

11.12 breadcrumb: what gives when the path does not fit, and leaving the field #

The second review of the breadcrumb (breadcrumb) fixed the fold by width, the OVERFLOW_SHRINK mode and what a segment takes along with it. No method is removed. One line applies to every native menu of the library: type-ahead.

What3.04.0
of_edit() sans / without ib_editablereturns 0, nothing opensreturns -4
leaving the path field for another control of the applicationthe field stayed open, text half typed, and nothing was reporteda changed text is validated: ue_path_entered; switching to another application still keeps the typing
OVERFLOW_SHRINKevery label capped at 12 characters, even with room to spare; the current place cut clean at the edge of the barnothing is cut while there is room; the middle gives ground first, down to a letter and …, the first segment and the current place last
OVERFLOW_COLLAPSEthe label of the current place could be elided before the middle foldedthe middle folds first; the current place is elided only as a last resort
of_insert_item(…, ai_index)the handles of the shifted segments designated another segment, their colours and tooltips stayed at the old addresstheir handles are released, their colours and tooltips follow them; a removed segment (of_clear, of_truncate, of_remove_item) takes its own along
of_item("b").il_back_color / of_item("a/b").il_back_colortwo values for the same segmentone: the unique bare key and the address designate the same segment
OVERFLOW_SCROLL, mouse wheelno effectslides the strip
native menus (trail branches, ribbon, menu bar, toolbar, context menu): a letter typeddid nothing, mnemonics asidethe letters typed within a second jump to the first entry that starts that way ("Win"), as in the explorer; a menu of more than 500 entries draws only what can be seen

The recipe: (1) an of_edit() whose 0 you tested without setting ib_editable now returns -4; (2) a ue_path_entered may arrive when the user clicks elsewhere in the window — it is the same text as with Enter; (3) after an of_insert_item in the middle of the trail, ask again for the handles of the segments that follow.

11.13 buttonbar and window keyboard: the key goes to the component of the focus #

The second review of the button bar (buttonbar) touched the window keyboard, shared by every component that answers Enter, Escape, an Alt+letter or a registered shortcut (button, buttonbar, and the of_register_shortcut shortcuts of every component). No method is removed; one is added, of_focus_button.

What3.04.0
two components of the same window answer Enter, Escape or the same shortcut (two MDI sheets, two panels)the first one created received it, even in the other sheetthe one closest to the focus (same sheet, same panel); on a tie, none: the key stays with the window
Enter on a PB CommandButton holding the focus, or in an open DropDownListBox; Escape in an open listfired the default / cancel button of the componentstays with the control, as in a Windows dialog box
of_register_shortcut(chord, key) on a button of the bar, then the button greyed and re-enabled, or its is_shortcut letter changedthe shortcut was erasedkept: the Alt+letter and the application shortcut no longer touch each other
a button's Alt+letter against an explicit shortcut on the same keythe first registered wonthe explicit shortcut wins; the letters are consulted only after it
of_clear_shortcuts()also erased the is_shortcut of a buttonerases only the application's shortcuts
buttonbar: of_register_shortcut(chord) without a key, or with a key that names no buttonreturned 0 and swallowed the key without firing anythingreturns -5
buttonbar: removing the is_default / is_cancel button, or clearing the barthe property kept the key; a button re-added under that key became the default againemptied: the role leaves with its button
buttonbar: another button of the bar has the focusthe accent stayed on is_default while Enter fired the focused buttonthe focused button wears the default look; is_default reads back unchanged
buttonbar: a negative ii_padding_xcame back as 12the theme's inset, like ii_padding_y and ii_gap
buttonbar: the separator line between buttonsdrawn in the middle of the gap, even when the buttons did not touchonly between joined buttons (ii_gap = 0), and on the right side in right-to-left reading

The recipe: (1) on a bar, give a shortcut to ONE button — of_register_shortcut("Ctrl+S", "save") — the keyless form returns -5; (2) after removing and re-adding the default button, set is_default again; (3) to put the focus on a given button ("No" rather than "Yes"), call of_focus_button("no").

11.14 button: a grayed-out button lets go, and Alt+letter answers #

The review of the button (button, deprecated in favor of buttonbar but still delivered and fixed) changes what it does on the keyboard and what its properties read back. No method is removed or added.

What3.04.0
a grayed-out button with ib_default, ib_cancel or is_shortcut (ib_enabled = false)held Enter, Escape or its shortcut for the whole window: the key was lostgives the key back to the window (a default PB CommandButton, a SingleLineEdit receive it); enabled again, it takes it back
the & of is_text (&Save)underlined the letter, nothing moreAlt + the letter fires the button wherever the focus is in the window, after the explicit shortcuts, never while grayed out; a PB menu bar using the same letter loses it
Enter or Space held on the focused buttona burst of ue_clicked; Space fired on pressa single ue_clicked; Space fires on release, as in Windows
an is_shortcut the window cannot fire ("Win+S", "Ctrl+Entrée", "Ctrl+Pause")read back as written; "Win+S" fired the button on a plain Srefused: it reads back "". Key names are written in English (Enter, Esc, Del)
is_shortcut with a tooltipthe chord appeared nowhereadded to the tooltip (to the title of a rich one): "Save (Ctrl+S)"
of_register_shortcut("F5") on the button, then ib_default, ib_cancel or is_shortcut changedF5 was erasedF5 and is_shortcut both answer
is_text, is_image, is_style, ii_badge, il_badge_color, ii_badge_size, ii_image_size read after of_reset()the old value, while the screen was resettheir default ("", "standard", 0, -1)
a grayed-out button and the Tab keyreceived the focusis skipped
an image-only caption ([picture=…]), or an & inside a tagthe caption vanished, or the tag was brokenshown as written

The recipe: write the key names of is_shortcut in English ("Ctrl+Enter") and read the property back when in doubt: "" means refused. If you grayed out a default button to "disable Enter", nothing to change: that is now what happens.

11.15 menubar: a menu bar that gives the focus back, and Alt+letter from anywhere #

The review of the menu bar (menubar) changes what it does with the focus and the keyboard, and what every native menu does in right-to-left writing. No method is removed nor changes its signature.

What3.04.0
a click in the bar, then a pick (Edit > Paste)the bar kept the focus: GetFocus() in ue_item_selected no longer named the field, the paste went nowherethe focus goes back to the previous control before ue_item_selected is raised; same on Escape and on a click outside
Alt+F held, or F10, with the focus in a fieldnothing: only Alt tapped then F opened FileAlt+F opens File from any control, F10 does what Alt does; an Alt+F your application registered keeps the priority, and a native PowerBuilder menu on the same letter loses it
Ctrl++, Ctrl+-, Ctrl+,, Alt+Left, Ctrl+Space, Ctrl+Tab, Ctrl+Backspace (every component with shortcuts)shown, but nothing fired from a PB control; the button refused them ("")fired wherever the focus is; these keys need Ctrl or Alt (alone, they stay navigation)
in RTL, the dropdowns of every native menu (menu bar, ribbon, toolbar, breadcrumb, context menu…)aligned on the left, cascades on the right, a › chevron, Left closedthe dropdown hangs right-aligned under its title, a cascade opens to the left (a ‹ chevron): Left opens, Right closes
a click on the title of a menu already openclosed it then opened it again (a second ue_menu_opening)closes it, as in Windows
an is_shortcut set on an item that opens a cascadeshown, and raised ue_item_selected("file/export"), an address no click producesneither shown nor fired: a shortcut belongs on a leaf
a bar whose menus are all hidden or greyed, beside a ribbontook Alt for nothing: the ribbon keytips no longer came upgives Alt back to the ribbon while no title can be reached
two titles on the same letter (&File, &Favorites)Alt+F always opened the first onein the bar, the band walks from one to the other, Enter opens
an item removed then added again at the same address (a recent files list)took back the colours and tooltip of the one that leftis born without colour (il_back_color reads back -1); same after of_remove_menu and of_clear
an accented or non-Latin letter in a dropdown (&Édition)underlined, but no key reached itis typed as on the keyboard
the Tab key on the barone stop per titleone stop for the whole bar; the arrows walk from title to title
new—is_image (an item's icon, changed live), is_group (radio items), ue_menu_closed(as_key) (a dropdown closed without a choice)

The recipe: in ue_item_selected, work on GetFocus() or on the DataWindow being edited as with a PowerBuilder menu; put an & in every title; if your application used Alt+letter for something else, register it (of_register_shortcut): it keeps the priority.

11.16 ribbon: a choice that holds, menus that stay open, refusals that say so #

The second review of the ribbon (ribbon) fixes what the user saw — a font choice undone, a menu closing under the pointer — and applies the library's rule to the ribbon: an add or a removal that cannot happen returns -5. No method is removed nor changes its signature.

What3.04.0
ribbon: a choice in the list of a combo box, then leaving the fieldue_combo_changed twice — the choice, then the text typed before it, which overwrote it; is_text read back the typed texta single ue_combo_changed, the choice; the field shows it at once, even for a read-only combo
ribbon: Enter or Escape in a combo box, a spinner, or on a focused ribbon buttonwent also to the window's default or Cancel buttonstay in the ribbon; Escape in a field gives back the text from before the typing
ribbon: a menu open while the application updates the ribbon (another control hidden, a tab renamed, a tile added)closed, and the choice made in it raised no eventstays open; it closes only when its own control goes, is hidden or greyed
ribbon: is_app_button rewritten (“File” → “Fichier”); is_app_button = ""the application menu was emptied; an empty string showed “File”the menu stays; an empty string removes the button and its menu
ribbon: of_select_tab of an unknown or hidden tab0-5, nothing changes
ribbon: adding a menu entry, a tile, a combo choice, a Quick Access Toolbar button, an application menu entry or a contextual group on a key already taken; a key holding a vertical bar or starting with __; an unknown parent (separator, header, choice, entry without an application button, tab under a missing contextual group)0: a duplicate, or nothing at all (a contextual tab without a banner)-5
ribbon: of_remove_tab, of_remove_group, of_remove_item, of_select_gallery_item on an unknown address; of_open on a Quick Access Toolbar button0, no effect-5. of_open("tab/group") opens the panel of a folded group
ribbon: an of_qat_item handle kept after of_remove_item of its buttonstayed valid and designated a button that was gonefreed: IsValid() returns false
ribbon: key tips — two controls with the same letter, one of them hidden; a control of a folded groupthe hidden one stole the letter and nothing ran; the letter of a folded group did nothing; a two-letter key tip never answeredonly a visible, enabled control answers; the letter opens the folded group's panel; "FP" is typed letter after letter
ribbon: arrow keys on a greyed split button, colour picker, combo box or gallerystopped on them, with nothing to do thereskip them; Down on a drop-down or split button opens its menu

The recipe: (1) ue_selection_changed and ue_minimized are raised when your code calls of_select_tab or sets ib_minimized too, as for a click: if you called your handling yourself right behind it, remove that call, it would run twice; (2) test the -5 of the adds if your build may repeat a key; (3) an is_app_button = "" that meant “File” is now written is_app_button = "File". The removals under a control and the handle of an application menu entry are in table 11.5.

11.17 toolbar: one shortcut rule, a single tooltip, refusals that say so #

The second review of the toolbar (toolbar) aligns its shortcuts with the window's and makes visible what was written to no effect. No method is removed nor changes its signature; two return codes appear (-5).

What3.04.0
a tool with is_shortcut = "Delete" (a bare key) and a field in the barDelete in the field fired the tool and erased nothing; outside the bar, nothinga bare key is never a shortcut: it needs Ctrl or Alt, or F1 to F24 — the window's rule
a shortcut key held down (every component)the action repeated: a toggle flickeredone action per press
the inherited of_register_shortcut(chord, key), on a toolbarfired nothing, and the key was swallowedtakes the address of the tool or entry ("main/save"), -5 otherwise; the one-argument form returns -5; of_clear_shortcuts empties is_shortcut too
the shortcut of a text box, a date picker, a labelswallowed the key and did nothingthe field takes the caret (Ctrl+F on a search box), the picker opens its calendar; a label takes none
the tooltip given at the add (of_add_button(…, tooltip))tags shown as typed; two bubbles at once when is_tooltip was set afterwardsa single themed tooltip, rich text, the shortcut appended; is_tooltip replaces it and reads back
of_add_bar(MAIN_BAR) after a first tool on mainreturned 0 and did nothing-5: to place it, add main before any tool
of_set_layout of a text that is not JSONreturned 0-5
ue_layout_changedmissing when adding a bar pushed another aside; raised when showing a bar already visibleraised in the first case, no longer in the second
separators, a drop-down or cascade whose entries are all hiddenorphan rules; the drop-down and the cascade stayed live and silentno rule leading, trailing or doubled; they are drawn greyed
a right click in the barno event, ue_rclicked includedue_item_rclicked(address) on a tool, ue_rclicked outside any tool — never both
the colours of a menu entry (of_menu_item(…).il_back_color)written and read back, never drawndrawn in the menu
toolbar: ue_rclicked, ue_mouse_enter, ue_mouse_leavedocumented, but never raisedraised: a right click on the tray outside any tool, the mouse entering and leaving

The recipe: (1) a tool shortcut on a bare key (other than F1 to F24) takes Ctrl or Alt; (2) on a toolbar, of_register_shortcut receives the address of the tool or entry; (3) to place the main bar, call of_add_bar(MAIN_BAR, band, rank) before its first tool.

11.18 statusbar: a grip that resizes, a message that does not replace the text #

The second review of the status bar (statusbar) gives the resize grip its real role and turns every empty or unknown key into a refusal. No method is removed nor changes its signature; of_flash_panel gains a return code (-5).

What3.04.0
ib_show_resize_grip = true, then drag the gripa drawing: nothing movesthe window is resized from its corner (bottom-left in RTL); the grip is not drawn on a maximized window or one without a sizing border
of_flash_panel on a key the bar was never given, or on an address0, nothing is shown-5
of_panel(k).is_text read during an of_flash_panelthe messagethe panel's text (the message is shown over it)
of_panel(""), of_panel("a/b"): write then readwrote into the keyless panels and the separatorsdesignates nothing: writes nowhere, reads back empty
is_state outside the STATE_* constants ("Error", "very bad")read back as is, with no colour; a value holding a space raised a script errorcase is ignored ("Error" = error); any other value means no state and reads back empty
ii_progress = 150 then read back150 (drawn at 100 %)100, what is drawn
a disabled panel that carries a statekept the state colour: it looked activegreyed, state mark included
a panel removed, emptied (of_clear, of_clear_menu), disabled or hidden while its list is openthe list stayed open, and a pick raised ue_panel_menu_clickedthe list closes, nothing is raised (likewise for an entry that became greyed or hidden)
double-click on a panel carrying a drop-down listue_panel_double_clicked on top of the opened listthe list alone
Up / Down arrow on a focused panel carrying a drop-down listmoved to the next panelopens the list (Left / Right arrows still move)
a drop-down list made of separators alonechevron and an empty listno list: the panel stays what ib_clickable says

The recipe: (1) set ib_show_resize_grip on a status bar placed at the bottom of a resizable window; (2) test the return of of_flash_panel like that of the other keyed methods; (3) give a key to every panel you want to read back or change.

11.19 dockcontainer: an of_select_panel that finds the panel, floating windows that remember #

The second review of the panel container (dockcontainer) gives of_select_panel an effect on a collapsed or floating panel, and makes the layout remember the floating windows. No method is removed nor changes its signature.

What3.04.0
of_select_panel of a collapsed, floating or hidden panel0, nothing showscollapsed: its sliding panel opens; floating: its window comes to the front; hidden: -5
of_float_panel of the main panel or of a hidden panel0, nothing happens-5
of_move_panel that stacks the MAIN panel0, and its neighbour vanishes without a tab-5, nothing moves
unknown as_position (of_add_panel, of_move_panel)0, the panel docks at the bottom-5, the control is not touched
layout (of_get_layout, ue_layout_changed, of_set_layout) and FLOATING panelsforgotten: a detached panel came back docked; moving its window raised nothingkept with the place of their window, reopened on restore (brought back onto a screen); moving or resizing the window raises ue_layout_changed
of_set_layout of a layout without a main panelthe current main panel is removedit is kept
is_title of a panel added without a titleits key; a title given later to the main panel did not make its header appear"" (the tab still shows the key); the main panel's header follows its title

The recipe: (1) ue_panel_selected, ue_panel_floated and ue_panel_pinned are raised after of_select_panel, of_float_panel and ib_pinned too, as for a gesture: if you called your handling yourself right behind them, remove that call, it would run twice; (2) test the -5 of of_move_panel, of_float_panel and of_select_panel; (3) a layout saved before 4.0 restores as it is, its panels stay docked.

11.20 tab: honest return codes, a right-click that goes back to the application #

The second review of the tabs (tab) makes of_add_page and of_select_page say what they refuse, announces the layout when your code hides a tab, and hands the right-click back to the application when the built-in menu is off. No method is removed nor changes its signature; one event is added.

What3.04.0
of_add_page of a control already hosted (by this tab under another key, or by another component)0, and a tab with nothing behind it-5, nothing is added
of_select_page of a tab the component refuses while no tab is active (all hidden)0-5
Hiding or showing a tab from your code (ib_visible)no ue_layout_changedue_layout_changed raised with the whole layout, as for of_move_page: it is the notice that the layout to store has changed
Right-click on a tab with ib_context_menu = falsenothingthe tab is selected then ue_tab_rclicked(as_key) (new) is raised: open your menu there
Coming back to the current tab while ue_selection_changing awaits its answera "yes" answer still activated the other tabthe question is cancelled: the user's last gesture wins
A tab disabled or hidden while ue_tab_closing awaits its answerclosed anywaystays open
Closing the focused tab with the keyboard (Delete)the focus left the strip, the arrows no longer answeredthe focus goes to the tab that became active

The recipe: (1) test the -5 of of_add_page (a page is hosted only once) and of of_select_page; (2) if you store the layout in ue_layout_changed, nothing to do — it now follows your ib_visible too; (3) where ib_context_menu = false left the right-click unanswered, script ue_tab_rclicked.

11.21 listbar: a hidden entry never selected, a checked layout #

The second review of the navigation bar (listbar) makes of_select_item refuse a hidden entry and of_set_layout a text that is not a layout, and adds a count pill, the Left / Right keys and the right click of an entry. No method is removed or changes its signature.

What3.04.0
of_select_item of a hidden entry (ib_visible = false)0, an invisible selection-5, the selection does not move; hiding the entry already selected keeps it selected
of_set_layout of a text that is not a JSON object (truncated file)0, nothing is applied-5, nothing is sent
Section folded in a restored layout, added laterborn foldedborn open: the fold only applies to a section that is there
Section with an empty titleits entries glued to the ones above; foldable, with nothing left to open ita gap (a thin rule in the rail) separates it; it never folds
New: is_badge (entry), ue_item_rclicked(as_keys), Left / Right and type-ahead—a count pill, the right click of an entry (one event, the entry is not selected), folding from the keyboard

The recipe: (1) ue_selection_changed is raised after an of_select_item of your code too (opening the home page at start-up, for instance), as for a click: if you opened the page yourself right behind it, leave that to the event, otherwise it would open twice; (2) test the -5 of of_select_item if you hide entries, and that of of_set_layout if the layout comes from a file; (3) a section without a title no longer folds: give it a title if it must fold.

11.22 tilesbox: a title that no longer creates a group, rearranging from the keyboard #

The second review of the tile panel (tilesbox) stops a group's title from creating that group, makes of_add_live_item refuse an empty text, gives one Tab stop per group and adds rearranging from the keyboard. No method is removed or changes its signature. il_badge_color now follows the rule of every colour (see the picture, button, tilesbox row above).

What3.04.0
of_group(key).is_title on a group never addedcreated a visible group that could be neither filled nor removed (except by of_clear)writes nothing: only of_add_group creates a group; an empty key, or one with / or ``, gives an inert handle
of_add_live_item with an empty text0, and the fixed text is_live_text was wiped-5, nothing changes
of_tile(address).of_count()0the number of faces set by of_add_live_item
Rearranging (ib_reorderable = true)with the mouse onlyfrom the keyboard too: Ctrl+Left/Right within the group (reading order), Ctrl+Up/Down to the neighbour group; same ue_tile_moved and ue_layout_changed
Tab keystopped on every tile; an arrow towards an edge with no neighbour jumped to the previous or next tile of the documentone stop per group (the last tile focused), the arrows walk the tiles; at an edge, the focus stays
Tile disabled or hidden while it is draggedmoved on release, ue_tile_moved raisedthe drag is abandoned, nothing moves
a group's ib_collapsed set by your codeno eventue_group_toggled, like a click on the header (nothing when the group already is in that state)

The recipe: (1) create a group with of_add_group before changing its title; (2) test the -5 of of_add_live_item if the text of a face can be empty; (3) what you did in ue_tile_moved now also applies to a move from the keyboard.

11.23 stepbar: "everything done" anchored on the last step, removed steps that no longer leave anything behind #

The second review of the step bar (stepbar) makes "everything done" consistent with the rule of the current step (an identity, not a rank), makes a removed step take what belonged to it, and fixes four display defects. No signature changes.

What3.04.0
of_add_step while the bar stands on "everything done"the added step showed as completed (check mark); ii_current went to n + 2 with no eventadded after the last step of that moment, it becomes the current step and ue_step_changed is raised; inserted before it, nothing changes
of_step(key).is_state on a key that names no stepkept, then applied to the step added later under that nameignored; an added step starts on its computed state
Colours and tooltip of a removed step (of_remove_step, of_clear_steps)came back on the next step added under the same keyleave with the step
Compacted bar (OVERFLOW_AUTO) whose current step is forced to errorno label left; a step forced to STATE_CURRENT gained onethe label stays with the current step (the rank of ii_current), whatever its state
OVERFLOW_SHRINKlabels on several lines, long words cut with no "..."one line per label, ending in "..."
POSITION_BOTTOM with a second line (is_description)above the label, glued to itunder the label, above the bullet
Hovering a disabled or out-of-reach step (il_back_color_hover, il_text_color_hover)the hover colour lit upnothing lights up: the step will not answer the click

The recipe: if your code adds a step after setting "everything done" (ii_current = of_count() + 1) and wants the bar to stay so, set ii_current = of_count() + 1 again after the add. If you rebuild the bar with the same keys, set the colours and tooltips you want again: they no longer survive the removal.

11.24 progressbar: a bar that fits its control, a label that follows the language #

The second review of the progress bar (progressbar) makes it fit where an HProgressBar stood, sends the component only the values that change the drawing, writes the percentage in the display language and adds is_label_format (a label of your own) and is_state (paused, error). No signature changes.

What3.04.0
Linear bar in a low or wide control12 px margin, 640 px wide at most: below 24 px high, a scroll bar appeared and the bar was cut2 px margin, the whole width; the track (14 px) and the label shrink with the height
Percentage label (ib_label)"50%" in every languagein the display language: "50 %" in French
of_set_property("value", "12,5"), or a text that is not a numbersent as it stands: the command was lost without a word; an invalid minimum blocked the scale until the next of_reset"12,5" is read 12.5; a text that is not a number (nor true/false for a boolean) is ignored
id_value read backthe value the component had received; every assignment was a round trip with itthe last value the application set; the component receives only those that change the drawing (a tenth of a percent)
of_reset() on a filled barthe bar slid back down to zeroit goes back to zero at once

The recipe: (1) if you computed a percentage by hand, set id_maximum and assign the raw value, on every row if you like; (2) if an automated test read the label, expect the format of the display language; (3) to show a pause or an error, prefer is_state to a colour chosen with il_color.

11.25 picture: an assignment reads the file again, a failure says why #

The second review of the image (picture) makes it read the disk at every assignment, like the native Picture, gives ue_error a second argument that says why the image is missing, refuses an http:// address instead of failing with no reason, and raises only one ue_clicked on a double click. ue_error is the only signature that changes.

What3.04.0
picture: double click on the imagetwo ue_clicked, then ue_double_clickedone ue_clicked, then ue_double_clicked — like the native Picture
picture: ue_errorue_error (string as_message): the message onlyue_error (string as_message, string as_reason): as_reason is one of the REASON_* constants (not found, too large, http refused, format not shown, other). Your event script receives the argument by itself; an event ue_error(…) call written in your code now passes two
picture: is_source = "http://…"tried over https by the engine; on a server without TLS, a failure with no reasonrefused up front: error glyph and ue_error with REASON_INSECURE. Download the image first (n_pbt_restclient.of_download) and show the file
picture: the same path set again after rewriting the file; mono: or tint: on is_source; an .avif imagethe old image; the error glyph; not foundthe file read again, like the native Picture; the glyph recolored by the theme; shown
picture: zoomed picture smaller than its frame (bands); horizontal wheel, touchpad; right button held downit left its frame when dragged; one zoom notch per event, zoom out on a horizontal scroll; the picture movedit stays where is_align and is_valign put it; zoom in proportion to the gesture, nothing on a horizontal scroll; only the left button drags

The recipe: (1) if a script opened a record on ue_clicked and another action on ue_double_clicked, nothing to do — the double click no longer opens the record again; (2) replace any parsing of the text of as_message with a test of as_reason; (3) an image on an intranet server over http is downloaded first (n_pbt_restclient.of_download into a temporary folder), then shown by its path; (4) if you added a ?v= to the path to force re-reading a rewritten image, remove it: assigning again is enough.

11.26 video: subtitles and formats that play, a failure that says why #

The second review of the player (video) gives ue_failed a second argument that says why a film does not play, refuses an http:// address, plays .mkv and .mov, loads subtitles given by URL, and pauses the film when the component is hidden. ue_failed is the only signature that changes.

What3.04.0
video: ue_failedue_failed (string as_message)ue_failed (string as_message, string as_reason) — as_reason is a REASON_* constant
video: an http:// sourcefailed as "cannot be found"refused at once, REASON_INSECURE; only https plays
video: a .mkv, a .mov.mkv "cannot be found", .mov refused by its nameserved and played when the engine decodes them; .avi, .wmv, .flv stay refused (REASON_FORMAT)
video: a subtitle given by URLnever showed, saying nothingdownloaded by the component (the server must allow CORS); a refusal raises ue_subtitles_failed
video: of_add_subtitles twice for the same languagereplaced the track silently, 0-5 (also for a language holding / or `); of_remove_subtitles` first
video: the component hidden while playingthe film went on, with its soundpaused, ue_paused, and waits for of_play (ib_pause_when_hidden, true by default)
video: dragging the volume on the barone ue_volume_changed per pixelone, when released
video: of_capture to a .jpga PNG under a .jpg namea JPEG; the overload of_capture(as_path, al_max_width) scales the picture down
video: of_get_last_error after an of_capture failurethe library's previous errorthe reason of THIS failure (missing folder, empty path…)
video: Escape in full screen, ib_enabled = falseno effect (only Alt+F4 left)leaves full screen

The recipe: (1) add as_reason to your ue_failed script and test it rather than the message text; (2) a film or a subtitle from an http intranet server is downloaded first (n_pbt_restclient.of_download to a temporary folder), then played by its path; (3) ue_fullscreen_changed, ue_pip_changed, ue_source_changed and ue_chapter_changed are raised after your own calls too (of_fullscreen, of_pip, of_next, of_seek…), as for a gesture: if your code called its handling itself right behind them, remove that call, it would run twice; (4) a wallboard that must play hidden sets ib_pause_when_hidden = false; (5) to replace the track of a language, of_remove_subtitles then of_add_subtitles.

11.27 pdfviewer: of_refresh says what it does, nothing downloads behind your back #

The second review of the viewer (pdfviewer) gives of_refresh a return code — and, to keep the same name with the same meaning, the ones of webbrowser and shellexplorer too —, reports every link of the document, and refuses what will never show instead of leaving you waiting. No event signature changes.

What3.04.0
pdfviewer · webbrowser · shellexplorer: of_refreshsubroutinelong: 0 once asked, -4 without a document or a page (pdfviewer, webbrowser), -2 if the component is not created
pdfviewer: of_refresh after a refusal (http://…)did nothing, without an eventthe refusal is said again: ue_load_failed
pdfviewer: a data: address of more than 2 MB of charactersnever shown, no eventue_load_failed with REASON_TOO_LARGE, at once
pdfviewer: a remote PDF served "as a download"ue_load_failed, but the file went to Downloadsue_load_failed with REASON_REFUSED, nothing is downloaded
pdfviewer: ue_link_clicked on a mailto:, file: or relative linknever raised (only http and https); a relative link arrived as an internal addressraised for every link; as_url is a mailto:… as is, or a disk path for a local file
pdfviewer: of_print_to_pdfwrote the page around the reader, not the document-4, nothing is written: copy the file of is_source
pdfviewer: file://localhost/C:/…, file:////server/share/…a wrong path, not foundC:\… and \\server\share\…
pdfviewer: the focus given to the component (Tab, of_focus_webview)stayed on the page: the keys did not scroll the documentgoes to the document: PageDown, arrows, Home / End scroll it

The recipe: (1) a call uo_pdf.of_refresh() compiles as is; test its return if you want to know there was nothing to read again; (2) a document of more than 1.5 MB held in memory is written to a temporary file, then shown by its path; (3) in ue_link_clicked, expect a mailto: and a disk path too, not only a web address; (4) to copy the document on screen, copy the file of is_source rather than of_print_to_pdf.

11.28 codeeditor: a search you can see, a “go to” that moves the caret, replace #

The second review of the code editor (codeeditor) brings on screen what the search finds, keeps the user's caret during a search, renames ii_doc_line and adds what a script editor is expected to do: replace, drive the caret, know whether to save, show an error in the gutter. No event signature changes.

What3.04.0
codeeditor: ii_doc_linea long with an integer prefixil_doc_line: rename it; a line past the end no longer marks anything (it reads back as written)
codeeditor: of_find with ib_search_enabled = false0, without searching anything-4
codeeditor: of_find("")ran the previous search againempties the box and clears the highlights, without ue_find_result
codeeditor: of_find, Enter in the barthe match often stayed out of view; the caret went onto itthe match is brought on screen; the search starts from the caret, which only moves when the bar is closed (Escape)
codeeditor: of_insert_text with the search bar openreplaced the match found, out of view, and emptied the Ctrl+Z historyinserts at the user's caret, brought on screen; Ctrl+Z undoes it
codeeditor: a new is_textkept the scroll position of the previous document; a CR-only text read back in LFshows from its first line and clears the markers; CR alone reads back in CR (a mix in CRLF)
codeeditor: ib_current_line with ib_wrapno bandthe band covers the whole line of the caret
codeeditor: additions—replace (of_replace, of_replace_all, Ctrl+H) and search options (ib_find_match_case, ib_find_whole_word, ib_find_regex, F3); caret and selection (il_caret_line, il_caret_column, of_select_range, of_selected_text, of_line_count); ib_modified; gutter markers (of_add_marker, of_remove_marker, of_clear_markers, of_marker_count)

The recipe: (1) replace ii_doc_line with il_doc_line; (2) test the -4 of of_find if the search can be switched off; (3) to “go to the line” of an error while moving the caret, set il_caret_line (and of_add_marker to show it) — il_doc_line only marks; (4) if you followed ue_caret_changed to learn where a snippet was inserted, read il_caret_line after of_insert_text; (5) a Save button can follow ib_modified, which you set back to false after saving.

11.29 jsontree and xmltree: the keyboard stays with the tree, a search that no longer moves the selection #

The second review of the JSON tree (jsontree) covered the engine it shares with the XML tree (xmltree): what follows applies to both unless stated. No signature changes; the JSON tree gains of_expand_path, of_collapse_path, of_is_expanded, of_copy and the ue_node_toggled event, raised by a user gesture as by of_expand_path and of_collapse_path.

What3.04.0
jsontree, xmltree: Enter on a leafpassed to the window: its default button firedkept by the tree, raises ue_node_clicked like a click; with no selection, nothing
jsontree, xmltree: Escape with the search box openthe window's Cancel button firedcloses the box first; box closed, Escape goes to the window
jsontree, xmltree: closing lines (}, ], </x>)reached by the arrows (ue_selection_changed for a block already reported); a click scrolled back to the opening lineskipped by the keyboard; a click selects the block without moving the view
jsontree, xmltree: Right arrow on a leafmoved down one linedoes nothing, as in a tree
jsontree, xmltree: a search during a selectionof_search_next or Escape could fold the chosen line and move the selection without an eventthe selected line stays visible and selected: of_selected_path does not change
jsontree: the message of ue_errorhalf translated, half English ("JSON invalide : Unexpected end of JSON (line 1, column 6)")entirely in the display language, line and column included: do not compare its text
jsontree, xmltree: a document of more than 1,500,000 linesshown, but its end out of reach, without a wordrefused: a message in the tree and ue_error
jsontree: Ctrl+C in the treecopied nothing, and went on as a window shortcutcopies the selection (like of_copy); with no selection, the shortcut stays the window's

The recipe: (1) if a window relied on Enter in the tree to trigger its default button, add that step to ue_node_clicked; (2) no longer look for an empty key in ue_selection_changed (a closing line is no longer selected); (3) do not parse the text of ue_error: it follows the language; (4) to restore a user's folds, read of_is_expanded and set them again with of_expand_path / of_collapse_path.

11.30 xmltree: every line has its XPath, any XPath selects #

The second review of the XML tree (xmltree) takes up everything the previous section says about the shared engine. It adds what is specific to XML: a path that ALWAYS designates the node of the line, readable as it is by n_pbt_xml. No signature changes; the XML tree gains of_selected_xml, of_copy, of_expand_path, of_collapse_path, of_is_expanded and the ue_node_toggled event.

What3.04.0
xmltree: path of a text, comment or processing-instruction lineits element's (while of_selected_value gave its own text); "" before the rootits own: /r/text()[2], /r/comment()[1], /r/processing-instruction('x')[1], /comment()[1] — n_pbt_xml.of_get_value reads the same value there
xmltree: element whose prefix is declared again with another URI, or with a sibling of the same name in another namespacea path that found nothing, or ANOTHER node*[local-name()='x' and namespace-uri()='urn:…'], which designates that very node
xmltree : of_select_pathonly the exact form the tree writes, -5 otherwiseany XPath (//line[@sku='A'], /r/a[1]…): the first node it matches is selected, of_selected_path then gives the tree's own path
xmltree: the message of ue_error“Invalid XML : error on line 1 at column 11: …”; the tree stayed empty“Invalid XML : line 1, column 11 (…)”, in the display language; the tree shows the source, the faulty line marked
xmltree: <?xml-stylesheet?> without an XML declarationshown twiceone line
n_pbt_xml: an XPath with @xml:langrefused (unknown xml prefix), empty valueread: the xml prefix is bound by default, as the standard requires
xmltree: Right arrow on a tag with attributesattributes could only be reached with the mousewalks the attributes one by one (Left goes back), one ue_selection_changed per step, then goes down
xmltree: Ctrl+C, folding by pathnothing; Ctrl+C went to the window as a shortcutof_selected_xml, of_copy and Ctrl+C (the XML of the selection), of_expand_path, of_collapse_path, of_is_expanded (any XPath), ue_node_toggled (a gesture, or of_expand_path / of_collapse_path)

The recipe: (1) if you went up from a text or comment line to its element through of_selected_path, take the parent of the path (what comes before the last /); (2) do not parse the text of ue_error: it follows the language; (3) a path saved in 3.0 under mixed namespaces is found again by handing it to of_select_path, which now accepts any XPath, then reading of_selected_path back.

11.31 xml: refusals that say so, namespaces that hold #

The second review of n_pbt_xml changes no signature. It makes of_remove follow the library's codes, refuses a read while a built document has an element open, and makes namespaces hold: what a mutator writes reads back in the right namespace, or is refused with a reason.

What3.04.0
xml: of_remove(""), of_remove with nothing loaded, a page that does not answer0, like "nothing removed"-5; -4; -2 — the count (>= 0) only when the removal was tried
xml: a read (of_exists, of_count, of_get_value, of_is_valid…) or a change while a built element is still openthe document was loaded as it stood, and the rest of the build started a NEW document: what came before was lost without an errorrefused: -4, empty or false, and is_last_error = "the document is still being built : close its elements first"; nothing is lost (of_xml and of_pretty stay available)
xml: of_add_xml under an element with a default namespace, or with the target's prefix (<s:Item/> under s:Body)the fragment stayed outside the namespace (xmlns=''), invisible to the XPath that had been used to add it; the target's prefix was refused (-4)the fragment inherits the namespaces in force at its target, like of_add_child
xml: of_set_attr / of_add_child with a prefixed name (xsi:type, p:item)0, an attribute outside the namespace that the XPath did not find; with an undeclared prefix, a document that no longer loadedin the namespace of the prefix (declared at the node, or bound by of_register_namespace); -4 and "undeclared prefix : xsi" when it is bound nowhere
xml: of_pretty after an of_add_child under a prefixed parent (s:Body)<Item> without a prefix: read back, outside the namespace<s:Item>, as in of_xml
xml: of_element_attr twice with the same name; a name :x, x: or a:b:ctwo attributes (malformed document); name accepted (a document that does not load)one attribute, with the second value; name refused (is_last_error), the element left out
xml: of_transformapplies an XSLT stylesheet to the loaded documentremoved: XSLT no longer exists in the engine (Chromium is removing it); transform on the server side or in PowerScript

The recipe: (1) an of_remove tested for = 0 (nothing removed) still works; test < 0 (nothing could be tried); (2) if you read a document WHILE building it (an of_exists to check, an of_count to number), close its elements first, or keep the count yourself; (3) an of_add_xml fragment no longer has to declare its target's namespace again: one that wrote xmlns='' to leave it still can; (4) test the return of of_set_attr / of_add_child with a prefixed name, and declare the prefix (or bind it with of_register_namespace).

11.32 json: an array that builds quickly, numbers that keep their digits, an empty text that says so #

The second review of n_pbt_json removes and changes no signature. It adds the decimal and longlong overloads of of_set_number and of_add_number, and the readers of_get_decimal and of_get_longlong. Building an array with of_add_* no longer rescans the whole array on each add: 2,000 rows build in a fraction of a second instead of several minutes. Two rules are now written in the documentation, unchanged: a null string is written "" (for a JSON null, of_set_raw(path, "null")), and a key written twice is read at its FIRST occurrence.

What3.04.0
json: of_load of a text made of blanks (line break, tab)-4, like a malformed text-5, like an empty text
json: of_load of a text preceded by a BOM (UTF-8 file with BOM)-40, the BOM is skipped
json: of_load of a document nested more than 512 levels deep0 — and a very deep text could stop the application-4, the application stays alive
json: of_set_number / of_add_number of a decimal (or of a literal such as 690.50)converted to a double: about fifteen digits, a cent could be lostdecimal overload: every digit, trailing zeros dropped (690.5); a longlong has its own too
json: of_get_number of an exponent with leading zeros (1E-0000005)0, or a huge valuethe right value (0.00001)
every component: \u followed by four characters that are not hexadecimal, in a string read backthe four characters disappearedkept as they are, like an unknown escape

The recipe: (1) if you tested of_load(...) = -4 to recognise an empty file, test < 0; (2) a decimal amount given to of_set_number is now written with every digit, with no change to your code; (3) read an id of more than 15 digits with of_get_longlong, an amount with of_get_decimal, no longer with of_get_number.

11.33 crosstab: of_from_datastore, refusals that say so, the displayed value on a double-click #

The second review of the crosstab renames of_set_data to of_from_datastore, the name of the datagrid and the scheduler, which read a DataStore the same way (same arguments, same codes). Any add, filter or format that names an unknown field now returns -5 instead of 0. The table only draws the columns that can be seen: a customer code or a daily date posed in Columns no longer freezes the screen. New: is_thousands, CF_ICONS, the VALUEFILTER_* and DATE_* constants.

What3.04.0
crosstab: feeding from a DataStoreof_set_data(ds)of_from_datastore(ds), same codes
crosstab: an unknown field (a typo) given to an add, a filter, a format, an order, a date grouping0, nothing happens-5
crosstab: a filter type, an aggregate, a date part not in the list; a calculated measure in Rows; of_set_value_filter at a position with no measure0; the position slid onto the last measure-5
crosstab: of_set_layout of a text that is not JSON; of_remove_calc_field / of_remove_calc_measure of an unknown name0-5
crosstab: ad_value of ue_cell_double_clickedthe raw aggregate (400 under "80.0 %"), 0 for an empty cellthe displayed value (0.8), NULL for an empty cell; an empty member is null in the tuples
crosstab: the same field in Rows (or Columns, Filters) AND in Valuesdepended on the call order: the value vanished; Remove on a dimension removed everythingallowed everywhere; adding or moving a dimension keeps the values, Remove removes the chip aimed at only
crosstab: an empty member (NULL, empty text); a text with a tab or a line break in the DataStore"null" or a row with no label; phantom rows, shifted columnsone (blank) member, last; the text stays in its row
crosstab: is_label after a new of_from_datastore; is_label = ""lost; a chip with no textkept; "" gives the DataWindow header back
crosstab: Ctrl+C in demo modeue_copy raised, clipboard writtenrefused like an export, the grid says so
crosstab: of_collapse_all; the grid's member filter; of_export_xlsx past 16,384 columns; exports in VALUES_ROWSrows only; applied at each box; an unreadable file; the measures side by siderows and columns; applied on OK (10,000 members listed at most); refused, ue_xlsx_saved says why; as on screen

The recipe: (1) replace of_set_data with of_from_datastore; (2) if you ignored the code returned by of_add_*_field or a filter, check < 0 — a typo finally shows; (3) in ue_cell_double_clicked, test IsNull(ad_value) before computing, and read ad_value as the displayed value (a share, not a sum, in a "%" mode).

11.34 Common base: of_count and of_keys_at count in long #

of_count returned an integer and of_keys_at took an integer rank — on every component, on their handles and on the non-visual objects that carry them (commandpalette, messagebox, speechout, toaster). Past 32,767 elements the count overflowed: a calendar (scheduler) fed from a 40,000-row DataStore could no longer be counted, and its last rows could not be walked. The four signatures are now in long: long of_count ( ), long of_count ( string as_keys ), string of_keys_at ( long al_index ), string of_keys_at ( string as_keys, long al_index ).

The recipe: a call does not change. An integer variable that receives of_count() stays right up to 32,767: declare it long. A descendant that overrode of_count or of_keys_at takes the new signature — the compiler shows you the line.

11.35 shellexplorer: a refresh that keeps the state, each row for itself #

The review of the shell explorer keeps its events: of_select, of_expand and of_collapse raise them like a click, as in 3.0. of_refresh and ib_show_files no longer close the tree. New: is_file_filter (the files shown), ib_show_hidden (hidden items, by default the Explorer setting), ib_enabled and of_refresh(path), which reads one branch again.

What3.04.0
shellexplorer: of_refresh(), or ib_show_files changedthe tree closed, the selection vanished without an eventthe open branches and the selection come back, found by their paths; a selection that no longer exists raises ue_selected("", "")
shellexplorer: of_expand, of_select, of_collapse of an empty path0-5
shellexplorer: of_expand of a file or of a folder without childrennothingue_path_not_found(…, "expand", "not a branch")
shellexplorer: of_keys_at(path, i)path/child, which nothing could read backthe child's path, whole: it feeds back into of_has, of_select, of_expand
shellexplorer: the same folder shown at two levels (the Music of the Desktop and that of the user folder)a click, a chevron or a double-click on one acted on the othereach row answers for itself
shellexplorer: hidden files and foldersnever shownshown according to the Explorer setting of the workstation, or ib_show_hidden
shellexplorer: a branch on a share that does not answer"…" for ever, even closed and opened againue_error "… : timeout" after 30 seconds; the branch is asked again when it reopens

The recipe: (1) ue_selected and ue_expanded are raised after an of_select or an of_expand of your code too, once the folder is reached (a path still to be read arrives a little later): do your work in the event, not right behind the call, or read of_selected_key() once the selection is there; (2) in ue_selected, handle as_path = "" (the selection is gone); (3) test -5 on an empty path; (4) set ib_show_hidden = false if your users must never see hidden items, whatever their Explorer says.

11.36 commandpalette: each object only touches its own palette, a close that always says so, the application's language #

There is only one palette on screen at a time: when a second palette object opens its own, it replaces the first one. An object now answers only for its palette and its chord. ue_opened and ue_closed are still raised on of_open / of_close: they tell the life cycle of the window, not a gesture (same rule as the audio and video players). New: of_insert_command (a command at a chosen rank), of_anchor_under (the palette under a control, in screen pixels), and on the handle is_group, is_hint, is_keywords, is_icon.

What3.04.0
commandpalette: ue_closedno argument; nothing at all when the palette died before showing (closed or replaced at once, window refused)ue_closed(string as_reason) follows every of_open that returned 0: "", cancelled, failed or blocked
commandpalette: of_is_open, of_close, of_reset, destroying an object whose palette was replaced by another one'sanswered TRUE for the other's palette, and closed itanswer only for their palette: FALSE, and nothing is closed
commandpalette: destroying (or resetting) an object whose chord another palette object of the same window has taken overremoved the other's chord: the key no longer answered anywherethe other's chord stays
commandpalette: of_register_shortcut of a key the hook does not name (ctrl+ù, win+k, space without a modifier)0 ("set"), and the key never did anything-5; the previous chord is kept
commandpalette: of_register_shortcut with an empty is_shortcut and no chord set02
commandpalette: of_add_command of a key holding a comma0 — and is_recent read it back as two commands-5
commandpalette: a group declared in several goes (File, Edit, then File)two "File" headersone only, where it first appeared; the order of the commands is kept within the group
commandpalette: the search and accentspreferences did not find « Préférences »accents do not count
commandpalette: the languagethe palette spoke English whatever PBT_SetLanguage saidthe application's language; an is_placeholder you set is not translated
commandpalette: the shortcut shown on a row, AZERTY keyboard; AltGr in the search boxCtrl+A read as Ctrl+Q; typing € (AltGr+E) ran the command shown as Ctrl+Alt+Ethe key is read as the DLL names it; a character typed is never a chord
commandpalette: a chord set without ipo_owner (the whole application) before a window's chord, on the same keywon in that window toothe window's chord comes first
commandpalette: POSITION_ABSOLUTEa command keyed x or y sent the palette to the edge of the screen; on two screens it went to the window's screenil_x / il_y hold, on the screen of the requested point
commandpalette: inv_pumpinternal variable left publicprotected: it is not part of the API, nothing to read or write
commandpalette: a command whose text quotes __licensethe palette did not open (-5)opens: only a reserved word written as a key is still refused

The recipe: (1) if your code triggers ue_closed() itself, give it a reason (""); a script that only receives it compiles as it is; (2) if you waited for ue_opened after of_open, also handle ue_closed("cancelled"), "failed", "blocked"; (3) test 2 and -5 on the return of of_register_shortcut; (4) rename a command key that holds a comma.

11.37 radialmenu: a bare key that aims true, an open wheel that follows the application, a label that can be read #

The wheel is drawn once, and the application keeps running while it is on screen: what you change in the meantime now counts. The hub spells nothing out (it had not done so since 3.0, whatever the documentation said): the pointed branch is what shows its whole label.

What3.04.0
radialmenu: of_item with a bare key (of_item("csv"))changed the label and state of the one branch carrying it, but its colours were not drawn, and the handle outlived the removal of the parentis resolved into the full address (import/csv): same handle, colours included, freed with the parent. Ambiguous or unknown: the handle changes nothing
radialmenu: a handle on an address not added yeta colour set through it coloured the branch added later at that address, even after of_clearwrites nothing; of_clear forgets every colour
radialmenu: of_showreturned nothingreturns a long: 0, or -2 when the component is not created
radialmenu: of_show(-1, -1)opened the wheel at the cursoropens it at the point (-1, -1), like any negative coordinate; at the cursor is of_show()
radialmenu: a branch removed, cleared, greyed or hidden while the wheel is opencould still be chosen: ue_item_selected for a branch that was gonecan no longer be chosen: ue_dismissed; of_clear, of_remove_item and of_reset close the open wheel
radialmenu: right-click elsewhere while the wheel is openthe new wheel replaced the old one without ue_dismissedue_dismissed for the old one, then the new wheel
radialmenu: long or empty labelcut at two lines and readable nowhere; empty, a slice with nothing on itthe pointed branch shows it whole; empty, the key is shown
radialmenu: right-to-left readingthe ring turned clockwise, ← climbed backthe ring turns the other way, ← and → trade places

The recipe: (1) of_show returns a long: a bare call compiles as it is; (2) replace of_show(-1, -1) with of_show(); (3) if you undid in ue_dismissed what you set up on opening, nothing to do: it now also comes when a wheel replaces another; (4) a handle obtained by a key two branches carry no longer changes anything: give the address.

11.38 toaster: events that say which toast, refusals that say so, a toast that waits to be seen #

The three toast events change signature, of_add_button returns a code, and a toast is no longer lost when the application is minimized.

What3.04.0
toaster: ue_toast_clicked, ue_toast_action, ue_toast_dismissed(string as_key …): as_key carried the identifier of the toast, as text(long al_id, string as_key …): al_id is the number of_show returned, as_key the is_key of the toast (empty without one)
toaster: of_add_buttonreturned the number of buttons; a key already taken, an empty key and a fourth button went through without a wordreturns 0; -5 with nothing added for an empty key, a key already taken, one holding / or a vertical bar, or a fourth button. The number is read with of_count()
toaster: of_show with a setting out of rangeshowed it anyway: an unknown kind as an information, an unknown corner top leftreturns -5 and shows nothing: is_kind, is_position or is_sound outside their constants, il_max_visible outside 0 to 20, il_timeout below TIMEOUT_AUTO
toaster: the same is_key shown by two toastersthe second replaced the first one's toast and took its events; an update kept the original cornereach toaster has its own; an update also takes the corner, the screen anchoring and the anchor window
toaster: the sounda system sound for every toast, information and success included; none for a toast coming out of the waiting lineis_sound: SOUND_AUTO (default) = error and warning only, SOUND_ALWAYS, SOUND_NEVER; played when the toast is asked for, even if it waits for room
toaster: toasts anchored to the screen from two windows, or without ipo_ownerlanded exactly on top of each other; the cap was counted per windowone stack per monitor and per corner, and il_max_visible counts per monitor
toaster: a toast anchored to a minimized windowwent to -32,000 px and expired without being seenwaits: it is not shown and its countdown does not run until the window comes back
toaster: a toast arriving while the stack is hoveredcounted down and could leave during the readingis born paused, like the others
toaster: PBT_SetLanguage, PBT_SetDefaultTheme, PBT_SetDefaultThemeAccent, PBT_SetDefaultFont, PBT_SetFlowDirection while a toast is showndid not reach it; the font size stayed 11 ptfollow at once, font size included
toaster: the text of a toastcould not be selected; grabbing the scrollbar of a long text could answer and closecan be selected and copied (right-click, Copy); the selection and the scrollbar no longer close the toast
toaster: il_button_countpublic variableprivate: read the number of buttons with of_count()
toaster: a toast whose text quotes __licensewas not shownis shown: only a reserved word written as a key is still refused
toaster: of_close of a toast on screenno eventue_toast_dismissed, like its cross; a toast still waiting, never seen, raises nothing

The recipe: (1) add long al_id as the first argument of your ue_toast_clicked, ue_toast_action and ue_toast_dismissed, and replace Long(as_key) with al_id; as_key is now the key of the toast; (2) if you read the return of of_add_button as a number of buttons, read of_count(); (3) to keep a sound on an information or a success, set is_sound = SOUND_ALWAYS.

11.39 messagebox: an empty required field is never accepted, choices with unique keys #

Five behaviours change; no signature does.

What3.04.0
messagebox: ib_input_required and a default button with a countdown (of_add_button_timed)the automatic click accepted the box with the field empty (of_show returned the button, of_input_value an empty string)the countdown runs out without clicking; the button waits for the field to be filled
messagebox: Esc or Alt+F4 on a button that is both default and cancel, required field emptyreturned 1, like a click on that buttonreturns 0: closed without a choice
messagebox: of_add_choiceaccepted a key already taken, or holding / or ``returns -5 and adds nothing
messagebox: of_choose without any choiceshowed a box with a single Cancel buttonshows nothing: an empty string, and of_get_last_error says "no choice to show"
messagebox: the buttons of the ready-made boxes (of_info, of_confirm, of_yes_no, of_prompt…) when the application set no language"Ok""OK", the word of the English catalogue ("Cancel", "Yes", "No" for the others)

The recipe: (1) a required field is now accepted by hand — if you relied on the automatic click to move on, give the field a default value (is_input_value); (2) give each choice a unique key, with no / or |, and test for -5 when your keys come from data.

11.40 Common base: the language announced by default, the decimal numbers of every machine #

Two fixes of the common base, valid for every component; no signature changes.

What3.04.0
the language a component announces (<html lang>)French, as long as the application chose no languageEnglish, the default language of the components; the language chosen by the application always replaces it
a decimal number given to a property (id_*)only a decimal comma was converted: on a machine whose decimal separator is another character, the value did not arrivethe decimal separator of the machine, whatever it is, is converted

The recipe: nothing to change. An application that chooses its language still sees it applied; a screen reader that read a component of an English application in French now reads it in English.

11.41 Keyboard, focus and room: what the 4.0 tests put right #

Behaviours seen while playing the components in a real application; no signature changes, one method is added.

What3.04.0
Enter on a focused PowerBuilder CommandButtonnothing (the key was lost)the button is pressed (clicked), as in a Windows dialog box; a bar's default button is not
A disabled component (ib_enabled = false)Tab stopped on itit leaves the Tab order; enabled again (or of_reset), it takes its place back
A Ctrl shortcut on a character without a key of its own (AZERTY: - on the 6 key, + with Shift)Ctrl+- never found its shortcutafter the key, the character of the active layout: Ctrl+-, Ctrl++, Ctrl+,, Ctrl+. fire on any keyboard
Typing in an open menu (search, underlined letters)a top-row key gave its digit (AZERTY: - arrived as 6)the character of the layout; an underlined &1 still answers the 1 key
The calendar of the date picker (toolbar)mouse onlyat the keyboard too: arrows (day, week), Page Up/Down (month), Home/End, Enter picks, Escape closes
Alt with two ribbons (or menu bars) in the same window, no sub-window between themthe first one createdthe one nearest the focus on screen
breadcrumb in a very narrow barthe first segment never gave ground: the current place fell to nothing, the trail overflowedafter the middle, the current place gives ground down to a letter and “…”, then the first segment; nothing leaves the bar
menubar: a choice made with the mouse in the drop-downthe focus stayed on the bar (Edit > Paste pasted nowhere)the focus goes back to the control that had it
ribbon: typing in a combo box then choosing in its listtwo ue_combo_changed: the typed text, then the choiceone, the choice
toolbar: is_text = "" set in ue_text_changed, the focus in the fieldthe old text came back, with a second eventthe field stays empty
The focus given back to a component after a window of the library (command palette)the page, with no element: the code editor's caret was gonethe element that had it
A label with an underlined letter (&File) in right-to-leftletters out of order (“ileF”)the word reads in order
n_pbt_utils.of_dir_folders(as_dir, ref as_names[])DirList into a ListBox: 4.5 s for 30,000 folders, and the application's current directory changednew: the sub-folders as the Explorer shows them, sorted, in one pass; -4 unreadable, -5 empty path
breadcrumb: of_add_children(as_keys, as_child_keys[], as_texts[])one branch at a time (of_add_child): 30,000 sub-folders opened the menu in over 20 snew: a whole level in one call

The recipe: nothing to change. An application whose focused CommandButton had to ignore Enter disables it (Enabled = false) or moves the focus away; for a large folder, replace the loop of of_add_child with one of_add_children.

11.42 Data and keyboard: what the second batch of 4.0 tests put right #

Behaviours seen while playing the data components in a real application; no signature changes.

What3.04.0
Enter or Escape pressed in a component that does not take themlost: the Default or Cancel PowerBuilder CommandButton did not firethe key goes back to the PowerBuilder window, as chapter 6.4 promises: Escape presses the Cancel button, Enter the Default one
PBT_SetLanguage followed at once by an order to the componentthe text of an event raised by that order (ue_error of xmltree) stayed in the old languagethe language holds from the next order on
jsontree, xmltree: the focus given to the component (of_focus_webview, the Tab key)stayed on the page: the arrows and Enter went nowherethe tree takes it; F3 and Shift+F3 go to the next or previous occurrence of the search
xmltree: an error reported AFTER the last character of its lineno column underlinedthe end of the line is marked (↵)
codeeditor with line wrapping (ib_wrap)the il_doc_line band could sit one row too high, on the end of the previous linethe bands cover every row of their line and follow the resizing
codeeditor: "Replace all" and of_replace_all on a long textseveral seconds when the focus was in the editor; beyond that, of_replace_all could return 0instant, and the number of replacements is returned; Ctrl+H with an empty search puts the caret in "Find"
crosstab: "Add to columns" (or rows, filters) from a chip's menu, by keyboardthe focus was lost; only Enter opened a chip's menuthe focus follows the chip to its new zone; Shift+F10 and the Menu key open its menu too
json: thousands of of_add_* in a rownearly a second for 2,000 rowsa few tens of milliseconds; the document is the same

The recipe: nothing to change. An application that handled Escape pressed in a component itself (a shortcut registered by of_register_shortcut) keeps it: a shortcut comes before the window.

11.43 Content and media: what the third batch of 4.0 tests put right #

Behaviours seen while playing the content and media components in a real application; no signature changes.

What3.04.0
speechout: of_speak then of_pause in the same scriptue_started and the first ue_sentence arrived before ue_paused: the voice was announced as startedue_started is raised when the voice really starts speaking; a pause set before that holds it: nothing is said or announced until of_resume
speechout: a language with no exact voice on the machine (fr-CA where there is only fr-FR)ue_started announced the language asked for (fr-CA)ue_started announces the language really read (fr-FR), after ue_voice_fallback
webbrowser: Tab after the last control of the address barthe focus left the component for the PowerBuilder windowthe focus enters the site, as in a browser; Shift+Tab from the site goes back to the bar
webbrowser: ib_private = true set while browsing is already privateno effect: the site found again what it had kept in the private sessiona new, empty private session every time
video in full screen: a modal box of the application (a MessageBox in ue_ended)hidden under the film, which took the keyboard back from it: the application looked frozenthe film gives way: the box comes in front and gets the keyboard; once it is closed, the film takes the screen back and Escape leaves it
tilesbox: an arrow key at an edge, next to a smaller tileUp arrow on a large tile of the top row jumped sideways, to the small neighbouronly a tile entirely past the edge counts: at an edge, the focus stays where it is
dockcontainer: il_back_color / il_text_color of a stacked panellost as soon as its tab was active (it took the theme's accent)the active tab keeps the panel's colours, like its button on the rail
progressbar: id_value set at every turn of a long loopabout 2.5 s for 20,000 rowswell under a second; the bar still moves while the loop runs

The recipe: nothing to change. An application that counted on ue_started to know that of_speak had been accepted reads its return code (0) instead; one that compares the language of ue_started with is_lang now learns about the fallback from that difference too, besides ue_voice_fallback.

11.44 Data: read a cell, sort and search more finely (json, jsontree, xmltree, crosstab) #

These 4.0 additions remove no signature. Two change what you see without you writing anything: the crosstab's Ctrl+C now copies the headers, and ue_cell_double_clicked receives one more argument.

What3.04.0
crosstab: ue_cell_double_clicked( string as_row_tuple_json, string as_col_tuple_json, double ad_value )( string as_row_tuple_json, string as_col_tuple_json, integer ai_measure, double ad_value ): ai_measure is the position of the double-clicked value in Values, from 1; with the two tuples, it is the address of_get_cell_value reads back
crosstab: Ctrl+C on cellsthe figures alonethe headers with them: the names of the columns on a first line, the member of each row (its path, "North / Boston") in a first column; ib_copy_headers = false gives the figures alone
crosstab: of_remove_field, of_get_cell_value—new: take a field out of every zone without rebuilding the layout; read the value of a cell by its tuples and the position of its measure
json: of_get_array, of_pretty (long al_indent)—new: every element of an array in one call; an indent in spaces, or the document compact on one line with 0
jsontree, xmltree: the searchalways case-insensitive, and inside wordsib_find_match_case and ib_find_whole_word, like the codeeditor, with the Aa and ab toggles of the search box; by default the search stays that of 3.0

The recipe: a script of ue_cell_double_clicked has nothing to change, the argument comes before the value. An application that pastes the result of Ctrl+C into a process that expects figures only sets ib_copy_headers = false.

11.45 Navigation and containers: tabs from the keyboard, panels on a double click (tab, dockcontainer, ribbon, stepbar, listbar) #

These 4.0 additions remove no signature. Three gestures change what a user sees without you writing anything: the middle click and Ctrl+Tab on a tab, and the double click on a dockcontainer panel or on the title bar of its floating window.

What3.04.0
tab: middle click on a tabnothingcloses a closable tab, by the same path as its cross: ue_tab_closing if ib_veto_close = true, then ue_tab_closed; a tab that cannot close, or a disabled one, does nothing
tab: Ctrl+Tab, Ctrl+Shift+Tab, Ctrl+Page Down / Page Upnothingthe next or previous tab that can become active, wrapping round, whether the keyboard is in the band or in a hosted page (a PowerBuilder control, another component); it is a gesture: ue_selection_changing if ib_veto_selection = true, then ue_selection_changed. From a control outside the tab, the key stays with the window
dockcontainer: double click on a header or a tab; on the title bar of a floating windownothing; the floating window was maximiseddetaches the panel into a floating window (ue_panel_floated); on that window's title bar, docks it back in its place (ue_panel_docked), like its cross
dockcontainer: right click on a header or a tabnothinga menu Float, Close, Close others (the other closable panels of the same group); each close asks its own question
dockcontainer: ib_veto_close, ue_panel_closing, of_close_panel, n_pbt_dock_panel.ib_closable—new: the question before a panel closes (opt-in, false by default), closing from code (-4 if you refuse it, -5 for the main panel or a panel that cannot close), and whether a panel can close, changed live
ribbon: n_pbt_ribbon_item.is_image, id_min, id_max, id_stepset at the adda control's image and a spinner's range change live and read back; a spinner's value is brought back into its new range without raising ue_value_changed, like id_value
stepbar: STATE_WARNING, STATE_SKIPPED, NAV_VISITED—new: a step passed with a warning (amber bullet), a skipped step (dashed ring), and a mode where every step already reached stays clickable, ahead too after going back
listbar: n_pbt_listbar_section.ib_pinned—new: a section pinned at the bottom of the bar (Settings, Help), which stays in view while the bar scrolls, in the rail too; the saved layout keeps the order of the sections

The recipe: nothing to change. In a tab page, Ctrl+Tab now changes tab even when the focus is in a control of the page, as in a Windows property sheet. To maximise a dockcontainer floating window, its Maximise button is still there.

11.46 Menus: the menu bar's chevron, a drop-down list in the toolbar, ten thousand commands (menubar, toolbar, commandpalette) #

These 4.0 additions remove no signature. One default behaviour changes: a menu bar that is too narrow no longer wraps, it folds its last titles into a chevron. And of_open of a command palette no longer refuses to open without ipo_owner or ipo_receiver.

What3.04.0
menubar: a bar too narrow for its titleswrapped: its height followed the rows, ue_auto_height at every change of widthstays on one line: the titles that do not fit, from the end, go into the dropdown of a chevron (their entries as cascades, Alt + their letter opens them from the chevron, the arrows stop on it like on a title, on the left in RTL); the height no longer changes. ib_wrap = true gives back the former behaviour
menubar: ib_track_hover, ue_item_hover, of_add_header, n_pbt_menubar_menu.is_align—new: the pointed entry of a dropdown, for the help text of a status bar (opt-in, false by default; an empty address on closing), section headers in a dropdown, and a menu set at the end of the bar (ALIGN_END, Help)
toolbar: of_add_combo, of_add_combo_item, of_remove_combo_item, of_clear_combo, ue_combo_changed, n_pbt_toolbar_item.ib_editable—new: a drop-down list in the bar, editable or not, that stays a working list folded into the chevron; its event has the name and the meaning of the ribbon's
commandpalette: two palettes on the same ipo_ownershared their queue of events (one could receive the other's ue_closed), and the second took the chord of the firsteach its own events and chord; the same chord registered by the second goes to it (1). of_open and of_register_shortcut no longer return -5 without ipo_owner or ipo_receiver: the events always arrive
commandpalette: thousands of commandsevery line drawn: opening and typing slowed downonly the visible lines are drawn: 10,000 commands open and filter without a wait
Shortcuts of the whole library: AltGr + a key that types a character (the euro of a French keyboard)fired the Ctrl+Alt shortcut of that key, in a PowerBuilder field toothe character is typed, no shortcut fires; AltGr alone no longer hands the keyboard to a menu bar or a ribbon

The recipe: a menu bar whose variable height was wanted sets ib_wrap = true; an application that laid out its controls in ue_auto_height at every resize has nothing more to do after the first one. An application shortcut on Ctrl+Alt + a letter that the user's keyboard types with AltGr no longer fires while he types: choose another key.

11.47 Content and media: a log line by line, the actual size of a picture, a film that waits (statictext, picture, video) #

These 4.0 additions remove no signature and change no default. Two behaviours become more precise: the zoom of a picture may go past its ceiling to reach the actual size, and a film from a URL played without a network says so.

What3.04.0
statictext: of_append_text, ib_follow_end—new: a log written line by line — only the line added travels to the component — and, as an option, the view that stays at the bottom as long as the reader does not scroll up
statictext: ib_auto_width, ue_auto_width—new: the userobject takes the width of its text (opt-in, false by default), as ib_auto_height does for the height
picture: id_max_zoom, the 1 keythe zoom stopped at 8.0 times the fitted view, whatever happened: a large scan shown small was never readable pixel for pixelthe ceiling is set (id_max_zoom, 8.0 by default); the 1 key shows the actual size, and the ceiling rises to it when it asks for more
video: ue_buffering, of_is_bufferinga film waiting for its data stayed frozen without a worda ring in the theme's accent turns on the picture, and ue_buffering(true) then ue_buffering(false) frame the wait
video: a film from a URL while the workstation has no network"this file cannot be found or played", ue_failed with REASON_UNSUPPORTED"no network connection" on the picture, ue_failed with REASON_NETWORK

The recipe: nothing to change. A log that rewrote is_text at every line moves to of_append_text; code that tested REASON_UNSUPPORTED for a film from a URL without a network tests REASON_NETWORK.

11.48 Review of 03/10/2026: a layout put back is announced, an export to a relative path lands in the right place (toolbar, crosstab) #

No signature changes. Two behaviours line up with the rule of the library: every change of the layout is announced, and a relative path to write to is resolved as for the other exports.

What3.04.0
toolbar: of_set_layoutno eventraises ue_layout_changed, as the dockcontainer and the datagrid do: every change of the layout is announced, whether it comes from the user or from your code
crosstab: of_export_csv, of_export_xlsx to a relative pathwritten in the current folder of the moment — which a DirList or a file dialog may have moved; ue_csv_saved / ue_xlsx_saved gave the path as givenwritten in the folder the application started in, as the other native exports; the event gives the full path; a path that depends on the current folder of a drive (\x, C:x) is refused

The recipe: an application that saves the layout in ue_layout_changed saves the same value again after of_set_layout — harmless. An export that relied on the current folder of the moment gives a full path.


← Migrating from 2.0 to 3.0 · Contents · Release notes →