11. Migrer de la 3.0 à la 4.0 #
← Migrer de la 2.0 à la 3.0 · Sommaire · Notes de version →
La 4.0 retire deux méthodes des trente-deux composants de la 3.0 — of_set_data du tableau croisé, renommée of_from_datastore (11.33), et of_transform de n_pbt_xml, qui part avec XSLT (11.31) — et change des signatures que le compilateur vous montre ligne par ligne : trois propriétés renommées (id_minimum et id_maximum de la barre de progression, 11.6 ; il_doc_line de l'éditeur de code, 11.28), des arguments d'événement ajoutés ou renommés (ue_tile_moved, 11.6 ; ue_failed du client REST, 11.6, et de la vidéo, 11.26 ; ue_error de l'image, 11.25 ; ue_cell_double_clicked, 11.33 ; ue_closed de la palette de commandes, 11.36 ; ue_toast_clicked, ue_toast_action et ue_toast_dismissed du toaster, 11.38), of_count et of_keys_at en long (11.34) et des méthodes qui rendent désormais un code (of_refresh, of_select, of_show, of_scroll_to_end, of_set_header…). La 11.6 aligne aussi deux comptes, un événement et le chemin de l'arbre JSON sur le reste de la bibliothèque : c'est là, avec les sections citées, que du code qui compilait est à retoucher. Ce qui change est concentré sur le ruban (ribbon), qui a fait l'objet d'une revue complète. Deux changements demandent de relire votre code : les questions posées avant une vignette de galerie du ruban et avant une sélection de la barre latérale (listbar) (11.1) et les poignées libérées par un retrait (11.2). Les autres corrigent des comportements que vous avez pu contourner (11.3) — dont le clic droit sur un item, qui ne produit plus qu'un seul événement dans tous les composants, et le fil d'Ariane (breadcrumb), dont les adresses et les refus ont été revus. Viennent ensuite le générateur n_pbt_menu2ribbon (11.4) et ce qui a été ajouté (11.5). Les onglets (tab) suivent la même règle : leurs deux questions, avant un changement d'onglet et avant une fermeture, sont désormais facultatives (11.1). Enfin, les thèmes suivent l'application (11.7) : l'accent de l'application survit à un changement de thème, et un axe de thème laissé vide suit celui de l'application. La revue de sécurité de crypto (11.8) ferme une confusion d'algorithme des jetons JWT, remplace plusieurs silences par un refus qui dit sa raison et fixe la règle des chemins relatifs des fichiers.
La revue complète du 28/09/2026 a relu chaque composant livré ; ce qu'elle change a une section par composant : restclient (11.9), speechout (11.10), statictext et le texte riche (11.11), breadcrumb (11.12), buttonbar et le clavier de fenêtre (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 et 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) et messagebox (11.39) ; le socle a deux sections, 11.34 et 11.40. Une règle les traverse, celle de PowerBuilder (SelectTab lève SelectionChanged) : un changement demandé par votre code lève le même événement qu'un geste de l'utilisateur, et pose la question du veto si vous l'avez activé ; la méthode rend -4 si vous refusez. Cela vaut pour une sélection (of_select_*), une ouverture ou un repli (of_expand, of_collapse, of_open), un zoom, un plein écran, un chapitre ; rien n'est levé quand rien ne change, et of_reset reste silencieux. Poser une VALEUR (une coche, un texte, un volume, un filtre) reste silencieux, comme SetItem. ue_layout_changed est levé dans les deux cas : il dit qu'une disposition à enregistrer a changé.
11.1 Les questions avant un changement sont désormais facultatives #
En 3.0, le ruban posait d'office la question ue_gallery_selection_changing avant de retenir une vignette : ib_veto_gallery valait true. En 4.0 il vaut false, comme tout événement annulable de la bibliothèque : chaque question coûte un aller-retour vers PowerBuilder, et seule une application qui y répond doit le payer. Une application qui ne scriptait pas l'événement ne voit aucune différence. Nouveau : of_select_gallery_item lève ue_gallery_selection_changed comme un clic et, la question armée, la pose aussi ; un refus lui fait rendre -4, la vignette ne change pas.
// 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
La recette : cherchez ue_gallery_selection_changing dans vos descendants du ruban ; là où il est scripté, ajoutez ib_veto_gallery = true à la construction. Là où vous écriviez ib_veto_gallery = false pour couper la question, la ligne ne sert plus à rien et peut partir.
La barre latérale (listbar) : ib_veto_selection #
Même règle : en 3.0 la listbar posait d'office ue_selection_changing avant chaque changement de sélection (ib_veto_selection valait true). En 4.0 il vaut false. Si vous scriptiez ue_selection_changing pour refuser un déplacement, posez ib_veto_selection = true : sans cela, l'événement n'est plus levé et la sélection passe sans vous demander. Armée, la question est posée aussi pour une sélection demandée par votre code (of_select_item, of_clear_selection), comme en 3.0 ; un refus leur fait désormais rendre -4, et la sélection ne bouge pas. ue_selection_changed part comme avant.
// 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
La recette : cherchez ue_selection_changing dans vos descendants de u_pbt_listbar ; là où il renvoie false, ajoutez ib_veto_selection = true à la construction. Là où vous écriviez ib_veto_selection = false pour couper la question, la ligne ne sert plus à rien et peut partir.
Les onglets (tab) : ib_veto_selection et ib_veto_close #
Même règle, deux questions : en 3.0 le composant tab posait d'office ue_selection_changing avant chaque changement d'onglet et ue_tab_closing avant chaque fermeture (ib_veto_selection et ib_veto_close valaient true). En 4.0 les deux valent false. Si vous scriptiez ue_selection_changing ou ue_tab_closing pour refuser, posez ib_veto_selection = true ou ib_veto_close = true : sans cela, l'événement n'est plus levé et l'onglet change ou se ferme sans vous demander. Armée, la question est posée aussi pour of_select_page, comme en 3.0, mais avant que l'onglet ne change : un refus lui fait rendre -4 et rien ne bouge ; un accord change l'onglet tout de suite, ue_selection_changed part comme avant, et of_selected_key() relu juste après rend déjà la nouvelle clé.
// 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
La recette : cherchez ue_selection_changing et ue_tab_closing dans vos descendants de u_pbt_tab ; là où l'un renvoie false, ajoutez la ligne correspondante à la construction. Là où vous écriviez = false pour couper une question, la ligne ne sert plus à rien et peut partir.
11.2 Les ajouts disent ce qu'ils refusent, les retraits libèrent leurs poignées #
En 3.0, un ajout que le ruban ignorait — une clé déjà prise, une entrée de menu sous un simple bouton — rendait quand même 0, et rien n'apparaissait. En 4.0 il rend -5, et le code de l'envoi (-2 quand le composant n'est pas créé) au lieu d'un 0 fixe. Et un retrait libère les poignées de ce qui part : une variable qui tenait la poignée d'un contrôle retiré n'est plus valide.
| Quoi | 3.0 | 4.0 |
|---|---|---|
| Ajout d'une clé déjà prise (onglet, groupe, contrôle) | 0, ignoré | -5 |
| Entrée de menu, choix de combo ou vignette sous un contrôle d'une autre nature | 0, ignoré | -5 |
of_add_tab, of_insert_tab, of_add_group, of_add_contextual_*, of_add_qat sur un composant non créé | 0 | -2 |
Une poignée tenue après of_remove_tab, of_remove_group, of_remove_item ou of_clear | restait en mémoire jusqu'à la destruction du ruban | libérée : IsValid() rend 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
La recette : ne gardez pas une poignée au-delà d'un retrait ou d'un of_clear — redemandez-la par of_tab, of_group, of_item ou of_menu_item, ce qui ne coûte rien. Là où une erreur de construction vous importe, testez le code rendu par l'ajout.
11.3 Autres comportements que vous pouvez voir #
Quand la fenêtre est étroite, le ruban replie ses groupes en un bouton qui ouvre un panneau. C'est de ce panneau que viennent les écarts les plus visibles :
| Quoi | 3.0 | 4.0 | |
|---|---|---|---|
| Bouton, ou partie principale d'un split, cliqué dans le panneau d'un groupe replié | ue_menu_selected("", false) | ue_clicked(adresse) | |
| Entrée de menu choisie dans ce panneau | adresse incomplète | adresse complète, cascade comprise | |
| Contrôle grisé, ou entrée masquée, dans ce panneau | répondait / était affichée | ne répond pas / n'est pas affichée | |
| Grand split réduit faute de place | un clic ouvrait seulement le menu | deux parties : la commande et la flèche du menu | |
Entrée de menu qui porte la clé de son contrôle (home/clip/paste/paste) | ue_clicked("home/clip/paste") | ue_menu_selected("home/clip/paste/paste", …) | |
of_menu_item("…/exp/pdf") quand une cascade porte aussi pdf | pouvait viser …/exp/more/pdf | vise l'entrée que l'adresse nomme | |
as_from_keys de ue_gallery_selection_changed sans vignette précédente | l'adresse de la galerie | "" | |
Relire une poignée de barre d'accès rapide, il_color d'un sélecteur de couleur, ii_visible_items, is_keytip d'un onglet, il_color d'un groupe contextuel, of_count() d'une galerie | vide, -1 ou 0 | la valeur à l'écran | |
of_keys_at d'un combo | "home/font/cb/cb:Arial" | "home/font/cb/Arial" : la clé d'un choix est son libellé | |
of_ctx_group(k).il_color quand un onglet porte aussi la clé k | colorait l'onglet | colore la seule bannière | |
Couleurs d'item (il_back_color…) et is_tooltip d'un onglet ou d'un groupe | sans effet | appliquées ; l'infobulle d'un groupe s'affiche sur son titre | |
of_remove_item("save") sur un bouton de barre d'accès rapide | -5 | le bouton est retiré | |
of_add_app_menu_separator("saveas/s1") | une ligne à la racine du menu | la ligne dans le sous-menu de saveas ; une clé sans / ne change pas | |
of_clear() alors qu'un onglet était actif | aucun événement | ue_selection_changed("") | |
Spinner au pas décimal (0.1) | 0.30000000000000004 | 0.3 | |
| Raccourci Alt (keytip) sur un contrôle grisé, ruban réduit | le ruban restait ouvert | il se referme, rien ne s'exécute | |
| toolbar : combo, spinner ou galerie grisés dans le panneau du chevron | répondaient | ne répondent pas | |
Tous : clic droit sur un item (ue_item_rclicked, ue_panel_rclicked, ue_row_rclicked…) | la page envoyait aussi l'événement du composant, qu'aucun composant à items ne relayait à PowerBuilder | un seul événement, celui de l'item ; un ue_rclicked de composant, là où il existe, ne part que hors des items | |
Tous : un dépôt de fichiers (ib_allow_drop) | ue_drag_leave puis ue_drop_files ; chemins coupés à 260 caractères | ue_drop_files seul, chemins entiers ; of_reset retire la cible de dépôt | |
breadcrumb : of_path() avec un segment masqué, ou en version de démonstration | les clés affichées : le masqué sauté, les premiers segments coupés | l'adresse du lieu courant, complète : of_truncate(of_path()) tombe juste ; le champ de saisie part de la même | |
breadcrumb : of_keys_at(n) ; of_has("computer/C:") | la clé nue ; cherchait parmi les branches du menu | l'adresse du segment ; cherche parmi les segments | |
breadcrumb : lire une poignée d'adresse profonde, of_item("a/b/c").is_text | lisait le dernier niveau seul : vide sur une clé répétée | le segment de cette adresse | |
breadcrumb : of_truncate, of_remove_item, of_clear_children, of_remove_child sur une adresse inconnue ou une clé nue répétée ; of_add_child sous un segment absent ou sur une branche déjà prise ; of_add_item d'une adresse qui atterrirait ailleurs | 0, ignoré (ou la seule dernière clé retenue) | -5 | |
breadcrumb : une poignée tenue après of_clear, of_remove_item, of_truncate, of_clear_children ou of_remove_child | restait en mémoire | libérée : IsValid() rend false | |
| breadcrumb : champ de saisie quitté sans rien changer, ou fenêtre changée (Alt+Tab) en cours de frappe | ue_path_entered levé | rien ; la saisie reste en changeant de fenêtre | |
breadcrumb : segment grisé ; Entrée sur un segment | son chevron s'ouvrait et il recevait un dépôt ; Entrée pressait aussi le bouton par défaut de la fenêtre | inerte en entier ; Entrée ne clique que le segment | |
buttonbar : Entrée quand un autre bouton de la barre a le focus clavier | déclenchait le bouton par défaut (is_default) | déclenche le bouton qui a le focus, comme Windows ; le défaut part quand aucun bouton de la barre n'a le focus | |
buttonbar : bouton par défaut ou annuler grisé ou masqué ; raccourci is_shortcut d'un bouton grisé ou masqué | Entrée, Échap ou Alt+lettre avalés sans effet, même dans un contrôle PowerBuilder de la fenêtre | la touche reste à la fenêtre | |
buttonbar : is_shortcut de plusieurs lettres ("Save") | Alt+S ne marchait que si la barre avait le focus | Alt+S marche de partout dans la fenêtre (la première lettre, comme documenté) | |
buttonbar : of_add_button / of_insert_button sur une clé déjà prise ou qui contient / ou une barre verticale ; of_remove_button / of_move_button sur une clé inconnue | 0, ignoré | -5 | |
buttonbar : of_remove_button, of_clear_buttons | le raccourci Alt+lettre, les couleurs et l'info-bulle du bouton restaient (un bouton recréé sous la même clé les reprenait) ; la poignée restait en mémoire | ils partent avec le bouton ; la poignée est libérée : IsValid() rend false | |
codeeditor : relire is_text d'un texte en CRLF | en LF (en CRLF seulement si l'éditeur était replié) : is_text = ls_original était faux | avec les fins de ligne reçues, replié ou non | |
codeeditor : relire is_syntax après SYNTAX_CSHARP, SYNTAX_CPP, SYNTAX_JAVA, SYNTAX_XML | la grammaire utilisée ("c", "html") | la valeur écrite | |
codeeditor : of_insert_text en lecture seule ou replié | inséré en lecture seule ; refusé replié, mais 0 | -4, rien n'est inséré | |
codeeditor : ii_doc_line | une bande seule, souvent hors de la vue, absente en retour à la ligne ; un integer | la ligne vient au milieu de la vue (une région repliée qui la cache s'ouvre), bande aussi en retour à la ligne ; curseur et sélection intacts ; un long — renommé il_doc_line en 4.0 (section 11.28) | |
codeeditor : #regionopen avec ib_folding | réécrit #region dans le texte (is_text, et après le repli) | jamais touché ; seul l'affichage replié montre #region | |
codeeditor : ue_find_result | deux fois par of_find, à chaque frappe, et (0, 0) à la fermeture de la barre ; levé même avec ib_search_enabled = false | une fois par recherche ou par pas ; à la frappe si le compte change ; jamais à la fermeture ; of_find sans effet quand la recherche est désactivée | |
codeeditor : ue_caret_changed | muet pendant la frappe ; replié, la ligne AFFICHÉE | levé aussi à la frappe ; replié, la ligne de la gouttière | |
codeeditor : Ctrl+Z après Entrée ou Tab ; Entrée / Tab en mode replié | l'historique d'annulation était vidé ; replié, la touche modifiait l'affichage et se perdait ensuite | chaque frappe s'annule ; replié, la touche est sans effet | |
codeeditor : ib_folding = true avec ib_line_numbers = false | les régions restaient fermées, sans marqueur pour les ouvrir | une gouttière étroite garde les marqueurs | |
commandpalette : of_add_command sur une clé déjà prise ou qui contient / ou une barre verticale ; of_remove_command sur une clé inconnue | 0 (une clé prise donnait deux commandes jumelles) | -5 | |
commandpalette : of_open / of_register_shortcut sans ipo_owner ni ipo_receiver | 0, et aucun événement n'arrivait jamais | -5 | |
| commandpalette : l'accord pressé pendant que la palette est ouverte | sans effet | referme la palette | |
| commandpalette : un objet palette détruit (page ou fenêtre fermée) | son accord restait posé et avalait la touche dans la fenêtre, sans aucun événement | l'accord part avec l'objet, et une palette qu'il avait ouverte se referme | |
commandpalette : raccourci affiché sur une ligne, sur un clavier non latin, avec un chiffre et Maj, Ctrl+Space, Ctrl+Up, Ctrl+Del, ou une touche F seule | ne répondait pas ; Win + la lettre lançait la commande au raccourci d'une seule lettre | répond : la touche physique, lue comme la DLL la lit ; Win n'est jamais un modificateur | |
commandpalette : une commande dont le raccourci est Ctrl+C, Ctrl+V, Ctrl+Z ou Ctrl+A, pendant la frappe | la commande partait à la place de la copie, du collage… | la zone de recherche garde ces quatre touches | |
commandpalette : of_reset | gardait is_recent, une palette ouverte et l'accord déjà posé | les vide, la referme, et ramène l'accord posé à SHORTCUT_DEFAULT | |
commandpalette : pbm_custom02 de la fenêtre ipo_owner | déclenché à chaque événement de la palette | jamais : seul un ipo_receiver posé le reçoit encore | |
Tous : relire une longue valeur texte (is_text d'un gros script) | de plus en plus lent avec la taille (plusieurs secondes) ; coupé sans prévenir au-delà d'un million de caractères | proportionnel à la taille ; entier | |
n_pbt_utils.of_dir_has_subfolders | comptait les dossiers cachés, système et les jonctions ; illisible au-delà de 260 caractères | les ignore, comme l'Explorateur (la surcharge ab_include_hidden les compte) ; lit les chemins longs | |
crosstab : séparateur de milliers de of_set_value_format | valait pour TOUTES les mesures ; "," non reconnu ($1 234 en français) | réglage de CETTE mesure ; ",", ".", " " reconnus ; "" = défaut du menu Options | |
crosstab : of_set_member_filter(champ, "") | aucun membre gardé : tableau vide | retire le filtre, comme of_set_member_order("") | |
crosstab : is_show, is_conditional_formatting, of_set_value_format avant of_add_value_field | perdus sans rien dire | gardés, relus tels quels, appliqués quand le champ entre en Valeurs | |
| crosstab : tri d'une colonne ou repli d'un groupe à la souris | aucun événement | ue_layout_changed, comme tout ce que of_get_layout enregistre | |
crosstab : dates d'un DataStore sur un poste non français (3/15/2025, 15.03.2025) | groupées dans un mois faux ou vide ; les dates triées sur l'année seule | le bon mois ; la colonne relue en aaaa-mm-jj, triée dans l'ordre du temps | |
| crosstab : nombres affichés en allemand, italien, espagnol, portugais | à l'américaine (1,234.5) | dans la langue d'affichage (1.234,5 en allemand) | |
crosstab : of_export_csv | nombres à point (1436.5), lus comme du texte par un Excel français ; un libellé =… s'exécutait à l'ouverture | séparateur décimal de la langue d'affichage ; une apostrophe devant = + - @ | |
crosstab : of_export_xlsx | totaux et sous-totaux masqués exportés quand même ; total en tête ignoré ; symbole « avant » passé après | le tableau tel qu'à l'écran | |
| crosstab : « Ajouter aux valeurs » du menu d'un champ déjà en Valeurs | remplaçait ses valeurs | ajoute une valeur, comme le glisser | |
| crosstab : « Retirer » du menu d'un champ filtré | le filtre restait actif, invisible | le filtre part avec le champ | |
crosstab : AGG_DISTINCT_COUNT sur une colonne à cellules vides | le vide comptait pour une valeur | ignoré, comme dans Excel | |
| crosstab : formule qui cite une mesure calculée | acceptée, valait 0 | refusée par ue_calc_field_error ; ses messages sont en anglais | |
crosstab : ue_layout_changed avec une accolade dans un membre ; ad_value de ue_cell_double_clicked | disposition tronquée ; valeur 0 si un champ en lignes s'appelle value, exposant perdu | lues entières | |
crosstab : ib_enabled = false, au clavier | Entrée ouvrait le menu d'un chip, Ctrl+C copiait | muet | |
| crosstab : clavier | le focus retombait en haut de page après chaque action ; aucun moyen de déplacer un champ | le focus reste sur l'élément actionné ; le menu d'un chip le déplace (autre zone, à gauche, à droite) | |
| crosstab : thème sombre | négatifs et flèches d'icônes peu lisibles ; cellule d'échelle de couleurs sélectionnée illisible | une palette propre au mode sombre | |
crypto : of_jwt_sign avec JWT_EDDSA | l'en-tête disait "alg":"EDDSA", que les autres bibliothèques (jose, PyJWT, jjwt) refusent | "alg":"EdDSA", le nom enregistré ; un jeton 3.0 se vérifie toujours | |
crypto : of_generate_keypair d'une sorte ou d'une taille inconnue ("ec_p256", 1024) | 0 et une paire RSA 2048, sans un mot | -5, les deux PEM vides, is_last_error nomme les KEY_* admises | |
crypto : of_hash_file("SHA-256", …) ; algorithme inconnu | vide, et is_last_error disait « fichier illisible » d'un fichier parfait | le nom se lit comme of_hash le lit (SHA-256 = HASH_SHA256) ; un algorithme inconnu est dit tel quel, distinct d'un fichier illisible | |
crypto : of_base64_encode_file d'un fichier trop gros (plus de 64 Mo en 32 bits, 512 Mo en 64 bits) ; d'un fichier absent en démo | « fichier illisible » ; « limite de la démo » | « trop gros pour ce processus » ; « fichier illisible » — is_last_error dit la vraie raison | |
crypto : is_last_error après un appel natif réussi (of_crc32, of_hash en MD5, of_hash_file…) | gardait l'erreur de l'appel précédent | vide | |
crypto : clé HMAC vide ; exp/nbf non numérique d'un JWT ; of_password_verify d'une valeur à plus de 10 000 000 tours ; of_random_password d'un jeu inconnu | erreur obscure du moteur ; jeton valable pour toujours ; calcul de plusieurs minutes ; jeu CHARSET_ALL pris sans un mot | refusés tout de suite, is_last_error dit pourquoi | |
crypto : of_base64_decode_to_file("", chemin) | -5 | 0, un fichier vide | |
dockcontainer : of_add_panel avec un as_relative_to inconnu, un contrôle déjà hébergé, une clé contenant / ou une barre verticale, ou POSITION_STACK sur le panneau principal | 0 : le panneau se dockait contre la racine, le contrôle était hébergé deux fois, ou le panneau empilé sur le principal restait invisible | -5, et le contrôle n'est pas touché | |
dockcontainer : of_move_panel vers une cible inconnue, ou POSITION_STACK sur le panneau principal | 0 : le panneau partait contre la racine, ou rien ne bougeait | -5 | |
dockcontainer : of_remove_panel, of_reset | le contrôle revenait dans sa fenêtre masqué, à la position et à la taille du dock | il revient à sa place, à sa taille et avec la visibilité qu'il avait avant of_add_panel | |
dockcontainer : is_main posé avant of_add_panel ; is_main = "" ; is_main d'un panneau empilé | ignoré en silence ; rien n'était transmis ; ses voisins de pile devenaient invisibles | retenu et appliqué à l'ajout du panneau ; le panneau principal est retiré ; il sort de sa pile, les autres restent visibles | |
dockcontainer : of_set_layout d'une disposition qui ne connaît aucun des panneaux, ou d'un texte qui n'est pas une disposition | le dock se vidait, ou le message entier était rejeté ; 0 | les panneaux restent, rangés à côté des autres (jamais sur le principal) ; -5 pour un texte qui n'est pas une disposition | |
dockcontainer : ue_layout_changed | levé au glisser, au redimensionnement et au docking seulement : une disposition enregistrée à cet événement oubliait un panneau fermé ou l'onglet actif | levé aussi quand un panneau est fermé, masqué, sélectionné ou renommé, et quand le panneau principal change | |
| dockcontainer : clic sur l'onglet (ou l'en-tête) du panneau déjà actif | ue_panel_selected à chaque clic | rien : l'événement dit un CHANGEMENT de panneau actif | |
dockcontainer : panneau désépinglé (ib_pinned = false) | sa languette ne s'ouvrait qu'au clic (la doc disait « au survol ») | elle s'ouvre au survol (400 ms) ou au clic ; ouverte au survol, elle se replie quand la souris la quitte | |
| dockcontainer : contrôle PB standard (MultiLineEdit, bouton…) dans un panneau flottant | ses notifications (modified, clicked, couleurs) n'arrivaient plus | elles arrivent comme lorsqu'il est ancré | |
dockcontainer : masquer un panneau flottant (ib_visible = false) | sa fenêtre restait ouverte | elle se ferme ; réaffiché, le panneau revient ancré à sa place | |
| dockcontainer : fenêtre flottante | sa zone utile était plus petite que le panneau (barre de titre prise dessus), elle pouvait sortir de l'écran, et un titre à balises les montrait | la zone utile a la taille du panneau, la fenêtre reste dans l'écran, le titre est du texte | |
| dockcontainer : séparateur au clavier | la première flèche le faisait sauter à 50 % | il part de sa position affichée | |
json : valeur nulle donnée à of_set_number, of_add_number, of_set_boolean, of_add_boolean, ou valeur nulle ou vide donnée à of_set_raw / of_add_raw | tout le document devenait nul (of_text rendait null) ou cessait d'être du JSON ; un booléen nul s'écrivait false | null est écrit, le reste du document est intact | |
json : écrire dans un document CHARGÉ dont une clé s'écrit "k" : v ou avec un caractère échappé, suivi d'un saut de ligne, ou dont la racine est un tableau ; of_set_members sur un tel document | la clé était ajoutée en double (la relecture rendait l'ancienne valeur), l'écriture rendait -4 à tort, ou le document était remplacé par le fragment | la valeur est remplacée ou ajoutée comme sur un document bâti ; of_set_members rend -4 si la racine n'est pas un objet | |
json : of_set_*("items/1/qty", …) — une clé NEUVE dans un objet placé dans un tableau | -4 | 0 : la clé est ajoutée au premier élément ; un tableau se traverse par un index qui existe, seule une clé d'objet est créée | |
json : chemin à segment vide (/name, a//b, a/) | des clés nommées "" étaient créées ; of_remove pouvait retirer une autre valeur | -5, rien n'est écrit ; un lecteur répond « absent » | |
json : of_load d'un nombre mal écrit (-, 01, +1, .5, 1., 1e) | 0, et of_is_valid vrai | -4, comme tout JSON invalide | |
json : of_get_number d'un nombre à exposant (1e-7, 2.5E+3) ou à plus de 9 décimales | exposant ignoré (2.5E+3 lu 2,5), décimales coupées à 9 | exposant résolu, 17 chiffres significatifs gardés | |
| json : lire ou modifier un gros document (une réponse d'API de 80 Ko, 2000 lignes) | des secondes par appel (20 s pour of_count), l'application semblait figée | un temps proportionnel à la taille ; lire les lignes une à une (rows/1, rows/2…) ou parcourir les clés par of_name ne repart plus du début | |
listbar : of_add_section / of_insert_section sur une clé prise ou qui contient / ou une barre verticale ; of_add_item / of_insert_item sur une adresse prise ou sous une section que la barre n'a pas ajoutée ; of_remove_item, of_move_item, of_select_item, of_remove_section, of_move_section sur une adresse inconnue | 0, ignoré ; une entrée sous une section inconnue s'affichait sous la dernière section | -5 ; of_add_section rend aussi -2 quand le composant n'est pas créé | |
listbar : une poignée tenue après of_remove_item, of_remove_section ou of_clear | restait en mémoire | libérée : IsValid() rend false | |
listbar : retirer ou vider ce qui porte la sélection (of_remove_item, of_remove_section, of_clear) | la sélection partait sans événement ; après of_clear, of_selected_key rendait encore l'ancienne | ue_selection_changed(ancienne, "") | |
listbar : ue_layout_changed | muet au pli d'une section et au retrait d'une entrée ; levé à l'entrée dans le rail | levé au pli (clic ou ib_collapsed d'une section) et au retrait ; plus au rail, qui n'est pas dans la disposition | |
listbar : rail (ib_collapsed = true) | les entrées d'une section repliée disparaissaient ; une entrée sans icône montrait une case vide ; of_reset laissait la barre étroite | toutes les entrées, un filet entre les sections, l'initiale d'une entrée sans icône, le libellé au survol ; of_reset rend sa largeur à la barre | |
listbar : is_image d'une section ; couleur posée sur la section k quand une entrée a aussi la clé k | sans effet ; l'entrée prenait la couleur aussi | l'icône est dessinée devant le titre ; seule la section prend la couleur | |
| listbar : clavier | après un changement de libellé ou une sélection, les flèches ne répondaient plus ; sans sélection, la barre était hors de la tabulation ; les titres de section ne se pliaient qu'à la souris | le focus reste sur l'entrée ; un arrêt de tabulation ; les titres se rejoignent aux flèches et se plient par Entrée ou Espace | |
listbar : entrée désactivée (ib_enabled = false) ; ib_auto_width sur une barre dépliée | libellé estompé, peu lisible en thème clair ; rapportait la largeur déjà donnée | la couleur « désactivé » du thème ; la largeur que demande la plus longue ligne | |
listbar : glisser une entrée (ib_reorderable) | bouton relâché hors de la fenêtre : l'entrée suivait encore la souris ; le clic de fin pouvait sélectionner l'entrée déposée | le geste s'arrête au retour de la souris ; la fin d'un glisser ne sélectionne rien | |
menubar : ue_menu_opening | levé à côté du déroulant : ce qu'il changeait (griser Coller…) ne se voyait qu'à l'ouverture suivante | le déroulant attend la fin de l'événement : le changement se voit à cette ouverture | |
menubar : of_item, of_remove_item sur une entrée dont la clé existe aussi dans une autre cascade (file/export/pdf, file/print/pdf) | pouvaient viser l'autre entrée (la première trouvée) ; le raccourci affiché sur l'une déclenchait l'autre | visent l'entrée que l'adresse nomme ; une clé seule ne descend plus dans les cascades | |
menubar : of_add_menu, of_add_item, of_add_separator sur une clé prise, qui contient / ou une barre verticale, ou sous un menu ou un parent inconnu ; of_remove_item / of_remove_menu d'une adresse inconnue | 0 ; une entrée au parent mal tapé se rangeait à la racine du menu, une clé prise donnait deux entrées jumelles | -5 | |
menubar : une poignée ou un raccourci après of_remove_item, of_remove_menu ou of_clear | la poignée restait en mémoire ; le raccourci restait armé et avalait la touche, qui n'arrivait plus à l'application | poignée libérée : IsValid() rend false ; raccourci désarmé | |
| menubar : entrée grisée, masquée ou retirée pendant que son déroulant est ouvert ; raccourci d'une entrée sous une cascade grisée | le clic ou la touche levait encore ue_item_selected | rien | |
menubar : couleurs d'item (il_back_color…) d'un menu ou d'une entrée ; is_tooltip d'un menu | sans effet ; une couleur se relisait -1 | appliquées : sur le titre du menu, sur l'entrée dans le déroulant ; l'info-bulle d'un menu s'affiche sur son titre, une entrée du déroulant n'en affiche aucune | |
| menubar : clavier | les flèches restaient bloquées sur un menu grisé ; en RTL, gauche et droite inversées ; Alt+lettre d'un menu masqué cachait la même lettre d'un autre menu ; après Alt+lettre et un choix, l'Alt suivant éteignait | les menus grisés sont sautés ; les flèches suivent le sens d'écriture ; seul un menu affiché répond à sa lettre ; l'Alt suivant rallume la barre | |
menubar : une cascade dont toutes les entrées sont masquées ; of_count("file"), of_keys_at | se choisissait comme une commande (file/export) ; les séparateurs étaient comptés (__sep0) | grisée ; seules les entrées sont comptées | |
menubar : of_item("file") — une adresse d'un seul niveau | relisait le menu, et écrivait sur une entrée « file » qui n'existe pas | ne désigne rien : passez par of_menu("file") | |
Tout texte lu d'un composant ou d'un JSON : échappements \b et \f | restaient écrits tels quels | décodés (retour arrière, saut de page) | |
| messagebox : une boîte qui n'a pas pu s'ouvrir (runtime WebView2 absent, moteur pas prêt) | of_show et les raccourcis (of_info…) rendaient 0, comme une fermeture : of_info laissait croire qu'une boîte s'était montrée | -4 (-6 si le runtime manque), la raison par of_get_last_error() ; of_choose et of_prompt rendent "" et of_get_last_error le dit. Un test = 1 pour « confirmé » reste juste | |
messagebox : Échap ou Alt+F4 pendant qu'un bouton d'annulation compte encore à rebours (of_add_button_timed) | fermaient la boîte (0) | ne font rien jusqu'à la fin du délai : il est là pour faire lire | |
| messagebox : le texte du message et de l'instruction | ne se sélectionnait pas ; un appui dessus déplaçait la boîte | se sélectionne et se copie (Ctrl+C copie la sélection, ou toute la boîte sans sélection) ; la boîte se déplace par son titre, son icône, ses marges | |
messagebox : une saisie de plus d'environ 900 caractères (of_prompt, ib_input) | revenait vide, comme une annulation | revient entière | |
messagebox : al_hwnd = 0, une boîte ouverte depuis une autre, une boîte déplacée puis agrandie | le bureau entier était désactivé ; la fenêtre se réactivait pendant que la première boîte attendait ; la boîte revenait au centre | rien n'est désactivé sans propriétaire ; la fenêtre reste désactivée tant qu'une boîte l'attend ; la boîte garde sa place | |
progressbar : plage vide ou inversée (id_maximum = 0 puis id_value = 0) | 0 % quelle que soit la valeur | 100 % dès que la valeur atteint le maximum (un lot vide est terminé), 0 % avant | |
| progressbar : le pourcentage affiché, l'anneau | arrondi au plus proche (99,5 % affichait « 100% ») ; anneau figé à 128 px ; en attente, l'anneau tournait avec l'arc de la valeur (vide à 0 : rien ne bougeait) | arrondi vers le bas : 100 % veut dire terminé ; l'anneau suit la taille du contrôle ; en attente, un arc fixe tourne ; moins d'animations demandé par Windows : l'attente pulse | |
Toute propriété double relue (id_value…) sur un poste à virgule décimale | la partie décimale était mal lue (0,5 ne se relisait pas 0,5) | relue exactement, quel que soit le séparateur du poste | |
speechout : un nouvel of_speak (ou of_close) pendant une lecture | la lecture coupée ne levait aucun ue_stopped | toute lecture commencée finit sur un ue_stopped | |
speechout : il_timeout_ms = 0 | coupait la lecture synchrone au bout de 5 s | sans limite (24 heures au plus) | |
| speechout : une voix qui n'a pas pu naître (runtime WebView2 absent, page refusée) | of_speak, of_speak_from, of_pause, of_resume, of_enqueue, of_clear_queue rendaient -1, of_open le code brut de la DLL | -2 (composant non créé, comme partout), is_last_error dit pourquoi ; -4 si la page n'a pas répondu | |
speechout : of_speak_sync appelé depuis un event levé pendant un autre of_speak_sync | coupait la lecture de l'appel extérieur, qui rendait quand même 0 | refusé : -4, is_last_error = "already speaking synchronously" | |
speechout : of_speak_from au-delà de la dernière phrase | 0, rien n'était lu | -5 | |
speechout : of_is_speaking() juste après of_speak | false tant que la liste des voix n'était pas arrivée | true dès of_speak | |
speechout : of_pick_voice("", genre), ou liste des voix illisible | choisissait dans la langue de la page ; un échec vidait is_voice | "" = is_lang ; un échec garde is_voice et remplit is_last_error | |
speechout : of_voice_used, of_duration, of_spoken_text juste après avoir changé is_lang ou le dictionnaire | pouvaient répondre sur les réglages précédents | répondent sur les réglages posés à l'instant | |
| speechout : erreur de script dans la page de la voix | silencieuse | ue_error("script error: …") | |
statictext : il_back_color avec ii_corner_radius | le fond remplissait tout le rectangle, seul le trait s'arrondissait | le fond suit l'arrondi ; hors des coins, la couleur de l'objet sur lequel le bloc est posé, que le userobject prend aussi | |
statictext : is_marquee_direction en lecture de droite à gauche | sens inversé : MARQUEE_START défilait vers la fin, "left" vers la droite | MARQUEE_START défile vers le début de lecture ; "left" / "right" restent physiques | |
statictext : relire il_back_color, ii_marquee_speed, is_marquee_direction, ib_marquee_overflow_only, ib_marquee_pause_on_hover, is_font_family | -1, 0, "", false quoi qu'on ait posé ; une famille à espace entre guillemets | la valeur posée | |
statictext : ii_marquee_speed = 0 | gardait la vitesse précédente | vitesse par défaut (60) | |
statictext : of_reset après une bordure | couleur et épaisseur gardées : la bordure suivante en héritait | effacées | |
statictext : ib_track_mouse, composant désactivé pendant le survol | ue_mouse_leave jamais levé | ue_mouse_leave levé à la sortie | |
statictext : défilement (ib_marquee) | un [foldarea] ne se repliait plus après ; avec ib_marquee_overflow_only, un changement de HAUTEUR repartait du début ; avec ib_wrap = false, ue_text_overflow(true) à tort | corrigé : le texte n'est plus recopié, seule une vraie LARGEUR neuve se remesure, et le défilement montre tout le texte | |
statictext : ib_hover_effect et liens survolés sur un thème sombre | assombris (contraste réduit) | éclaircis | |
Texte riche : [symbol=clock] | affiché tel quel | un glyphe monochrome ; un nom inconnu reste du texte | |
statusbar : of_add_panel / of_insert_panel sur une clé prise ou qui contient / ou une barre verticale ; of_move_panel, of_remove_panel, of_clear_menu, of_add_menu_separator sur un panneau inconnu ; of_add_menu_item / of_insert_menu_item sous un panneau inconnu ou sur une entrée prise ; of_remove_menu_item, of_move_menu_item sur une adresse inconnue | 0, ignoré | -5 | |
statusbar : of_insert_panel(…, 0) | ajouté en fin de barre | en tête, comme toute position (0 ou moins = en premier) | |
statusbar : un séparateur (of_add_sep) dans of_count, of_keys_at et les positions de of_insert_panel / of_move_panel | compté comme un panneau à clé vide | ignoré : une position compte les panneaux | |
statusbar : un séparateur entre deux panneaux ALIGN_END | dessiné à la fin du groupe de début | entre les deux : il ouvre le groupe du panneau qui le suit | |
| statusbar : clic droit sur un panneau non cliquable | aucun événement | ue_panel_rclicked (un panneau désactivé reste muet) | |
statusbar : of_panel(k).ii_progress relu sur un panneau sans barre | 0, comme une barre à 0 % | -1 | |
statusbar : of_menu_item("k"), une adresse d'un seul niveau | écrivait sur le panneau k (ib_enabled le grisait) | ne désigne rien : n'écrit nulle part, se relit vide | |
statusbar : couleurs et info-bulle d'un panneau après of_clear, puis un panneau ajouté sous la même clé | repris par le nouveau panneau | oubliés : le panneau part d'une base propre | |
statusbar : is_state en thème sombre ; panneau désactivé | couleurs du thème clair, peu lisibles ; grisé par transparence, fond perdu au survol | une palette sombre lisible ; grisé par la couleur du thème, fond gardé | |
| statusbar : un panneau dont toutes les entrées de liste sont masquées | main et chevron, clic sans effet | un panneau ordinaire, cliquable ou non selon ib_clickable | |
| statusbar, toolbar : flèches Gauche / Droite en lecture de droite à gauche (RTL) | à contresens : Droite allait vers la gauche de l'écran | dans le sens de l'écran, comme l'onglet et le ruban | |
stepbar : poser ii_current | rien n'était levé | ue_step_changed quand la barre bouge, comme la doc le promettait | |
| stepbar : retirer, insérer ou déplacer une étape avant l'étape en cours | l'étape en cours changeait sans rien dire (c'était un rang) | la même étape reste en cours ; si c'est elle qu'on retire, la barre passe à la suivante atteignable et lève ue_step_changed | |
stepbar : ii_current posé avant les étapes ; of_clear_steps | ramené à la première étape ; l'étape en cours survivait au vidage | gardé et appliqué dès que l'étape existe ; le vidage repart sur la première étape | |
stepbar : of_add_step / of_insert_step sur une clé prise ou qui contient / ou une barre verticale ; of_move_step, of_remove_step sur une clé inconnue | 0, ignoré | -5 | |
stepbar : of_insert_step(…, 0) | ajouté en fin de barre | inséré en tête, comme toute position de la bibliothèque (of_add_step ajoute en fin) | |
| stepbar : flèches gauche / droite en écriture de droite à gauche | la flèche droite allait à l'étape suivante, dessinée à gauche | inversées : la flèche gauche va à l'étape suivante | |
tab : of_add_page / of_insert_page avec une clé qui contient / ou une barre verticale ; un envoi en échec | 0 ; la page restait hébergée et sa clé prise | -5 ; un envoi en échec rend son code, la page revient à sa fenêtre et la clé reste libre | |
tab : of_select_page d'une page masquée, désactivée ou au-delà du plafond de démonstration | 0, et la page s'affichait sous une bande sans onglet actif | -5, rien ne bouge | |
tab : croix, raccourci (is_shortcut), « Fermer tout / les autres / à droite » sur un onglet désactivé ou masqué | l'onglet s'activait ou se fermait, et PB libérait sa page | sans effet : un onglet qu'on ne peut ni voir ni atteindre ne s'active ni ne se ferme | |
| tab : l'onglet actif est fermé, retiré ou masqué | le suivant ou le premier prenait la main, même masqué ou désactivé ; as_from_key de ue_selection_changed était vide | le voisin sélectionnable prend la main (droite, sinon gauche) ; as_from_key nomme l'onglet réellement quitté, même disparu | |
| tab : plus aucun onglet visible (tous masqués, ou une disposition qui les masque) | la page de l'ancien onglet restait affichée, sans événement | la page est cachée et ue_selection_changed(ancien, "") est levé ; ré-afficher un onglet le sélectionne | |
tab : une poignée tenue après of_remove_page ou une fermeture ; son raccourci | restaient ; une page rouverte sous la même clé reprenait couleur, infobulle et ancien raccourci | libérés : IsValid() rend false, la page revient neuve | |
tab : of_set_layout avec un texte qui n'est pas du JSON | 0, et le message partait cassé | -5, rien n'est envoyé | |
| tab : clavier dans la bande d'onglets | Entrée perdait le focus ; les flèches s'arrêtaient sur les onglets désactivés ou débordés ; ni Haut/Bas en bande verticale ni inversion en écriture de droite à gauche | le focus reste sur l'onglet ; les flèches suivent l'axe de la bande et sautent ce qui ne répond pas ; Suppr ferme un onglet fermable | |
| tab : texte d'un onglet désactivé ; infobulle d'un onglet au titre tronqué | estompé en bloc (illisible au survol en office2007 sombre) ; le nom complet natif masquait votre infobulle | texte dans la couleur désactivée du thème, lisible ; votre infobulle passe devant | |
tilesbox : of_get_layout | rendait toujours "" : une disposition sauvegardée ne se restaurait jamais | la disposition entière, le même texte que ue_layout_changed ; elle garde aussi la taille et la visibilité de chaque tuile et la visibilité de chaque groupe | |
tilesbox : of_set_layout qui nomme une tuile sous un autre groupe que le sien ; un texte qui n'est pas du JSON | la première tuile de même clé, dans n'importe quel groupe, y était déplacée ; 0 | une tuile se retrouve par son groupe et sa clé : l'entrée est ignorée ; -5 sur un texte qui n'est pas du JSON | |
tilesbox : of_add_group sur une clé déjà prise ; of_add_tile / of_insert_tile sur une clé prise ou sous un groupe jamais ajouté ; une clé vide, avec / ou ` | ` | 0 ; un groupe repris était renommé, une tuile sous un groupe inconnu le créait sous sa clé | -5, rien n'est ajouté : ajoutez d'abord le groupe par of_add_group |
tilesbox : of_move_tile, of_remove_tile, of_add_live_item, of_clear_live_items sur une tuile inexistante ; of_remove_group sur un groupe inexistant | 0 | -5 | |
tilesbox : une poignée tenue après of_remove_tile, of_remove_group, of_clear, ou une tuile glissée dans un autre groupe | restait en mémoire ; une tuile recréée sous la même adresse reprenait couleurs et infobulle de l'ancienne, une tuile glissée les perdait | la poignée est libérée (redemandez-la par of_tile / of_group) ; couleurs et infobulle suivent la tuile glissée et partent avec ce qui est retiré | |
statusbar, stepbar, crosstab : une poignée tenue après of_remove_panel, of_remove_menu_item, of_clear_menu, of_clear (statusbar), of_remove_step, of_clear_steps (stepbar), of_remove_calc_field ou of_remove_calc_measure (crosstab) | restait en mémoire jusqu'à la destruction du composant | libérée : IsValid() rend false ; redemandez-la par of_panel, of_menu_item, of_step ou of_field | |
Tous les composants à items : relire il_back_color_hover / il_text_color_hover d'un item jamais coloré | 0, le noir | -1, « la couleur du thème », comme les trois autres couleurs d'item | |
tilesbox : relire ib_enabled / ib_visible d'un groupe, ou ii_badge_size | false, false, 0 quoi qu'on ait posé | la valeur en vigueur | |
tilesbox : groupe désactivé (ib_enabled = false) | son entête le repliait encore (clic, Entrée) et levait ue_group_toggled ; une tuile glissée y tombait | inerte : ni repli, ni événement, ni dépôt | |
tilesbox : couleurs d'item (il_back_color, il_text_color, il_accent…) posées sur un groupe | sans effet ; une couleur de groupe a pouvait peindre une tuile a d'un autre groupe | elles peignent l'entête du groupe (fond, titre, survol, filet d'accent), jamais ses tuiles | |
tilesbox : of_tile(""), ou une adresse à un ou trois niveaux | poser une propriété arrêtait l'application (« Array boundary exceeded ») ou écrivait sur le groupe | un handle inerte : il n'écrit rien et se lit vide | |
| tilesbox : clavier et lecteur d'écran | le focus se perdait à chaque mise à jour venue de PB (un badge) et le menu de taille se fermait ; menu de taille à la souris seulement ; en RTL, les flèches à contresens ; libellés lus avec leurs balises | focus et menu gardés ; menu de taille au clavier (flèches, Entrée, Échap) ; flèches dans le sens de lecture ; texte nu | |
toaster : il_max_visible | défaut 5, envoyé par CHAQUE toaster : un toaster qui avait posé 10 retombait à 5 dès qu'un autre affichait un toast | défaut 0 = rien n'est envoyé ; le plafond est COMMUN à l'application et la dernière valeur posée l'emporte (5 tant que personne ne l'a posé) | |
toaster : of_show d'un toast qui attend sa place | rendait 0 : le toast ne pouvait pas être fermé par of_close, et ses événements portaient un id jamais reçu ; file pleine (200) : 0 aussi, toast perdu sans un mot | un vrai identifiant, tout de suite ; of_close le retire de la file ; file pleine : -4 | |
toaster : deux toasts sans is_key qui portent un bouton de même clé | le second REMPLAÇAIT le premier (la clé du bouton était prise pour celle du toast) et of_show rendait l'id du premier | deux toasts, deux identifiants ; seul is_key met à jour en place | |
| toaster : événements de plusieurs toasters | rangés par FENÊTRE : deux toasters ancrés à la même fenêtre se volaient leurs événements, et ceux d'un toaster détruit étaient livrés au suivant | chaque toaster reçoit ceux de SES toasts ; un toaster détruit ne reçoit plus rien, ses toasts restent à l'écran, muets | |
toaster : pbm_custom02 de la fenêtre ipo_owner | la fenêtre d'ancrage recevait un pbm_custom02 à chaque événement de toast, même sans ipo_receiver | seule une fenêtre posée dans ipo_receiver le reçoit ; si vous aviez mappé pbm_custom02 de votre fenêtre pour les toasts, posez ipo_receiver, ou mieux, retirez ce code : les événements arrivent seuls | |
| toaster : affichage | un toast restait sur place quand sa fenêtre bougeait ; un toast de plus de 800 px (une longue trace d'erreur) n'apparaissait JAMAIS ; largeur non mise à l'échelle (180 px à 200 %) ; mettre à jour un toast survolé relançait toute la pile | il suit sa fenêtre ; le texte défile dans le toast, plafonné ; largeur à l'échelle de l'écran ; la pile reste en pause sous la souris | |
toolbar : ajouts, of_add_separator, of_move_item, of_remove_* sur une clé prise, avec / ou ` | `, une barre inconnue ou une adresse inconnue | 0, ignoré — une faute de frappe dans le nom d'une barre créait une barre fantôme | -5 ; seule main n'a pas besoin d'of_add_bar |
toolbar : of_clear, of_remove_bar, of_remove_item | les poignées restaient, les raccourcis restaient armés, et un outil recréé sous la même clé retrouvait les couleurs de l'ancien | poignées libérées, raccourcis désarmés, couleurs et info-bulles oubliées ; redemandez une poignée après un retrait | |
toolbar : of_item d'une adresse à trois niveaux (une entrée de menu) | agissait sur l'OUTIL de la barre qui portait le même nom que l'entrée | ne désigne rien ; une entrée se pilote par of_menu_item(adresse) | |
| toolbar : entrée choisie dans un déroulant replié dans le chevron | ue_menu_selected sans le parent de la cascade, sous un autre déroulant, ou vide | l'adresse complète, comme dans la barre | |
toolbar : ii_index d'une barre | ne déplaçait jamais une barre vers la droite | la barre prend le rang demandé | |
toolbar : of_add_bar(clé, bande, rang) sur un rang déjà pris, ou une bande au-delà des autres | ordre indéfini ; la bande se relisait telle quelle (5 pour la deuxième rangée) | elle s'insère devant la barre qui tenait le rang ; les bandes se resserrent | |
toolbar : of_count, of_keys_at d'une barre, rang d'of_move_item | comptaient les séparateurs, sous une clé vide | les outils seulement : un séparateur n'a ni clé ni rang | |
| toolbar : Entrée dans une zone de texte | validait le champ ET pressait le bouton par défaut de la fenêtre | valide le champ seulement ; Échap lui rend son texte validé | |
toolbar : is_shortcut relu ; ii_band / ii_index d'une barre inconnue | ctrl+s (normalisé) ; 1 | Ctrl+S, tel qu'écrit ; 0 | |
toolbar : couleur ou info-bulle posée sur une BARRE (of_bar) | sans effet, ou peignait l'outil qui portait le même nom | appliquée à la barre, et à elle seule | |
| toolbar : raccourci d'un outil masqué ou au-delà du plafond de démonstration | avalait la touche | la touche va à l'outil suivant qui porte le même accord | |
webbrowser : une page qui ne se charge pas (hôte inconnu, réseau, certificat, of_stop) | levait ue_load_completed, comme un succès | lève ue_load_failed(as_url, al_status) à la place | |
webbrowser : des mots dans is_address (« facture 2026 ») | partaient en https://facture 2026 et plus aucune navigation ne se faisait ensuite | refusés avec ue_error, la suite se charge ; recherche si is_search_url est posé | |
webbrowser : un chemin disque (C:\temp\x.html) ou //hôte/x dans is_address | précédé de https://, donc introuvable | file:///C:/temp/x.html ; https://hôte/x ; javascript: refusé | |
webbrowser : is_address relu après un lien suivi ou Précédent | la dernière adresse posée | la page affichée | |
webbrowser : of_go_back, of_go_forward, of_stop juste après is_address | pouvaient passer AVANT l'adresse ; of_stop laissait repartir les pages en attente | jouent dans l'ordre écrit ; of_stop abandonne aussi les pages en attente | |
webbrowser : of_save_as_png, of_save_as_jpg, of_print_to_pdf, of_print | capturaient ou imprimaient la barre d'adresse | le site affiché | |
webbrowser : lien target=_blank, window.open après un clic | rien ne se passait | la page s'ouvre dans la vue, ue_new_window le dit | |
webbrowser : réponse de of_execute_javascript de plus de 4 095 caractères | coupée sans le dire | entière | |
| webbrowser : F5, Ctrl+R, Ctrl+molette dans la barre d'adresse | rechargeaient ou zoomaient la barre elle-même | sans effet dans la barre ; actifs dans le site | |
webbrowser : clic droit dans un champ de saisie avec ib_context_menu | Précédent / Suivant / Actualiser | Couper / Copier / Coller | |
webbrowser : Tab ou of_focus_webview sans barre d'adresse ; fichier déposé sur le site avec ib_allow_drop | focus perdu dans une barre de 0 px ; fichier ouvert par le navigateur | le focus va au site, Tab en ressort vers votre fenêtre ; ue_drop_files | |
pdfviewer : is_source relu, et as_source de ue_load_completed | l'adresse interne /_file/?p=… | ce que vous avez écrit (doc\notice.pdf) | |
| pdfviewer : fichier absent, pas un PDF, site injoignable ou qui refuse le cadre | ue_load_completed sur une page d'erreur, ou aucun événement | ue_load_failed(as_source, as_reason) (constantes REASON_*), la visionneuse reste vide | |
pdfviewer : http://, data: d'un autre type que PDF, page web en https:// | affichés (ou cadre vide pour http://) | refusés : ue_load_failed avec REASON_INSECURE ou REASON_NOT_PDF | |
pdfviewer : un # dans is_source | coupait le chemin (Devis #12.pdf introuvable) ; le fragment d'une URL se mêlait à ii_page / ii_zoom | un # fait partie du nom du fichier ; le fragment d'une URL est ignoré, page et zoom passent par les propriétés | |
pdfviewer : ii_page posé avant is_source | perdu : le document s'ouvrait à la page 1 | gardé pour ce document ; sans page posée depuis le dernier is_source, un nouveau document s'ouvre à la page 1 | |
pdfviewer : PDF régénéré au même chemin puis of_refresh ; PDF de 32 Mo et plus ; chemin de plus de 260 caractères | l'ancienne version pendant 5 minutes ; rien ; introuvable | le fichier tel qu'il est sur le disque ; affiché (servi par morceaux) ; affiché | |
pdfviewer : lien suivi dans le document ; of_print | le site remplaçait le document, sans retour ; imprimait la page qui encadre le document | le document reste, ue_link_clicked(as_url) vous donne l'adresse ; of_print imprime le document (-4 sans document) | |
picture, button, tilesbox : il_badge_color | 0 = la couleur du thème ; le noir était inatteignable ; non posée, se relisait 0 | -1 = non posée (la couleur du thème), comme toute autre couleur ; 0 est le noir. Remplacez il_badge_color = 0 par -1 | |
picture : id_zoom posé avant ib_zoomable | se relisait à la valeur posée, l'image restait au cadrage | ignoré : id_zoom reste 1.0 tant que ib_zoomable est faux ; posez ib_zoomable d'abord | |
picture : ib_mirror sur une image tournée d'un quart de tour ; is_align / is_valign avec une rotation | retournement vertical à l'écran ; le placement tournait avec l'image | miroir et placement se lisent à l'écran, quelle que soit la rotation | |
| picture : glisser l'image zoomée ; molette ; image désactivée (statictext désactivé aussi) | ue_clicked à chaque déplacement ; zoom vers le centre, l'image pouvait sortir du cadre ; les dépôts de fichiers restaient acceptés | aucun clic après un déplacement ; zoom vers le pointeur, l'image couvre toujours son cadre ; désactivée, elle refuse tout dépôt ; ue_error nomme la source | |
xml : une lecture (of_get_value, of_count, of_is_valid…) ou une modification après une construction | agissait sur le dernier document CHARGÉ, ou sur rien | le document bâti est chargé et devient LE document ; le of_start_element suivant en commence un nouveau | |
xml : of_load en échec (vide, mal formé), of_start_element après un chargement | l'ancien document restait lisible et modifiable (of_is_valid vrai) ; un document à moitié bâti restait dans of_xml | rien ne reste | |
xml : of_xml, of_pretty, of_get_values d'un gros document | coupé sans le dire au-delà de quelques Mo | entier | |
xml : of_get_value d'un XPath de valeur (count(//line), sum(//@qty)) | vide | la valeur en texte (3, true) | |
| xml : XPath dont le préfixe est déclaré plus bas que la racine (réponse SOAP) | vide sans of_register_namespace | résolu | |
xml : is_last_error après un XPath invalide, une page qui ne répond pas, un refus ; codes de of_register_namespace / of_clear_namespaces | vide, ou « no node matched » quelle que soit la cause ; les modifications rendaient -4 quand la page manquait ; 0 toujours ; une page sans réponse rendait le texte null | la vraie raison ; -2 quand la page ne répond pas ; une URI vide est refusée (-5) ; vide plutôt que null | |
xml : of_pretty | perdait CDATA, commentaires, instructions de traitement et déclaration XML, et remaniait le texte mixte | tout est gardé ; un élément qui contient du texte reste sur une ligne | |
| xml : construction — saut de ligne ou tabulation dans un attribut, CR dans un texte, caractère de contrôle, nom invalide | relus altérés ; document mal formé ; nom écrit tel quel | relus identiques ; caractère de contrôle retiré ; nom refusé (is_last_error), l'élément laissé de côté | |
xml : un document qui contient un élément <parsererror> ; message d'un XML mal formé | refusé comme mal formé ; message encadré de « This page contains the following errors » | chargé ; la ligne d'erreur seule | |
xml : of_remove de nœuds imbriqués, de la racine, d'un attribut ; of_set_value("/") ; of_add_child sous un attribut | comptait deux fois ; vidait le document ; l'attribut restait ; 0 sans effet ; « parent not found » | le compte réel ; la racine reste (0 et is_last_error) ; l'attribut est retiré ; -4 ; « the node cannot have children » | |
xml : of_close puis une lecture ; ipo_owner posé après la première lecture | le document chargé était perdu ; ignoré jusqu'à of_close | le document est gardé et rechargé ; transmis à la lecture suivante | |
thèmes metro-light et metro-dark : texte sur l'accent | blanc sur le bleu #25a0da dans les deux thèmes (en-têtes de volet, onglet actif, bouton de l'application du ruban, tuiles) : peu lisible | metro-light : l'accent devient le bleu plus profond #1b76a1, texte toujours blanc ; metro-dark : l'accent reste #25a0da, le texte posé dessus devient presque noir (#1b1b1b) | |
thèmes office-dark, office2003-dark, office2003-light, office2007-light : couleurs d'accent et d'en-tête | en mode sombre, l'accent se lisait mal sur la page (puce d'étape, pastille), et le texte blanc sur le survol de l'accent et les en-têtes de colonnes du crosstab ; en mode clair, le haut des dégradés du bouton de l'application et des en-têtes de volet était trop pâle pour le texte blanc | office-dark : accent #1178d1, survol #0f6cbd ; office2003-dark : accent #4574cb, survol #2f5aa8 ; en-têtes du crosstab plus foncés au survol et sur les totaux ; office2003-light et office2007-light : haut du dégradé plus profond (#3e73cf, #4275c5), en-têtes de volet d'office2007-light plus opaques. Texte toujours blanc, au moins 4,5:1 | |
texte posé sur une couleur choisie par l'application (il_accent, couleur d'un item, en-tête de volet, pastille) | blanc jusqu'à une couleur assez claire : un bleu moyen comme #25a0da recevait du blanc à 2,95:1 | blanc ou presque noir (#1b1b1b), celui des deux qui se lit le mieux (rapport de contraste WCAG) : #25a0da reçoit désormais du texte sombre | |
jsontree : of_clear | vidait l'arbre ET remettait ib_wrap et ib_search_enabled à leurs défauts | vide l'arbre seulement ; les réglages restent (c'est of_reset qui remet tout) | |
jsontree : is_json = "" | levait ue_error « Invalid JSON » | vide l'arbre sans erreur, comme la doc le promettait | |
jsontree : nombres et clés affichés, is_json relu | réécrits par le moteur : 12345678901234567890 affiché 12345678901234567000, 1.10 → 1.1, la première de deux clés identiques perdue ; is_json se relisait compacté, et vide après un JSON invalide | tels que reçus, les deux clés comprises ; is_json se relit tel qu'il a été posé | |
jsontree : of_match_count, al_count de ue_search_result | comptaient les LIGNES ; deux occurrences sur une ligne étaient toutes deux « courantes » | comptent les OCCURRENCES, une seule est courante et of_search_next passe de l'une à l'autre | |
| jsontree : blocs ouverts par une recherche | TOUS ceux qui contenaient une correspondance, pour de bon : chercher « e » ouvrait tout le document | seulement ceux qui cachent l'occurrence COURANTE ; of_clear_search rend les plis d'avant la recherche | |
jsontree : of_select_path | une ligne cachée dans un bloc replié restait invisible ; un chemin inconnu rendait 0 | les blocs qui la cachent s'ouvrent ; un chemin inconnu rend -5 et la sélection ne bouge pas | |
| jsontree : replier le bloc qui contient la sélection | la sélection devenait invisible, et la flèche suivante repartait du haut | la sélection passe sur le bloc replié ; au geste de l'utilisateur, ue_selection_changed le dit | |
xmltree : of_clear | vidait l'arbre ET remettait ib_wrap et ib_search_enabled à leurs défauts | vide l'arbre seulement ; les réglages restent (c'est of_reset qui remet tout) | |
xmltree : is_xml = "" | levait ue_error « Invalid XML » et affichait une erreur rouge | vide l'arbre sans erreur, comme la doc le promettait | |
xmltree : is_xml relu | re-sérialisé par le moteur : guillemets changés, retours à la ligne perdus, vide après un XML invalide | se relit tel qu'il a été posé, un texte invalide compris | |
| xmltree : ce qui s'affiche | un CDATA était invisible (l'élément s'affichait vide), commentaires, instructions de traitement et déclaration absents, le texte d'un contenu mixte recollé avant les enfants, un texte rogné de ses blancs, une valeur d'attribut à guillemet affichée telle quelle | chaque nœud à sa place, dans l'ordre du document ; la ligne reste du XML (guillemet choisi ou ", , &, <) ; les blancs significatifs restent | |
xmltree : of_match_count, al_count de ue_search_result | comptaient les LIGNES ; une correspondance à cheval sur deux morceaux (id="4152") était comptée sans être surlignée | comptent les OCCURRENCES, chacune surlignée, une seule est courante | |
| xmltree : éléments ouverts par une recherche | TOUS ceux qui contenaient une correspondance, pour de bon | seulement ceux qui cachent l'occurrence COURANTE ; of_clear_search rend les plis d'avant la recherche | |
xmltree : of_select_path | une ligne cachée dans un élément replié restait invisible ; un chemin inconnu rendait 0 | les éléments qui la cachent s'ouvrent ; un chemin inconnu rend -5 et la sélection ne bouge pas | |
xmltree : ue_selection_changed | levé aussi sans changement (Fin sur la dernière ligne) ; replier l'élément qui contenait la sélection la rendait invisible, sans rien dire | levé seulement quand la sélection change ; au pli, la sélection passe sur l'élément replié et l'événement le dit | |
xmltree : ue_error | le texte brut de la page d'erreur du moteur ; un élément <parsererror> du document passait pour une erreur | la seule ligne de l'erreur (« error on line N at column M: … ») ; un <parsererror> du document est un élément comme un autre | |
| restclient : cookies d'une requête synchrone | aucun gardé : chaque of_get / of_post ouvrait sa propre session ; ib_keep_cookies ne valait que pour l'asynchrone | une session par objet, commune au synchrone et à l'asynchrone : une connexion synchrone vous garde connecté pour les appels suivants | |
restclient : ib_keep_cookies changé après la première requête | sans effet jusqu'à of_close | appliqué tout de suite ; à false, les cookies déjà gardés sont oubliés | |
| restclient : redirection vers un autre hôte ou port | seuls Authorization, Cookie et l'authentification Basic tombaient : une clé d'API (X-Api-Key…) partait chez le tiers | aucun en-tête posé par of_set_header ne suit, seul Content-Type reste | |
restclient : of_set_bearer("") après of_set_basic ; of_set_basic puis of_set_bearer ; of_set_basic("", "") | le mot de passe Basic continuait de partir ; deux Authorization à la fois ; Basic Og== envoyé | une seule authentification à la fois : chacune remplace l'autre, un jeton ou un utilisateur vide la retire | |
restclient : HEAD, ou réponse 204 / 304 qui annonce une taille | -4 « connection lost », et rejouée en asynchrone | le statut (200, 304…) | |
restclient : corps avec un octet invalide ; charset=iso-8859-1 ou utf-16 | tout le corps relu en Windows-1252 ; charset ignoré | l'octet invalide seul devient un caractère de remplacement ; le charset annoncé est lu | |
| restclient : corps en texte de plusieurs centaines de Mo | gardé trois fois en mémoire : un processus 32 bits pouvait mourir | -4 « body too large : use of_download » au-delà de 64 Mo | |
| restclient : cent requêtes asynchrones lancées d'un coup | cent fils en parallèle | six au plus en même temps, les suivantes attendent leur tour | |
| restclient : une requête asynchrone envoyée plus souvent que toutes les 50 ms (un timer de télémétrie) | aucune réponse livrée tant que le flux durait | les réponses arrivent au fil de l'eau | |
restclient : of_cancel d'un id inconnu ; d'une requête dont la réponse est arrivée | -1 ; 0 (mais c'est ue_response qui arrivait) | -5 ; -4, trop tard | |
restclient : of_response_text() après of_download ; après un of_download(url, "") | le nombre d'octets écrits ; la réponse d'avant | vide : ce corps-là est dans le fichier ; vide | |
restclient : lire une réponse asynchrone pendant ue_progress | une mémoire en cours d'écriture : plantage possible | vide, of_status(id) = 0 tant que la requête court | |
video : changer de film (is_source, of_play(source)) | le segment, les sous-titres, les marqueurs et les chapitres de l'ancien film restaient | ils partent avec lui (posés avant tout film, ils attendent le premier) ; un film de la liste de lecture garde ses sous-titres. Posez-les après is_source | |
video : of_go_chapter, of_next_chapter, of_previous_chapter | levaient ue_seeked ; of_go_chapter au-delà du nombre de chapitres rendait 0 sans rien faire | ue_seeked et ue_chapter_changed, comme un clic sur la barre ; -5 | |
video : of_add_source alors qu'un is_source est posé | la liste ne démarrait jamais, of_next allait au premier film | la première entrée se charge tout de suite (sauf si le film posé JOUE : la liste le suit à sa fin) | |
video : ue_started après of_stop ou la fin du film, puis of_play | jamais : une fois par source | à chaque nouvelle lecture (pas à la reprise d'une pause) | |
video : of_play sur un film en erreur ; of_reset ; ii_rate au film suivant | « en cours » pour toujours, sans événement ; l'ancien film restait chargé (of_duration, of_capture) ; retour à la vitesse normale | ue_failed de nouveau ; le film est déchargé ; la vitesse tient | |
video : of_capture vers un fichier impossible à écrire ; vers « \x » ou « C:x » | -4 (la doc disait -5) ; écrits selon le dossier courant | -4, et aucun fichier tronqué ne reste ; -5 : donnez un chemin complet | |
| video : plein écran puis Alt+Tab vers une autre application | le film restait au-dessus de tout | l'autre application passe devant ; revenir à la vôtre ramène le film au-dessus de tout | |
| pdfviewer : un clic simple sur un lien du PDF | rien : seul Ctrl+clic levait ue_link_clicked | ue_link_clicked(url), le document reste affiché | |
webbrowser : une page data: (is_address = "data:text/html,…") | affichée, mais aucun ue_load_completed | ue_load_completed avec l'adresse demandée | |
webbrowser : une nouvelle fenêtre demandée par le site vers file:, data: ou mailto: | ouverte dans la vue, ue_new_window levé | refusée : ue_error, pas de ue_new_window ; seuls http et https s'ouvrent | |
| webbrowser : un site qui demande la caméra, le micro, la position, les notifications | invite du navigateur à l'utilisateur, réponse retenue | refusé sauf si ue_permission_requested renvoie true ; rien n'est retenu | |
| webbrowser : cookies, sessions et stockage des sites | dans le profil commun des composants | dans un profil de navigation à part : une session ouverte en 3.0 n'est pas reprise (se reconnecter une fois) ; of_clear_browsing_data l'efface | |
webbrowser : is_search_url sans %s, sans http(s):// ou en javascript: | accepté : chaque recherche échouait ou perdait le texte tapé | refusé : ue_error, le moteur en place reste | |
webbrowser : gestion/ ou [::1]/x dans is_address | « pas une adresse » (ou une recherche) | https://gestion/, https://[::1]/x ; le mot seul reste un texte | |
webbrowser : clic droit sur une sélection, un lien, une image avec ib_context_menu | Précédent / Suivant / Actualiser seulement | en plus : Copier, Ouvrir le lien, Copier l'adresse du lien, Copier l'image | |
tab : onglet fermable désactivé (ib_enabled = false) | la croix disparaissait mais sa place restait vide | ni croix ni place vide : l'onglet a la largeur d'un onglet non fermable | |
video : of_chapters sur un titre qui contient ` | ` ou une tabulation | coupé en deux chapitres | lu tel quel |
video : un .srt en Windows-1252 ou en UTF-16 | accents illisibles ; aucun sous-titre | lu correctement | |
commandpalette : of_open sans runtime WebView2 ; toaster : of_show sans runtime | -1 | -6, comme la boîte de message | |
crypto, xml, restclient : of_open quand la page ou le client ne peut pas naître | le code brut de la DLL (-1, -3…) | -2, et is_last_error dit pourquoi |
Si vous aviez contourné l'un de ces défauts — par exemple en traitant ue_menu_selected avec une adresse vide pour rattraper les clics d'un groupe replié —, retirez le contournement : l'événement documenté arrive désormais.
11.4 Le générateur n_pbt_menu2ribbon #
En 3.0, le code écrit par n_pbt_menu2ribbon employait des méthodes de poignée qui n'existaient plus : il ne compilait pas sans retouches. En 4.0 il écrit l'API actuelle — chaque ajout par son adresse depuis le composant —, reprend les sous-menus à toute profondeur, reporte les items grisés et cochés, et son routeur couvre ue_clicked, ue_toggled et ue_menu_selected. Les clés changent : chacune est désormais le ClassName de l'item de menu (m_fichier/g1/m_ouvrir) au lieu du chemin pointé (m_principal.m_fichier.m_ouvrir).
La recette : un ruban déjà construit à la main, ou à partir d'un code généré puis corrigé, n'a rien à changer. Pour repartir du générateur, relancez-le et collez aussi le nouveau routeur : l'ancien lisait des clés pointées.
11.5 Ce qui a été ajouté #
Rien de ce qui suit ne demande de modifier votre code. Les nouveaux composants de la 4.0 sont présentés dans les notes de version.
| Objet | Ajouté |
|---|---|
| ribbon | of_add_menu_separator accepte l'adresse d'une entrée : la ligne va dans la cascade placée sous elle |
| ribbon | of_add_menu_item et of_add_menu_check acceptent une cascade de toute profondeur, et chaque niveau se pilote par of_menu_item(adresse) |
| menubar | of_add_separator accepte l'adresse d'une entrée (file/export) : la ligne va dans sa cascade ; of_menu(...) porte les couleurs (il_accent, il_back_color…) et l'info-bulle du titre |
| crosstab | of_set_label_filter(as_field, as_type, as_a, as_b) en texte (LABEL_CONTAINS, LABEL_BEGINS, LABEL_ENDS) et les constantes LABEL_GT, LABEL_LT, LABEL_BETWEEN : la forme à deux double ne pouvait passer qu'un nombre |
| crypto | KEYFORMAT_TEXT, KEYFORMAT_HEX, KEYFORMAT_BASE64 et une surcharge d'of_hmac, d'of_jwt_sign et d'of_jwt_verify qui les prend (une clé en OCTETS : secret fourni encodé, AWS SigV4) ; une entropie facultative pour of_protect et of_unprotect |
| crypto | of_hmac_verify (un HMAC reçu — webhook — comparé en temps constant) ; of_jwt_header et une surcharge d'of_jwt_sign qui ajoute des membres à l'en-tête (kid) ; of_totp_verify(…, ref al_step), le pas accepté, pour refuser un code déjà servi ; il_password_hash_rounds |
| messagebox | of_get_last_error() : pourquoi la dernière boîte n'a pas pu s'ouvrir (chaîne vide si elle s'est ouverte) |
| n_pbt_utils | of_dir_has_subfolders(as_dir, ab_include_hidden) : la même question en comptant aussi les dossiers cachés, système et les jonctions |
| n_pbt_utils | of_dw_date_order() : l'ordre dans lequel un DataWindow écrit une date sur ce poste (dmy, mdy, ymd) |
| speechout | of_is_paused() : vrai tant que la lecture est en pause |
| Texte riche | la balise [symbol=nom] : dix-huit glyphes monochromes qui suivent la couleur du texte (chapitre 4) |
| toolbar | of_menu_item(adresse) → n_pbt_toolbar_menu_item : chaque entrée de menu déroulant a son état (ib_enabled, ib_visible, ib_checked, is_text, is_shortcut), désignée par son adresse complète |
| toolbar | is_date sur un sélecteur de date ; of_clear_menu ; of_remove_item d'une entrée de menu ; of_add_menu_separator sur une entrée (cascade) ; cascades de toute profondeur |
| pdfviewer | ue_load_failed(as_source, as_reason) et les constantes REASON_* ; ue_link_clicked(as_url) ; les adresses file:/// ; ib_allow_save et ib_allow_print : les commandes Enregistrer et Imprimer de la barre du lecteur |
| picture | is_alt_text (texte de remplacement lu par un lecteur d'écran) ; un clavier pour le zoom : +, -, 0, et les flèches pour se déplacer ; n_pbt_utils.of_json_get_str_array |
| webbrowser | is_search_url (moteur de recherche des mots qui ne sont pas une adresse) ; ib_veto_new_window et ue_new_window ; ib_veto_downloads et ue_download_starting ; ue_load_failed ; ue_error ; of_show_html ; of_clear_browsing_data et ib_private ; ib_veto_navigation et ue_navigating ; is_title et ue_title_changed ; ue_permission_requested et les constantes PERMISSION_* ; les constantes LOADSTATUS_* de ue_load_failed ; F5, Ctrl+R, Alt+flèches et Échap dans la barre d'adresse |
| jsontree | of_selected_text rend la valeur DÉCODÉE de la feuille sélectionnée (une chaîne sans guillemets ni échappements) ; of_has_selection distingue la racine, dont le chemin est "", de « rien de sélectionné » ; le clavier saute les accolades fermantes (elles ne se sélectionnent plus) |
| xmltree | chaque attribut est une cible : un clic donne son chemin (/order/@id), of_select_path le sélectionne, of_selected_value rend sa valeur ; un gros document ne dessine que les lignes à l'écran (8 000 éléments frères : 12 s → 0,15 s, une flèche en quelques ms) |
| video | ue_muted_changed(ab_on) : l'utilisateur a coupé ou rendu le son (le haut-parleur de la barre, la touche M), comme le volume et la vitesse avaient déjà leur événement |
| ribbon | of_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 : les retraits sous un contrôle — une liste « Fichiers récents » se met à jour sans recréer le contrôle |
| ribbon | of_app_menu_item(adresse) : la poignée d'une entrée du menu applicatif (is_label, is_image, ib_enabled, ib_checked, ib_visible) ; of_app_menu_item("") parcourt le menu |
| ribbon | touches d'accès rapide de plusieurs lettres (is_keytip = "FP"), tapées l'une après l'autre ; noms accessibles (barre d'accès rapide, vignettes) pour les lecteurs d'écran |
| toolbar | of_add_split_button : un bouton partagé (action + menu) ; is_group : des bascules exclusives (alignement) ; is_image sur un outil et sur une entrée de menu ; ue_item_rclicked(adresse) : le clic droit d'un outil |
11.6 Des noms et des comptes alignés sur le reste de la bibliothèque #
Sept endroits parlaient autrement que le reste de la bibliothèque. La 4.0 les aligne plutôt que de garder l'écart : deux noms changent — le compilateur vous montre chaque ligne à reprendre —, deux comptes partent désormais de 1, comme toute position du produit, le chemin rendu par l'arbre JSON devient celui de n_pbt_json, celui de l'arbre XML sous un espace de noms par défaut un XPath qui marche, et un événement ambiant devient facultatif. Ce sont les seuls changements de la 4.0 qui demandent de retoucher du code qui compilait.
| Quoi | 3.0 | 4.0 |
|---|---|---|
| progressbar : bornes de l'échelle | il_minimum, il_maximum, des long | id_minimum, id_maximum, des double : une échelle 0..1 ou un nombre d'octets au-delà de 2 Go est valide ; le préfixe dit le type |
| json : index de tableau dans un chemin | à partir de 0 : lines/0 est la première ligne | à partir de 1 : lines/1 est la première ligne ; lines/0 ne désigne rien — une lecture le dit absent, une écriture rend -5 |
jsontree : chemin d'un nœud (ue_node_clicked, ue_selection_changed, of_selected_path, of_select_path) | niveaux joints par un point, index de tableau à partir de 0 : lines.0.sku — un dialecte que n_pbt_json ne lisait pas | le chemin de n_pbt_json : niveaux joints par /, index à partir de 1 (lines/1/sku), "" pour la racine — à passer tel quel à of_get_string sur le même texte |
xmltree : chemin d'un élément sous un espace de noms PAR DÉFAUT (xmlns="urn:…") | /Envelope/Body — un XPath qui ne trouve rien | /*[local-name()='Envelope']/*[local-name()='Body'] — à passer tel quel à n_pbt_xml.of_get_value ; un élément sans espace de noms ou préfixé garde son chemin |
toolbar : band et index de la disposition (of_get_layout, of_set_layout, ue_layout_changed) | à partir de 0, une chaîne dite « opaque » alors que ii_band, ii_index et ue_bar_reordered comptaient à partir de 1 | à partir de 1, comme ii_band, ii_index et ue_bar_reordered ; un aller-retour lecture → restauration rend la même chaîne |
statictext : ue_cycle | levé à chaque boucle du défilement, sans abonnement — et aussi par une animation à l'intérieur du texte | événement ambiant, donc facultatif : levé seulement quand ib_track_cycle = true, comme ue_mouse_enter avec ib_track_mouse ; seulement à chaque passage complet du texte |
tilesbox : 2ᵉ argument de ue_tile_moved | as_to_keys, alors qu'il porte UNE clé : le groupe d'arrivée | as_to_key : le nom dit ce qu'il porte, comme partout ailleurs (as_key = un niveau, as_keys = une adresse) |
restclient : ue_failed | ue_failed (long al_id, string as_error) | ue_failed (long al_id, long al_code, string as_error) : -4 échec, -5 URL ou fichier — plus besoin de lire le texte |
restclient : of_json_value | une clé, première occurrence à n'importe quelle profondeur ; une valeur texte seulement — un nombre rendait le nom de la clé suivante | un chemin n_pbt_json ("json/customer", "items/1/qty"), toute valeur : "4152", "true". Une clé imbriquée se nomme avec ses parents |
La recette : (1) recompilez — le compilateur signale il_minimum, il_maximum et as_to_keys : renommez-les id_minimum, id_maximum et as_to_key ; (2) cherchez les chemins json qui visent un tableau et ajoutez 1 à chaque index — une boucle for i = 0 to n - 1 sur "lines/" + String(i) devient for i = 1 to n ; (3) une disposition de toolbar enregistrée par la 3.0 compte à partir de 0 : ajoutez 1 à band et à index de chaque barre avant de la rendre à of_set_layout, ou laissez l'utilisateur réorganiser une fois — relue telle quelle, elle rangerait ses deux premières bandes ensemble ; (4) là où ue_cycle est scripté, posez ib_track_cycle = true à la construction ; (5) là où vous lisiez un chemin de l'arbre JSON (ue_node_clicked, of_selected_path) ou en donniez un (of_select_path), remplacez les points par / et ajoutez 1 à chaque index de tableau — lines.0.sku devient lines/1/sku, qui se donne tel quel à n_pbt_json.of_get_string.
11.7 Les thèmes suivent l'application #
La revue des thèmes a fait tenir à chaque composant la règle qu'annonçait déjà le chapitre 4 : l'application décide du thème et de l'accent, un composant ne s'en écarte que pour ce qu'on lui demande. Aucune méthode n'est renommée ; ce qui change se voit à l'écran, à la relecture des propriétés et dans deux codes retour.
| Quoi | 3.0 | 4.0 |
|---|---|---|
PBT_SetDefaultTheme | changer de thème effaçait l'accent posé par PBT_SetDefaultThemeAccent — même en reposant le même thème — et aplatissait le fond dégradé d'office2007 et d'office2003 | l'accent de l'application reste d'un thème à l'autre ; chaque thème garde son fond |
PBT_GetDefaultThemeAccent | l'accent du thème quand l'application n'en avait posé aucun | -1 tant que l'application n'a pas posé d'accent |
| codes retour natifs | PBT_SetDefaultTheme d'un nom inconnu rendait 0 et laissait les composants suivants sans thème ; PBT_SetDefaultThemeAccent d'une couleur système PowerBuilder la peignait en noir | -5 dans les deux cas ; le thème ou l'accent précédent reste |
| un axe de thème posé seul | figeait l'autre axe sur le thème de l'application du moment | l'axe vide suit le thème de l'application à chacun de ses changements ; les deux axes vides = le thème de l'application, entièrement |
is_theme_style · is_theme_mode · il_theme_accent | se relisaient "", "" et -1, quoi qu'on ait posé | le style et le mode affichés, et l'accent propre du composant (-1 s'il n'en a pas) |
une valeur d'axe inconnue ("office2010") ou mal écrite (" Metro ") | le composant perdait son thème et n'écoutait plus celui de l'application jusqu'au of_reset() | espaces et majuscules ignorés ; une valeur inconnue est ignorée et l'axe garde sa valeur |
| accent d'un composant à thème local | il gardait l'accent de son thème, même quand l'application en posait un | il suit l'accent de l'application tant qu'il n'a pas le sien |
| un accent personnalisé | seul l'accent changeait : le survol et l'appui des boutons à l'accent, les sélections teintées, les boutons cochés du ruban gardaient l'accent du thème | tout ce qui dérive de l'accent le suit |
tilesbox : il_badge_color | 0 et -1 = la couleur du thème ; relu 0 sans couleur posée ; le noir était inatteignable | -1 = la couleur du thème (relu -1) ; 0 est le noir, comme pour toutes les couleurs |
une couleur système PowerBuilder (ButtonFace…) dans il_theme_accent | peinte en noir | comptée comme -1 : l'accent de l'application |
webbrowser · pdfviewer | le contenu tiers suivait le mode clair/sombre de Windows, et un rectangle blanc précédait son premier affichage | il suit le thème par défaut de l'application, sur le fond du thème (4.11) |
| accent d'office-dark et d'office2003-dark | un bleu foncé, lu à 3:1 quand il colorait un texte (liens, [accent]) | un bleu plus clair (#479ef5, #7ea1e8), avec un texte sombre posé dessus |
datagrid : filets entre les lignes | absents dans huit thèmes sur dix | présents dans les dix thèmes, à la couleur de la grille du thème |
| contraste élevé de Windows | les icônes monochromes et les anneaux dessinés en ombre disparaissaient | pris en charge (4.10) |
La recette : (1) si vous reposiez l'accent après chaque PBT_SetDefaultTheme, ce n'est plus nécessaire — et si vous comptiez sur un changement de thème pour effacer l'accent, appelez PBT_SetDefaultThemeAccent(-1) ; (2) testez le code retour de PBT_SetDefaultTheme quand le nom vient d'une saisie ou d'un fichier ; (3) là où vous posiez les deux axes sur chaque composant pour suivre l'application, laissez-les vides ; (4) une tuile dont la pastille revenait « au thème » par il_badge_color = 0 s'écrit désormais -1.
11.8 crypto : ce que la sécurité refuse, et où vont les chemins relatifs #
La revue de sécurité de crypto ferme une porte réelle — un jeton JWT forgé avec la clé PUBLIQUE passait quand l'algorithme manquait — et remplace plusieurs silences par un refus qui dit sa raison. Aucune méthode n'est retirée ; une signature ECDSA change de forme, et les chemins relatifs des fichiers suivent une seule règle.
| Quoi | 3.0 | 4.0 |
|---|---|---|
of_jwt_verify, of_jwt_sign | un algorithme VIDE valait JWT_HS256, et une clé PEM était acceptée comme secret HS256 : vérifié avec la clé PUBLIQUE comme secret, un jeton forgé passait ; l'algorithme de l'en-tête se comparait sans casse, un en-tête crit était ignoré | l'algorithme est OBLIGATOIRE et comparé EXACTEMENT ; une clé PEM n'est jamais un secret HMAC (of_hmac aussi) ; crit est refusé — chaîne vide et is_last_error |
of_jwt_sign | un exp donné dans les claims était remplacé en silence par la durée al_expires_seconds | les deux à la fois sont refusés : gardez l'un ou l'autre |
of_encrypt, of_decrypt | un mot de passe VIDE chiffrait — n'importe qui relisait la valeur | refusé, comme of_encrypt_file le faisait déjà ; of_password_hash("") reste permis |
il_pbkdf2_rounds | une valeur hors de 1 000 à 10 000 000 retombait en silence sur 100 000 | l'appel est REFUSÉ : chaîne vide pour un texte, -5 pour un fichier, et is_last_error dit les bornes |
of_password_hash | il_pbkdf2_rounds (100 000 tours) | il_password_hash_rounds, 600 000 par défaut (chiffre de l'OWASP) ; les valeurs déjà stockées portent leurs tours et se vérifient toujours |
of_base64_decode, of_base64url_decode, of_hex_decode, of_decrypt… | des octets qui ne sont pas un texte (un PDF, une image) revenaient en caractères de remplacement, sans erreur | chaîne vide et is_last_error, qui renvoie à of_base64_decode_to_file ou of_decrypt_file |
of_sign (clé EC) | signature IEEE-P1363 (64 octets), qu'openssl dgst et Java refusent | signature DER ; of_verify lit les deux formes, une signature déjà stockée se vérifie toujours ; le JWT ES256 reste en P1363 |
of_totp_code, of_totp_verify | un caractère hors base32 dans le secret (un 0 tapé pour un O) était retiré en silence : un AUTRE secret, des codes qui ne correspondaient jamais | refusé (espaces et = tolérés) |
of_encrypt_file | « quelle que soit sa taille » | jusqu'à 64 Go, la limite d'un conteneur AES-GCM : au-delà, -7 |
chemins relatifs : n_pbt_crypto, u_pbt_zip, of_capture du lecteur vidéo | lus dans le dossier COURANT (qui bouge après un DirList ou une boîte de fichiers), écrits à côté de l'EXE (dans l'IDE : le dossier de PowerBuilder) | LUS dans le dossier courant, puis dans le dossier où l'application a démarré, puis à côté de l'EXE ; ÉCRITS dans le dossier où l'application a démarré — le même dans l'IDE et compilé |
La recette : (1) passez TOUJOURS l'algorithme d'un JWT en constante (n_pbt_crypto.JWT_RS256), jamais lu dans le jeton ni dans un INI qui peut manquer ; (2) si un serveur vérifiait vos signatures EC en P1363 (.NET VerifyData par défaut), convertissez-les ou vérifiez-les en DER (DSASignatureFormat.Rfc3279DerSequence) ; (3) un il_pbkdf2_rounds réglé hors bornes doit revenir entre 1 000 et 10 000 000 ; (4) un fichier qu'un chemin relatif écrivait à côté de l'EXE se trouve désormais dans le dossier de démarrage de l'application — donnez un chemin complet si vous en voulez un autre.
11.9 restclient : des en-têtes qui disent leur refus, des redirections qui gardent la requête #
La revue de restclient remplace deux pertes silencieuses — une modification jamais faite derrière une redirection, une authentification jamais envoyée derrière un en-tête mal formé — par un comportement juste ou un refus qui dit sa raison. Aucune méthode n'est retirée ; trois deviennent des fonctions, deux propriétés arrivent, et l'authentification Windows ne part plus que si vous la demandez. Un point vaut pour les cinq composants non visuels qui lèvent des événements : une erreur dans votre code d'événement se voit désormais.
| Quoi | 3.0 | 4.0 |
|---|---|---|
of_set_header, of_set_bearer, of_set_basic | des subroutines : rien n'était rendu, une valeur mal formée était gardée telle quelle | des fonctions long : 0, ou -5 et rien ne change — nom vide, nom avec une espace, un deux-points ou un caractère de contrôle, valeur avec un saut de ligne ; une valeur vide retire l'en-tête |
| une requête dont un en-tête est mal formé | partait SANS AUCUN en-tête — l'Authorization comprise —, sans erreur | -5, et is_last_error = « invalid header: <nom> » ; rien ne part |
| redirection 301 / 302 d'un PUT, PATCH ou DELETE | repartait en GET sans corps : la modification n'était jamais faite, et l'appel rendait 200 | méthode et corps gardés ; seul un POST devient GET ; une 303 repart en GET sans corps (sauf HEAD) |
| une connexion réutilisée (celle d'une redirection) que le serveur ferme au même instant | -4 et « request failed: 12152 », par moments | la requête est renvoyée UNE fois, corps compris, sur une connexion neuve : le serveur n'y avait rien répondu |
of_response_text ( ), of_response_headers ( ), of_response_header, of_json_value | la dernière réponse synchrone du PROCESSUS : un second n_pbt_restclient l'écrasait | celle de L'OBJET ; of_close l'oublie |
| corps lu en texte, application 32 bits | plafonné à 64 Mo | plafonné à 16 Mo (64 Mo en 64 bits) : au-delà, -4 « body too large » — lisez-le avec of_download |
of_get_async, of_request_async… sur une URL ou un en-tête invalide | un id de requête, puis un ue_failed | -5 tout de suite, aucun événement ne suit ; is_last_error dit pourquoi (« invalid URL: … ») |
| authentification Windows (Negotiate / NTLM) | le système répondait de lui-même au défi d'un serveur de l'intranet avec les identifiants de la session | désactivée par défaut : les identifiants ne partent vers aucun serveur sans ib_windows_auth = true (propriété neuve) ; is_client_certificate (neuve) pose un certificat client |
of_cancel (long al_id), of_status (long al_id), of_response_text (long al_id)… | l'id d'une requête d'un AUTRE objet atteignait cette requête | seules les requêtes de l'objet : of_cancel rend -5 et n'annule rien, les lecteurs rendent 0 / "" |
of_status (long al_id) dans ue_failed | pouvait rendre 200 : le statut d'une réponse reçue puis perdue | 0 pour toute requête échouée |
ue_failed | -4 échec, -5 URL ou fichier | -4 échec, -5 fichier ou certificat client, -6 requête de plage refusée en démo |
erreur d'exécution dans un événement de l'application — restclient, speechout, soundplayer, toaster, commandpalette | avalé sans un mot, et les événements suivants n'étaient plus livrés | remonte à l'événement SystemError de l'application comme toute erreur de script ; les événements suivants sont livrés |
il_max_retries | rejouait aussi un échec qui se reproduit à l'identique (trop de redirections, redirection invalide ou HTTPS → HTTP, TLS, fichier impossible à écrire) | ces échecs-là ne sont plus rejoués ; 429, 503 et une panne réseau le sont toujours |
| URL et chemins | le fragment #… partait au serveur ; une Location relative ?page=2 perdait le dernier segment ; un chemin relatif d'of_download suivait le dossier courant du moment | le fragment n'est plus envoyé ; Location résolue comme un navigateur (RFC 3986) ; un chemin relatif est résolu à l'appel, dans le dossier courant de l'application au chargement de la bibliothèque |
La recette : (1) testez le retour de of_set_header, of_set_bearer et of_set_basic quand la valeur vient d'une saisie, d'un INI ou d'une base — un appel qui ignore le retour compile toujours, mais le -5 est désormais la seule trace d'un en-tête refusé ; (2) une API d'intranet en authentification Windows qui répondait 200 répond maintenant 401 : posez ib_windows_auth = true sur CE client ; (3) une application 32 bits qui lit un corps de plus de 16 Mo en texte passe à of_download ; (4) si vous partagiez un id de requête entre deux objets, lisez la réponse sur l'objet qui l'a envoyée ; (5) une erreur qui apparaît maintenant dans SystemError depuis ue_response ou ue_failed existait déjà : elle était avalée.
11.10 speechout : des réglages vivants, une destruction silencieuse #
La revue de speechout tient deux promesses que la doc faisait sans que le code les tienne — un réglage changé en cours de lecture vaut pour la phrase suivante, of_speak_from hors du texte rend -5 — et retire un événement levé au mauvais moment. Aucune méthode n'est retirée. Ajoutés : of_add_abbreviation, of_clear_abbreviations, l'événement ue_mark et deux balises, [mark=nom] et [rate=150]…[/rate].
| Quoi | 3.0 | 4.0 |
|---|---|---|
of_speak_from (al_index) | au-delà du texte, la lecture en cours était COUPÉE, rien ne repartait, et le retour valait 0 | -5, et la lecture en cours continue |
ii_rate, ii_pitch, ii_volume, is_lang, is_voice | changés pendant une lecture, pris seulement à la lecture SUIVANTE (la doc promettait la phrase suivante) | propriétés vivantes : la phrase suivante les prend |
ue_voice_fallback | muet quand seule la RÉGION manquait (fr-CA demandé, voix fr-FR) | levé ; les balises de langue se comparent sans casse, et fr_FR vaut fr-FR |
destroy (n_pbt_speechout) | pendant une lecture, levait ue_stopped — sur une fenêtre déjà à moitié détruite | aucun événement ; of_close() lève toujours ue_stopped |
of_sentence_count, of_count | le compte du texte lu la fois PRÉCÉDENTE (0 avant toute lecture) | le compte de is_text tel qu'il est |
of_pause, of_resume, of_clear_queue | créaient la voix quand elle n'existait pas ; une pause pendant l'attente des voix était perdue | 0 sans rien créer, -4 si la page n'a pas répondu ; la pause tient jusqu'à of_resume |
ue_sentence (al_index) | une abréviation (Mr., Dr., e.g.), une adresse épelée ou une version coupaient la phrase ; un texte sans point était une seule phrase | elles ne coupent plus ; une phrase de plus de 250 caractères est coupée : les numéros de phrase peuvent changer |
[pause=ms] | ignorée en FIN de texte | un silence après la dernière phrase, avant ue_stopped |
La recette : (1) testez le -5 de of_speak_from quand l'index vient d'un clic ; (2) si votre ue_stopped faisait quelque chose à la fermeture de la fenêtre, appelez of_close() vous-même dans son close — la destruction ne le lève plus ; (3) ne gardez pas de numéros de phrase d'une version à l'autre : prenez-les de ue_sentence, comme la doc l'a toujours dit.
11.11 statictext et texte riche : un balisage lu de gauche à droite #
La revue de statictext a surtout corrigé le texte à balises commun (4.7) : ces lignes valent pour le libellé de tout composant — titre d'onglet, panneau de barre d'état, toast, boîte de dialogue. Aucune méthode n'est retirée. Ajouté : of_refresh_parent_color(), et les zones [action], [hyperlink] et les en-têtes de [foldarea] d'un statictext s'atteignent au clavier (Tab, puis Entrée ou Espace).
| Quoi | 3.0 | 4.0 |
|---|---|---|
of_escape_markup (une donnée collée à une balise) | un ] échappé juste après une balise formait une paire avec le ] de la balise : "[b]" + of_escape_markup("] fin") + "[/b]" affichait [b]] fin sans gras | la donnée s'affiche telle quelle, et la mise en forme autour tient |
[/b], [/i]… | une fermante fermait la DERNIÈRE balise ouverte, quelle qu'elle soit : [b]A[i]B[/b]C laissait C en gras ; [/br] fermait aussi quelque chose | une fermante ne ferme que sa balise (les styles ouverts dedans continuent) ; une fermante sans ouvrante, ou celle d'une balise auto-fermante, est ignorée |
[picture=…] | coupée à la première virgule : une data URI (ce que rend of_icon()) ou un nom de fichier à virgule ne s'affichait pas | les dimensions se lisent en fin de valeur : les deux s'affichent |
[picture=\\server\share\x.png] | l'image était lue sur le partage réseau | refusée : une image du balisage ne va jamais chercher un partage réseau. Passez par of_icon(), qui l'embarque, ou par un picture |
[hyperlink=HTTPS://…] | un schéma en majuscules n'était pas ouvert | ouvert : le schéma se lit sans casse ; la liste reste http, https, mailto |
ii_padding | 0 = la marge du thème : un libellé ne pouvait jamais s'aligner sur le bord d'un champ voisin | -1 = la marge du thème (nouveau défaut), 0 = aucune marge |
ue_clicked, ue_double_clicked | levés en repliant un [foldarea] (trois événements pour un double-clic), et en relâchant la souris après une sélection de texte | plus levés : replier est une affaire de lecture |
[foldarea:… [action=…]] | levait ue_action ET repliait le bloc | ue_action seul, le bloc ne bouge pas |
ii_line_spacing, is_marquee_direction | une valeur hors de 50-400 était ignorée et relue 0 ; un sens inconnu ("UP") défilait vers le début mais se relisait tel quel | ramenée à la borne la plus proche, relue telle qu'appliquée ; un sens inconnu vaut et se relit MARQUEE_START |
of_scroll_to_end | ne rendait rien | rend 0, ou -2 si le composant n'est pas créé |
La recette : (1) un ii_padding = 0 écrit pour dire « la marge du thème » devient -1 — ou disparaît ; (2) une image de balisage posée sur un partage réseau passe par of_icon() ; (3) un ue_clicked qui réagissait au repli d'une section ne sera plus appelé : le repli ne se scripte pas.
11.12 breadcrumb : ce qui cède quand le chemin ne tient pas, et quitter le champ #
La seconde revue du fil d'Ariane (breadcrumb) a corrigé le repli par la largeur, le mode OVERFLOW_SHRINK et ce qu'un segment emporte avec lui. Aucune méthode n'est retirée. Une ligne vaut pour tous les menus natifs de la bibliothèque : la recherche par frappe.
| Quoi | 3.0 | 4.0 |
|---|---|---|
of_edit() sans / without ib_editable | rend 0, rien ne s'ouvre | rend -4 |
| quitter le champ de saisie du chemin pour un autre contrôle de l'application | le champ restait ouvert, texte à moitié tapé, et rien n'était rapporté | un texte modifié est validé : ue_path_entered ; passer à une autre application garde toujours la saisie |
OVERFLOW_SHRINK | tout libellé plafonné à 12 caractères, même avec la place ; le lieu courant coupé net au bord de la barre | rien n'est coupé tant qu'il y a la place ; le milieu cède d'abord, jusqu'à une lettre et …, le premier segment et le lieu courant en dernier |
OVERFLOW_COLLAPSE | le libellé du lieu courant pouvait s'élider avant que le milieu ne se replie | le milieu se replie d'abord ; le lieu courant ne s'élide qu'en dernier recours |
of_insert_item(…, ai_index) | les poignées des segments décalés désignaient un autre segment, leurs couleurs et infobulles restaient à l'ancienne adresse | leurs poignées sont libérées, leurs couleurs et infobulles les suivent ; un segment retiré (of_clear, of_truncate, of_remove_item) emporte les siennes |
of_item("b").il_back_color / of_item("a/b").il_back_color | deux valeurs pour le même segment | une seule : la clé nue unique et l'adresse désignent le même segment |
OVERFLOW_SCROLL, molette | sans effet | fait glisser la bande |
| les menus natifs (branches du fil, ruban, barre de menus, barre d'outils, menu contextuel) : une lettre tapée | ne faisait rien, hors mnémonique | les lettres tapées dans la seconde sautent à la première entrée qui commence ainsi (« Win »), comme dans l'explorateur ; un menu de plus de 500 entrées ne dessine que ce qui se voit |
La recette : (1) un of_edit() dont vous testiez le 0 sans avoir posé ib_editable rend désormais -4 ; (2) un ue_path_entered peut arriver quand l'utilisateur clique ailleurs dans la fenêtre — c'est le même texte qu'avec Entrée ; (3) après un of_insert_item au milieu du fil, redemandez les poignées des segments qui suivent.
11.13 buttonbar et clavier de fenêtre : la touche va au composant du focus #
La seconde revue de la barre de boutons (buttonbar) a touché le clavier de fenêtre, que partagent tous les composants qui répondent à Entrée, Échap, une lettre Alt ou un raccourci enregistré (button, buttonbar, et les raccourcis de of_register_shortcut de tous les composants). Aucune méthode n'est retirée ; une est ajoutée, of_focus_button.
| Quoi | 3.0 | 4.0 |
|---|---|---|
| deux composants d'une même fenêtre répondent à Entrée, Échap ou au même raccourci (deux feuilles MDI, deux panneaux) | le premier créé la recevait, même dans l'autre feuille | le plus proche du focus (même feuille, même panneau) ; à égalité, personne : la touche reste à la fenêtre |
Entrée sur un CommandButton PB qui a le focus, ou dans une DropDownListBox ouverte ; Échap dans une liste ouverte | déclenchait le bouton par défaut / annuler du composant | reste au contrôle, comme dans une boîte de dialogue Windows |
of_register_shortcut(chord, key) sur un bouton de la barre, puis le bouton grisé et réactivé, ou sa lettre is_shortcut changée | le raccourci était effacé | gardé : la lettre Alt et le raccourci de l'application ne se touchent plus |
| une lettre Alt d'un bouton face à un raccourci explicite de la même touche | le premier enregistré gagnait | le raccourci explicite gagne ; les lettres ne sont consultées qu'après lui |
of_clear_shortcuts() | effaçait aussi le raccourci is_shortcut d'un button | n'efface que les raccourcis de l'application |
buttonbar : of_register_shortcut(chord) sans clé, ou avec une clé qui ne nomme aucun bouton | rendait 0 et avalait la touche sans rien déclencher | rend -5 |
buttonbar : retirer le bouton de is_default / is_cancel, ou vider la barre | la propriété gardait la clé ; un bouton rajouté sous cette clé redevenait défaut | vidée : le rôle part avec son bouton |
| buttonbar : un autre bouton de la barre a le focus | l'accent restait sur is_default alors qu'Entrée déclenchait le bouton focalisé | le bouton focalisé porte l'apparence par défaut ; is_default relu est inchangé |
buttonbar : ii_padding_x négatif | revenait à 12 | le retrait du thème, comme ii_padding_y et ii_gap |
| buttonbar : le trait de séparation entre boutons | dessiné au milieu de l'espace, même quand les boutons ne se touchaient pas | seulement entre boutons accolés (ii_gap = 0), et du bon côté en lecture de droite à gauche |
La recette : (1) sur une barre, donnez un raccourci à UN bouton — of_register_shortcut("Ctrl+S", "save") — la forme sans clé rend -5 ; (2) après avoir retiré puis rajouté le bouton par défaut, reposez is_default ; (3) pour mettre le focus sur un bouton précis (« Non » plutôt que « Oui »), appelez of_focus_button("no").
11.14 button : un bouton grisé rend la main, et Alt+lettre répond #
La revue du bouton (button, déprécié au profit de buttonbar mais toujours livré et corrigé) change ce qu'il fait au clavier et ce que relisent ses propriétés. Aucune méthode n'est retirée ni ajoutée.
| Quoi | 3.0 | 4.0 |
|---|---|---|
un bouton ib_default, ib_cancel ou is_shortcut grisé (ib_enabled = false) | retenait Entrée, Échap ou son raccourci dans toute la fenêtre : la touche était perdue | rend la touche à la fenêtre (un CommandButton PB par défaut, un SingleLineEdit la reçoivent) ; réactivé, il la reprend |
le & de is_text (&Enregistrer) | soulignait la lettre, sans plus | Alt + la lettre déclenche le bouton où que soit le focus dans la fenêtre, après les raccourcis explicites, jamais grisé ; une barre de menus PB qui utilise la même lettre la perd |
| Entrée ou Espace tenus sur le bouton qui a le focus | une rafale de ue_clicked ; Espace partait à l'appui | un seul ue_clicked ; Espace part au relâchement, comme sous Windows |
un is_shortcut que la fenêtre ne sait pas déclencher ("Win+S", "Ctrl+Entrée", "Ctrl+Pause") | se relisait tel quel ; "Win+S" faisait partir le bouton sur un S seul | refusé : il se relit "". Les noms de touches s'écrivent en anglais (Enter, Esc, Del) |
is_shortcut avec une info-bulle | l'accord n'apparaissait nulle part | ajouté à l'info-bulle (au titre d'une info-bulle enrichie) : « Enregistrer (Ctrl+S) » |
of_register_shortcut("F5") sur le bouton, puis ib_default, ib_cancel ou is_shortcut changés | F5 était effacé | F5 et is_shortcut répondent tous les deux |
is_text, is_image, is_style, ii_badge, il_badge_color, ii_badge_size, ii_image_size lus après of_reset() | l'ancienne valeur, alors que l'écran était remis à zéro | leur défaut ("", "standard", 0, -1) |
| un bouton grisé et la touche Tab | recevait le focus | est sauté |
un libellé fait d'une seule image ([picture=…]), ou un & dans une balise | le libellé disparaissait, ou la balise était cassée | affichés tels qu'écrits |
La recette : écrivez les noms de touches de is_shortcut en anglais ("Ctrl+Enter", pas "Ctrl+Entrée") et relisez la propriété si vous doutez : "" veut dire refusé. Si vous grisiez un bouton par défaut pour « désactiver Entrée », rien à changer : c'est désormais ce qui se passe.
11.15 menubar : une barre de menus qui rend le focus, et Alt+lettre de partout #
La revue de la barre de menus (menubar) change ce qu'elle fait du focus et du clavier, et ce que font tous les menus natifs en écriture de droite à gauche. Aucune méthode n'est retirée ni ne change de signature.
| Quoi | 3.0 | 4.0 |
|---|---|---|
un clic dans la barre, puis un choix (Edition > Coller) | la barre gardait le focus : GetFocus() dans ue_item_selected ne nommait plus le champ, le collage partait dans le vide | le focus revient au contrôle d'avant avant que ue_item_selected parte ; même chose sur Échap et sur un clic dehors |
Alt+F maintenu, ou F10, le focus dans un champ | rien : seul Alt tapé puis F ouvrait File | Alt+F ouvre File depuis n'importe quel contrôle, F10 fait comme Alt ; un Alt+F que votre application a enregistré garde la priorité, et un menu PowerBuilder natif sur la même lettre la perd |
Ctrl++, Ctrl+-, Ctrl+,, Alt+Left, Ctrl+Space, Ctrl+Tab, Ctrl+Backspace (tous les composants à raccourcis) | affichés, mais rien ne partait depuis un contrôle PB ; le button les refusait ("") | déclenchés où que soit le focus ; ces touches exigent Ctrl ou Alt (seules, elles restent la navigation) |
en RTL, les déroulants de tous les menus natifs (barre de menus, ruban, barre d'outils, fil d'Ariane, menu contextuel…) | calés à gauche, cascades à droite, chevron ›, Gauche refermait | le déroulant pend bord droit sous son titre, une cascade s'ouvre à gauche (chevron ‹) : Gauche ouvre, Droite referme |
| un clic sur le titre d'un menu déjà ouvert | le refermait puis le rouvrait (un second ue_menu_opening) | le referme, comme sous Windows |
un is_shortcut posé sur une entrée qui ouvre une cascade | affiché, et levait ue_item_selected("file/export"), une adresse qu'aucun clic ne produit | ni affiché ni déclenché : un raccourci se pose sur une feuille |
| une barre dont tous les menus sont masqués ou grisés, à côté d'un ruban | prenait Alt pour rien : les keytips du ruban ne sortaient plus | rend Alt au ruban tant qu'aucun titre n'est atteignable |
deux titres sur la même lettre (&Fichier, &Favoris) | Alt+F ouvrait toujours le premier | dans la barre, la bande passe de l'un à l'autre, Entrée ouvre |
| une entrée retirée puis rajoutée à la même adresse (une liste de fichiers récents) | reprenait les couleurs et l'info-bulle de la disparue | naît sans couleur (il_back_color relu -1) ; même chose après of_remove_menu et of_clear |
une lettre accentuée ou non latine dans un déroulant (&Édition) | soulignée, mais aucune touche ne l'atteignait | se tape comme sur le clavier |
| la touche Tab sur la barre | un arrêt par titre | un seul arrêt pour toute la barre ; les flèches passent d'un titre à l'autre |
| nouveau | — | is_image (l'icône d'une entrée, changée à chaud), is_group (des entrées radio), ue_menu_closed(as_key) (un déroulant fermé sans choix) |
La recette : dans ue_item_selected, travaillez sur GetFocus() ou sur le DataWindow en saisie comme avec un menu PowerBuilder ; écrivez un & dans chaque titre ; si votre application utilisait Alt+lettre pour autre chose, enregistrez-le (of_register_shortcut) : il garde la priorité.
11.16 ribbon : un choix qui tient, des menus qui restent ouverts, des refus qui se disent #
La seconde revue du ruban (ribbon) corrige ce que l'utilisateur voyait — un choix de police défait, un menu qui se fermait sous le pointeur — et applique au ruban la règle de la bibliothèque : un ajout ou un retrait qui ne peut pas se faire rend -5. Aucune méthode n'est retirée ni ne change de signature.
| Quoi | 3.0 | 4.0 |
|---|---|---|
| ribbon : un choix dans la liste d'une zone de liste, puis la sortie du champ | ue_combo_changed deux fois — le choix, puis le texte tapé avant lui, qui l'écrasait ; is_text relisait le texte tapé | un seul ue_combo_changed, le choix ; le champ le montre aussitôt, même d'une zone non modifiable |
ribbon : Entrée ou Échap dans une zone de liste, un compteur, ou sur un bouton du ruban qui a le focus | partaient aussi au bouton par défaut ou d'annulation de la fenêtre | restent au ruban ; Échap dans un champ rend le texte d'avant la frappe |
| ribbon : un menu ouvert pendant que l'application met le ruban à jour (un autre contrôle masqué, un onglet renommé, une vignette ajoutée) | se fermait, et le choix fait dedans ne levait aucun événement | reste ouvert ; il ne se ferme que si son propre contrôle disparaît, est masqué ou grisé |
ribbon : is_app_button réécrit (« File » → « Fichier ») ; is_app_button = "" | le menu applicatif était vidé ; une chaîne vide affichait « File » | le menu reste ; une chaîne vide retire le bouton et son menu |
ribbon : of_select_tab d'un onglet inconnu ou masqué | 0 | -5, rien ne change |
ribbon : ajout d'une entrée de menu, d'une vignette, d'un choix de zone de liste, d'un bouton d'accès rapide, d'une entrée du menu applicatif ou d'un groupe contextuel sur une clé déjà prise ; clé qui contient une barre verticale ou commence par __ ; parent inconnu (séparateur, en-tête, choix, entrée sans bouton applicatif, onglet sous un groupe contextuel absent) | 0 : un doublon, ou rien du tout (un onglet contextuel sans bandeau) | -5 |
ribbon : of_remove_tab, of_remove_group, of_remove_item, of_select_gallery_item sur une adresse inconnue ; of_open sur un bouton d'accès rapide | 0, sans effet | -5. of_open("onglet/groupe") ouvre le panneau d'un groupe replié |
ribbon : une poignée of_qat_item tenue après of_remove_item de son bouton | restait valide et désignait un bouton disparu | libérée : IsValid() rend false |
| ribbon : touches d'accès rapide (keytips) — deux contrôles de la même lettre dont l'un est masqué, un contrôle d'un groupe replié | le masqué volait la lettre, rien ne partait ; la lettre d'un groupe replié ne faisait rien ; une touche de deux lettres ne répondait jamais | seul un contrôle visible et actif répond ; la lettre ouvre le panneau du groupe replié ; "FP" se tape lettre après lettre |
| ribbon : flèches du clavier sur un bouton scindé, un sélecteur de couleur, une zone de liste ou une galerie grisés | s'y arrêtaient, sans rien pouvoir y faire | les sautent ; Bas sur un bouton déroulant ou scindé ouvre son menu |
La recette : (1) ue_selection_changed et ue_minimized sont levés aussi quand votre code appelle of_select_tab ou pose ib_minimized, comme pour un clic : si vous appeliez vous-même votre traitement juste derrière, retirez cet appel, il passerait deux fois ; (2) testez le -5 des ajouts si votre construction peut répéter une clé ; (3) un is_app_button = "" qui voulait dire « File » s'écrit désormais is_app_button = "File". Les retraits sous un contrôle et la poignée d'une entrée du menu applicatif sont dans le tableau 11.5.
11.17 toolbar : une règle de raccourci, une seule info-bulle, des refus qui se disent #
La seconde revue de la barre d'outils (toolbar) aligne ses raccourcis sur ceux de la fenêtre et rend visible ce qui était écrit sans effet. Aucune méthode n'est retirée ni ne change de signature ; deux codes de retour apparaissent (-5).
| Quoi | 3.0 | 4.0 |
|---|---|---|
un outil à is_shortcut = "Delete" (une touche seule) et un champ dans la barre | Suppr dans le champ déclenchait l'outil et n'effaçait rien ; hors de la barre, rien | une touche seule n'est jamais un raccourci : il faut Ctrl ou Alt, ou F1 à F24 — la règle de la fenêtre |
| une touche de raccourci tenue (tous les composants) | l'action se répétait : une bascule clignotait | une action par appui |
of_register_shortcut(accord, cle) hérité, sur une toolbar | ne déclenchait rien, et la touche était avalée | prend l'adresse de l'outil ou de l'entrée ("main/save"), -5 sinon ; la forme à un argument rend -5 ; of_clear_shortcuts vide aussi is_shortcut |
| le raccourci d'un champ, d'un sélecteur de date, d'un libellé | avalait la touche sans rien faire | le champ reçoit le curseur (Ctrl+F sur une recherche), le sélecteur ouvre son calendrier ; un libellé n'en prend pas |
l'info-bulle donnée à l'ajout (of_add_button(…, tooltip)) | balises en littéral ; deux bulles à la fois si is_tooltip était posé ensuite | une seule info-bulle thémée, texte riche, le raccourci en suffixe ; is_tooltip la remplace et se relit |
of_add_bar(MAIN_BAR) après un premier outil sur main | rendait 0 sans rien faire | -5 : pour la placer, ajoutez main avant tout outil |
of_set_layout d'un texte qui n'est pas du JSON | rendait 0 | -5 |
ue_layout_changed | absent quand un ajout de barre en poussait une autre ; levé quand on réaffichait une barre déjà visible | levé dans le premier cas, plus dans le second |
| séparateurs, déroulant ou cascade dont les entrées sont toutes masquées | traits orphelins ; le déroulant et la cascade restaient actifs et muets | pas de trait en tête, en queue ni doublé ; ils se dessinent grisés |
| un clic droit dans la barre | aucun événement, ue_rclicked compris | ue_item_rclicked(adresse) sur un outil, ue_rclicked hors de tout outil — jamais les deux |
les couleurs d'une entrée de menu (of_menu_item(…).il_back_color) | écrites et relues, jamais dessinées | dessinées dans le menu |
toolbar : ue_rclicked, ue_mouse_enter, ue_mouse_leave | documentés, mais jamais levés | levés : un clic droit sur la barre hors de tout outil, l'entrée et la sortie de la souris |
La recette : (1) un raccourci d'outil sur une touche seule (hors F1 à F24) prend Ctrl ou Alt ; (2) sur une toolbar, of_register_shortcut reçoit l'adresse de l'outil ou de l'entrée ; (3) pour placer la barre main, appelez of_add_bar(MAIN_BAR, bande, rang) avant son premier outil.
11.18 statusbar : une poignée qui redimensionne, un message qui ne remplace pas le texte #
La seconde revue de la barre d'état (statusbar) donne à la poignée de redimensionnement son vrai rôle et fait de chaque clé vide ou inconnue un refus. Aucune méthode n'est retirée ni ne change de signature ; of_flash_panel gagne un code de retour (-5).
| Quoi | 3.0 | 4.0 |
|---|---|---|
ib_show_resize_grip = true, puis tirer la poignée | un dessin : rien ne bouge | la fenêtre se redimensionne par son coin (bas-gauche en RTL) ; la poignée n'est pas dessinée sur une fenêtre maximisée ou sans bord redimensionnable |
of_flash_panel sur une clé que la barre n'a jamais reçue, ou sur une adresse | 0, rien ne s'affiche | -5 |
of_panel(k).is_text relu pendant un of_flash_panel | le message | le texte du panneau (le message passe par-dessus) |
of_panel(""), of_panel("a/b") : écriture puis relecture | écrivait dans les panneaux sans clé et les séparateurs | ne désigne rien : n'écrit nulle part, se relit vide |
is_state hors des constantes STATE_* ("Error", "very bad") | relu tel quel, sans couleur ; une valeur à espace levait une erreur de script | la casse est ignorée ("Error" = erreur) ; toute autre valeur vaut « aucun état » et se relit vide |
ii_progress = 150 puis relu | 150 (dessiné à 100 %) | 100, ce qui est dessiné |
| un panneau désactivé qui porte un état | gardait la couleur de l'état : il avait l'air actif | grisé, marque d'état comprise |
un panneau retiré, vidé (of_clear, of_clear_menu), désactivé ou masqué pendant que sa liste est ouverte | la liste restait ouverte, et un choix levait ue_panel_menu_clicked | la liste se ferme, rien n'est levé (de même pour une entrée devenue grisée ou masquée) |
| double-clic sur un panneau à liste déroulante | ue_panel_double_clicked en plus de la liste ouverte | la liste seule |
| Flèche haut / bas sur un panneau à liste déroulante qui a le focus | passait au panneau voisin | ouvre la liste (les flèches Gauche / Droite se déplacent toujours) |
| une liste déroulante faite de séparateurs seuls | chevron et liste vide | pas de liste : le panneau reste ce que dit ib_clickable |
La recette : (1) posez ib_show_resize_grip sur une barre d'état placée en bas d'une fenêtre redimensionnable ; (2) testez le retour de of_flash_panel comme celui des autres méthodes à clé ; (3) donnez une clé à tout panneau que vous voulez relire ou modifier.
11.19 dockcontainer : un of_select_panel qui trouve le panneau, des fenêtres flottantes qui se souviennent #
La seconde revue du conteneur de panneaux (dockcontainer) donne à of_select_panel un effet sur un panneau replié ou flottant, et fait retenir les fenêtres flottantes par la disposition. Aucune méthode n'est retirée ni ne change de signature.
| Quoi | 3.0 | 4.0 |
|---|---|---|
of_select_panel d'un panneau replié, flottant ou masqué | 0, rien ne se voit | replié : son volet glisse ; flottant : sa fenêtre passe devant ; masqué : -5 |
of_float_panel du panneau principal ou d'un panneau masqué | 0, rien ne se passe | -5 |
of_move_panel qui empile le panneau PRINCIPAL | 0, et son voisin disparaît sans onglet | -5, rien ne bouge |
as_position inconnue (of_add_panel, of_move_panel) | 0, le panneau se docke en bas | -5, le contrôle n'est pas touché |
disposition (of_get_layout, ue_layout_changed, of_set_layout) et panneaux FLOTTANTS | oubliés : un panneau détaché revenait ancré ; déplacer sa fenêtre ne levait rien | retenus avec la place de leur fenêtre, rouverts à la restauration (ramenés sur un écran) ; déplacer ou redimensionner la fenêtre lève ue_layout_changed |
of_set_layout d'une disposition sans panneau principal | le panneau principal courant est retiré | il est gardé |
is_title d'un panneau ajouté sans titre | sa clé ; un titre donné ensuite au panneau principal ne faisait pas naître son en-tête | "" (l'onglet montre toujours la clé) ; l'en-tête du principal suit son titre |
La recette : (1) ue_panel_selected, ue_panel_floated et ue_panel_pinned sont levés aussi après of_select_panel, of_float_panel et ib_pinned, comme pour un geste : si vous appeliez vous-même votre traitement juste derrière, retirez cet appel, il passerait deux fois ; (2) testez le -5 de of_move_panel, of_float_panel et of_select_panel ; (3) une disposition enregistrée avant la 4.0 se restaure telle quelle, ses panneaux restent ancrés.
11.20 tab : des codes de retour honnêtes, un clic droit qui revient à l'application #
La seconde revue des onglets (tab) fait dire à of_add_page et of_select_page ce qu'ils refusent, annonce la disposition quand votre code masque un onglet, et rend le clic droit à l'application quand le menu intégré est coupé. Aucune méthode n'est retirée ni ne change de signature ; un événement est ajouté.
| Quoi | 3.0 | 4.0 |
|---|---|---|
of_add_page d'un contrôle déjà hébergé (par cet onglet sous une autre clé, ou par un autre composant) | 0, et un onglet sans rien derrière | -5, rien n'est ajouté |
of_select_page d'un onglet refusé alors qu'aucun onglet n'est actif (tous masqués) | 0 | -5 |
Masquer ou réafficher un onglet par votre code (ib_visible) | aucun ue_layout_changed | ue_layout_changed levé avec la disposition entière, comme pour of_move_page : c'est l'avis que la disposition à enregistrer a changé |
Clic droit sur un onglet avec ib_context_menu = false | rien | l'onglet est sélectionné puis ue_tab_rclicked(as_key) (nouveau) est levé : ouvrez-y votre menu |
Revenir sur l'onglet courant pendant que ue_selection_changing attend sa réponse | une réponse « oui » activait quand même l'autre onglet | la question est annulée : le dernier geste de l'utilisateur l'emporte |
Un onglet désactivé ou masqué pendant que ue_tab_closing attend sa réponse | se fermait quand même | reste ouvert |
| Fermer au clavier (Suppr) l'onglet qui a le focus | le focus quittait la bande, les flèches ne répondaient plus | le focus passe à l'onglet devenu actif |
La recette : (1) testez le -5 de of_add_page (une page ne s'héberge qu'une fois) et de of_select_page ; (2) si vous enregistrez la disposition dans ue_layout_changed, rien à faire — elle suit désormais aussi vos ib_visible ; (3) là où ib_context_menu = false laissait le clic droit sans suite, scriptez ue_tab_rclicked.
11.21 listbar : une entrée masquée jamais sélectionnée, une disposition vérifiée #
La seconde revue de la barre de navigation (listbar) fait refuser à of_select_item une entrée masquée et à of_set_layout un texte qui n'est pas une disposition, et ajoute une pastille de compteur, le clavier Gauche / Droite et le clic droit d'entrée. Aucune méthode n'est retirée ni ne change de signature.
| Quoi | 3.0 | 4.0 |
|---|---|---|
of_select_item d'une entrée masquée (ib_visible = false) | 0, une sélection invisible | -5, la sélection ne bouge pas ; masquer l'entrée déjà sélectionnée la garde sélectionnée |
of_set_layout d'un texte qui n'est pas un objet JSON (fichier tronqué) | 0, rien n'est appliqué | -5, rien n'est envoyé |
| Section repliée dans une disposition restaurée, ajoutée plus tard | naît repliée | naît dépliée : le pli ne vaut que pour une section présente |
| Section au titre vide | ses entrées collées à celles du dessus ; repliable, et plus rien pour la rouvrir | un espace (un filet en rail) la sépare ; elle ne se replie jamais |
Nouveau : is_badge (entrée), ue_item_rclicked(as_keys), Gauche / Droite et frappe d'une lettre | — | une pastille de compteur, un clic droit d'entrée (un seul événement, l'entrée n'est pas sélectionnée), le pli au clavier |
La recette : (1) ue_selection_changed est levé aussi après un of_select_item de votre code (ouvrir la page d'accueil au démarrage, par exemple), comme pour un clic : si vous ouvriez la page vous-même juste derrière, laissez-le à l'événement, sinon elle s'ouvrirait deux fois ; (2) testez le -5 d'of_select_item si vous masquez des entrées, et celui d'of_set_layout si la disposition vient d'un fichier ; (3) une section sans titre ne se replie plus : donnez-lui un titre si elle doit se replier.
11.22 tilesbox : un titre qui ne crée plus de groupe, la réorganisation au clavier #
La seconde revue du panneau de tuiles (tilesbox) empêche le titre d'un groupe de créer ce groupe, fait refuser à of_add_live_item un texte vide, donne une seule tabulation par groupe et ajoute la réorganisation au clavier. Aucune méthode n'est retirée ni ne change de signature. il_badge_color suit désormais la règle de toutes les couleurs (voir la ligne picture, button, tilesbox plus haut).
| Quoi | 3.0 | 4.0 | |
|---|---|---|---|
of_group(clé).is_title sur un groupe jamais ajouté | créait un groupe visible qu'on ne pouvait ni remplir ni retirer (sauf of_clear) | n'écrit rien : seul of_add_group crée un groupe ; une clé vide, avec / ou ` | ` rend un handle inerte |
of_add_live_item d'un texte vide | 0, et le texte fixe is_live_text était effacé | -5, rien ne change | |
of_tile(adresse).of_count() | 0 | le nombre de faces posées par of_add_live_item | |
Réorganiser (ib_reorderable = true) | à la souris seulement | aussi au clavier : Ctrl+Gauche/Droite dans le groupe (sens de lecture), Ctrl+Haut/Bas vers le groupe voisin ; mêmes ue_tile_moved et ue_layout_changed | |
| Touche Tab | s'arrêtait sur chaque tuile ; une flèche vers un bord sans voisin sautait à la tuile précédente ou suivante du document | un arrêt par groupe (la dernière tuile focalisée), les flèches parcourent les tuiles ; au bord, le focus reste | |
| Tuile désactivée ou masquée pendant qu'on la glisse | déplacée au relâchement, ue_tile_moved levé | le glisser est abandonné, rien ne bouge | |
ib_collapsed d'un groupe posé par votre code | aucun événement | ue_group_toggled, comme un clic sur l'en-tête (rien quand le groupe est déjà dans cet état) |
La recette : (1) créez un groupe par of_add_group avant d'en changer le titre ; (2) testez le -5 d'of_add_live_item si le texte d'une face peut être vide ; (3) ce que vous faisiez dans ue_tile_moved vaut désormais aussi pour un déplacement au clavier.
11.23 stepbar : « tout terminé » ancré sur la dernière étape, des étapes retirées qui n'emportent plus rien #
La seconde revue de la barre d'étapes (stepbar) rend « tout terminé » cohérent avec la règle de l'étape en cours (une identité, pas un rang), fait partir avec une étape retirée ce qui lui appartenait, et corrige quatre défauts d'affichage. Aucune signature ne change.
| Quoi | 3.0 | 4.0 |
|---|---|---|
of_add_step quand la barre est « tout terminé » | l'étape ajoutée s'affichait terminée (coche) ; ii_current passait à n + 2 sans événement | ajoutée après la dernière étape d'alors, elle devient l'étape en cours et ue_step_changed est levé ; insérée avant, rien ne change |
of_step(clé).is_state sur une clé qui ne nomme aucune étape | gardé, puis appliqué à l'étape ajoutée plus tard sous ce nom | ignoré ; une étape ajoutée naît sur son état calculé |
Couleurs et infobulle d'une étape retirée (of_remove_step, of_clear_steps) | revenaient sur la prochaine étape ajoutée sous la même clé | partent avec l'étape |
Barre compactée (OVERFLOW_AUTO) dont l'étape en cours est forcée en erreur | plus aucun libellé ; une étape forcée à STATE_CURRENT gagnait le sien | le libellé reste à l'étape en cours (le rang de ii_current), quel que soit son état |
OVERFLOW_SHRINK | libellés sur plusieurs lignes, mots longs coupés sans « … » | une ligne par libellé, finie par « … » |
POSITION_BOTTOM avec une seconde ligne (is_description) | au-dessus du libellé, collée à lui | sous le libellé, au-dessus de la pastille |
Survol d'une étape désactivée ou hors de portée (il_back_color_hover, il_text_color_hover) | la couleur de survol s'allumait | rien ne s'allume : l'étape ne répondra pas au clic |
La recette : si votre code ajoute une étape après avoir posé « tout terminé » (ii_current = of_count() + 1) et veut que la barre le reste, reposez ii_current = of_count() + 1 après l'ajout. Si vous rebâtissez la barre avec les mêmes clés, reposez les couleurs et infobulles voulues : elles ne survivent plus au retrait.
11.24 progressbar : une barre qui tient dans son contrôle, un libellé qui suit la langue #
La seconde revue de la barre de progression (progressbar) la fait tenir là où était un HProgressBar, n'envoie plus au composant que les valeurs qui changent le dessin, écrit le pourcentage dans la langue d'affichage et ajoute is_label_format (un libellé à vous) et is_state (pause, erreur). Aucune signature ne change.
| Quoi | 3.0 | 4.0 |
|---|---|---|
| Barre linéaire dans un contrôle bas ou large | marge de 12 px, 640 px de large au plus : sous 24 px de haut, une barre de défilement apparaissait et la barre était coupée | marge de 2 px, toute la largeur ; la piste (14 px) et le libellé rétrécissent avec la hauteur |
Libellé du pourcentage (ib_label) | « 50% » dans toutes les langues | dans la langue d'affichage : « 50 % » en français |
of_set_property("value", "12,5"), ou un texte qui n'est pas un nombre | envoyé tel quel : commande perdue sans rien dire ; un minimum invalide bloquait l'échelle jusqu'au prochain of_reset | « 12,5 » est lu 12.5 ; un texte qui n'est ni un nombre (ni true/false pour un booléen) est ignoré |
id_value relu | la valeur que le composant avait reçue ; chaque affectation était un aller-retour avec lui | la dernière valeur posée par l'application ; le composant ne reçoit que celles qui changent le dessin (un dixième de pour cent) |
of_reset() sur une barre remplie | la barre redescendait à zéro en glissant | elle revient à zéro d'un coup |
La recette : (1) si vous calculiez un pourcentage à la main, posez id_maximum et affectez la valeur brute, à chaque ligne si vous voulez ; (2) si un test automatique lisait le libellé, attendez le format de la langue d'affichage ; (3) pour montrer une pause ou une erreur, préférez is_state à une couleur choisie par il_color.
11.25 picture : une affectation relit le fichier, un échec dit sa raison #
La seconde revue de l'image (picture) lui fait relire le disque à chaque affectation, comme le Picture natif, donne à ue_error un second argument qui dit pourquoi l'image manque, refuse une adresse http:// au lieu d'échouer sans raison, et ne lève plus qu'un ue_clicked sur un double-clic. ue_error est la seule signature qui change.
| Quoi | 3.0 | 4.0 |
|---|---|---|
| picture : double-clic sur l'image | deux ue_clicked, puis ue_double_clicked | un seul ue_clicked, puis ue_double_clicked — comme le Picture natif |
picture : ue_error | ue_error (string as_message) : le message seul | ue_error (string as_message, string as_reason) : as_reason vaut une constante REASON_* (introuvable, trop gros, http refusé, format non affiché, autre). Votre script de l'événement reçoit l'argument de lui-même ; un appel event ue_error(…) écrit dans votre code en passe désormais deux |
picture : is_source = "http://…" | tenté en https par le moteur ; sur un serveur sans TLS, échec sans raison | refusé d'emblée : pictogramme d'erreur et ue_error avec REASON_INSECURE. Téléchargez d'abord l'image (n_pbt_restclient.of_download) et affichez le fichier |
picture : le même chemin réaffecté après avoir réécrit le fichier ; mono: ou tint: sur is_source ; une image .avif | l'ancienne image ; le pictogramme d'erreur ; introuvable | le fichier relu, comme le Picture natif ; le glyphe recoloré par le thème ; affichée |
| picture : image zoomée plus petite que son cadre (bandes) ; molette horizontale, pavé tactile ; bouton droit maintenu | elle sortait du cadre au glisser ; un cran de zoom par événement, dézoom sur un défilement horizontal ; l'image se déplaçait | elle reste où is_align et is_valign la placent ; zoom proportionnel au geste, rien sur un défilement horizontal ; seul le bouton gauche déplace |
La recette : (1) si un script ouvrait une fiche sur ue_clicked et une autre action sur ue_double_clicked, rien à faire — le double-clic ne rouvre plus la fiche ; (2) remplacez l'analyse du texte de as_message par un test de as_reason ; (3) une image d'un serveur intranet en http se télécharge d'abord (n_pbt_restclient.of_download vers un dossier temporaire), puis s'affiche par son chemin ; (4) si vous ajoutiez un ?v= au chemin pour forcer la relecture d'une image réécrite, retirez-le : la réaffectation suffit.
11.26 video : des sous-titres et des formats qui se lisent, un échec qui dit sa raison #
La seconde revue du lecteur (video) donne à ue_failed un second argument qui dit pourquoi un film ne joue pas, refuse une adresse http://, lit les .mkv et les .mov, charge les sous-titres donnés par URL, et met le film en pause quand le composant est masqué. ue_failed est la seule signature qui change.
| Quoi | 3.0 | 4.0 | |
|---|---|---|---|
video : ue_failed | ue_failed (string as_message) | ue_failed (string as_message, string as_reason) — as_reason est une constante REASON_* | |
video : une source http:// | échouait en « introuvable » | refusée tout de suite, REASON_INSECURE ; seul https se lit | |
video : un .mkv, un .mov | .mkv « introuvable », .mov refusé par son nom | servis et lus quand le moteur les décode ; .avi, .wmv, .flv restent refusés (REASON_FORMAT) | |
| video : un sous-titre donné par URL | ne s'affichait jamais, sans rien dire | téléchargé par le composant (le serveur doit autoriser CORS) ; un refus lève ue_subtitles_failed | |
video : of_add_subtitles deux fois pour la même langue | remplaçait la piste en silence, 0 | -5 (aussi pour une langue à / ou ` | ) ; of_remove_subtitles` d'abord |
| video : le composant masqué pendant la lecture | le film continuait, avec le son | pause, ue_paused, et attente d'of_play (ib_pause_when_hidden, vrai par défaut) | |
| video : glisser le volume sur la barre | un ue_volume_changed par pixel | un seul, au lâcher | |
video : of_capture vers un .jpg | un PNG sous un nom .jpg | un JPEG ; la surcharge of_capture(as_path, al_max_width) réduit l'image | |
video : of_get_last_error après un échec d'of_capture | l'erreur précédente de la bibliothèque | la raison de CET échec (dossier absent, chemin vide…) | |
video : Échap en plein écran, ib_enabled = false | sans effet (seul Alt+F4 sortait) | sort du plein écran |
La recette : (1) ajoutez as_reason à votre script de ue_failed et testez-le plutôt que le texte du message ; (2) un film ou un sous-titre d'un serveur intranet en http se télécharge d'abord (n_pbt_restclient.of_download vers un dossier temporaire), puis se lit par son chemin ; (3) ue_fullscreen_changed, ue_pip_changed, ue_source_changed et ue_chapter_changed sont levés aussi après vos propres appels (of_fullscreen, of_pip, of_next, of_seek…), comme pour un geste : si votre code appelait lui-même son traitement juste derrière, retirez cet appel, il passerait deux fois ; (4) un mur d'écrans qui doit jouer masqué pose ib_pause_when_hidden = false ; (5) pour remplacer la piste d'une langue, of_remove_subtitles puis of_add_subtitles.
11.27 pdfviewer : of_refresh dit ce qu'il fait, rien ne se télécharge à votre insu #
La seconde revue de la visionneuse (pdfviewer) donne un code de retour à of_refresh — et, pour garder le même nom au même sens, à celui du webbrowser et du shellexplorer —, dit tout lien du document, et refuse ce qui ne s'affichera jamais au lieu de laisser attendre. Aucune signature d'événement ne change.
| Quoi | 3.0 | 4.0 |
|---|---|---|
pdfviewer · webbrowser · shellexplorer : of_refresh | subroutine | long : 0 une fois demandé, -4 sans document ou sans page (pdfviewer, webbrowser), -2 si le composant n'est pas créé |
pdfviewer : of_refresh après un refus (http://…) | ne faisait rien, sans événement | le refus est redit : ue_load_failed |
pdfviewer : une adresse data: de plus de 2 Mo de caractères | jamais affichée, aucun événement | ue_load_failed avec REASON_TOO_LARGE, tout de suite |
| pdfviewer : un PDF distant servi « en téléchargement » | ue_load_failed, mais le fichier partait dans Téléchargements | ue_load_failed avec REASON_REFUSED, rien n'est téléchargé |
pdfviewer : ue_link_clicked sur un lien mailto:, file: ou relatif | jamais levé (seuls http et https) ; un lien relatif arrivait en adresse interne | levé pour tout lien ; as_url est un mailto:… tel quel, ou un chemin disque pour un fichier local |
pdfviewer : of_print_to_pdf | écrivait la page qui encadre le lecteur, pas le document | -4, rien n'est écrit : copiez le fichier de is_source |
pdfviewer : file://localhost/C:/…, file:////serveur/partage/… | un chemin faux, introuvable | C:\… et \\serveur\partage\… |
pdfviewer : le focus donné au composant (Tab, of_focus_webview) | restait sur la page : les touches ne faisaient pas défiler le document | passe au document : PageDown, flèches, Début / Fin le font défiler |
La recette : (1) un appel uo_pdf.of_refresh() compile tel quel ; testez son retour si vous voulez savoir qu'il n'y avait rien à relire ; (2) un document de plus de 1,5 Mo tenu en mémoire s'écrit dans un fichier temporaire, puis s'affiche par son chemin ; (3) dans ue_link_clicked, attendez aussi un mailto: et un chemin disque, pas seulement une adresse web ; (4) pour copier le document affiché, copiez le fichier de is_source plutôt qu'of_print_to_pdf.
11.28 codeeditor : une recherche qui se voit, un « aller à » qui déplace le curseur, remplacer #
La seconde revue de l'éditeur de code (codeeditor) amène à l'écran ce que la recherche trouve, garde le curseur de l'utilisateur pendant une recherche, renomme ii_doc_line et ajoute ce qu'un éditeur de script attend : remplacer, piloter le curseur, savoir s'il faut enregistrer, montrer une erreur dans la gouttière. Aucune signature d'événement ne change.
| Quoi | 3.0 | 4.0 |
|---|---|---|
codeeditor : ii_doc_line | un long au préfixe d'integer | il_doc_line : renommez-le ; une ligne au-delà du texte ne marque plus rien (elle se relit telle qu'écrite) |
codeeditor : of_find avec ib_search_enabled = false | 0, sans rien chercher | -4 |
codeeditor : of_find("") | relançait la recherche précédente | vide le champ et efface les surlignages, sans ue_find_result |
codeeditor : of_find, Entrée dans la barre | l'occurrence restait souvent hors de la vue ; le curseur allait sur elle | l'occurrence est amenée à l'écran ; la recherche part du curseur, qui ne bouge qu'à la fermeture de la barre (Échap) |
codeeditor : of_insert_text barre de recherche ouverte | remplaçait l'occurrence trouvée, hors de la vue, et vidait l'historique de Ctrl+Z | insère au curseur de l'utilisateur, amené à l'écran ; Ctrl+Z l'annule |
codeeditor : un nouveau is_text | gardait le défilement de l'ancien document ; un texte en CR seul se relisait en LF | s'affiche depuis sa première ligne et efface les marqueurs ; CR seul se relit en CR (un mélange en CRLF) |
codeeditor : ib_current_line avec ib_wrap | aucune bande | la bande couvre toute la ligne du curseur |
| codeeditor : ajouts | — | remplacer (of_replace, of_replace_all, Ctrl+H) et options de recherche (ib_find_match_case, ib_find_whole_word, ib_find_regex, F3) ; curseur et sélection (il_caret_line, il_caret_column, of_select_range, of_selected_text, of_line_count) ; ib_modified ; marqueurs de gouttière (of_add_marker, of_remove_marker, of_clear_markers, of_marker_count) |
La recette : (1) remplacez ii_doc_line par il_doc_line ; (2) testez le -4 d'of_find si la recherche peut être coupée ; (3) pour « aller à la ligne » d'une erreur en déplaçant le curseur, posez il_caret_line (et of_add_marker pour la montrer) — il_doc_line ne fait que marquer ; (4) si vous suiviez ue_caret_changed pour savoir où un extrait a été inséré, lisez il_caret_line après of_insert_text ; (5) un bouton Enregistrer peut suivre ib_modified, que vous remettez à false après l'enregistrement.
11.29 jsontree et xmltree : le clavier reste à l'arbre, une recherche qui ne déplace plus la sélection #
La seconde revue de l'arbre JSON (jsontree) a porté sur le moteur qu'il partage avec l'arbre XML (xmltree) : ce qui suit vaut pour les deux, sauf mention. Aucune signature ne change ; l'arbre JSON gagne of_expand_path, of_collapse_path, of_is_expanded, of_copy et l'événement ue_node_toggled, levé par un geste de l'utilisateur comme par of_expand_path et of_collapse_path.
| Quoi | 3.0 | 4.0 |
|---|---|---|
| jsontree, xmltree : Entrée sur une feuille | transmise à la fenêtre : son bouton par défaut partait | gardée par l'arbre, lève ue_node_clicked comme un clic ; sans sélection, rien |
| jsontree, xmltree : Échap, zone de recherche ouverte | le bouton Annuler de la fenêtre partait | ferme d'abord la zone ; zone fermée, Échap revient à la fenêtre |
jsontree, xmltree : lignes fermantes (}, ], </x>) | atteintes par les flèches (ue_selection_changed d'un bloc déjà annoncé) ; un clic ramenait la vue sur l'ouvrante | sautées par le clavier ; un clic sélectionne le bloc sans déplacer la vue |
| jsontree, xmltree : Flèche droite sur une feuille | descendait d'une ligne | ne fait rien, comme dans un arbre |
| jsontree, xmltree : une recherche pendant une sélection | of_search_next ou Échap pouvaient replier la ligne choisie et déplacer la sélection sans événement | la ligne sélectionnée reste visible et sélectionnée : of_selected_path ne change pas |
jsontree : le message de ue_error | moitié traduit, moitié anglais (« JSON invalide : Unexpected end of JSON (line 1, column 6) ») | entièrement dans la langue d'affichage, ligne et colonne comprises : ne comparez pas son texte |
| jsontree, xmltree : un document de plus de 1 500 000 lignes | affiché, mais sa fin hors d'atteinte, sans rien dire | refusé : un message dans l'arbre et ue_error |
| jsontree : Ctrl+C dans l'arbre | ne copiait rien, et partait comme raccourci de la fenêtre | copie la sélection (comme of_copy) ; sans sélection, le raccourci reste à la fenêtre |
La recette : (1) si une fenêtre comptait sur Entrée dans l'arbre pour valider son bouton par défaut, ajoutez ce geste dans ue_node_clicked ; (2) ne cherchez plus de clé vide dans ue_selection_changed (une fermante n'est plus sélectionnée) ; (3) n'analysez pas le texte de ue_error : il suit la langue ; (4) pour rétablir les plis d'un utilisateur, lisez of_is_expanded et reposez-les par of_expand_path / of_collapse_path.
11.30 xmltree : chaque ligne a son XPath, tout XPath se sélectionne #
La seconde revue de l'arbre XML (xmltree) reprend tout ce que la section précédente dit du moteur partagé. Elle ajoute ce qui est propre au XML : un chemin qui désigne TOUJOURS le nœud de la ligne, lisible tel quel par n_pbt_xml. Aucune signature ne change ; l'arbre XML gagne of_selected_xml, of_copy, of_expand_path, of_collapse_path, of_is_expanded et l'événement ue_node_toggled.
| Quoi | 3.0 | 4.0 |
|---|---|---|
| xmltree : chemin d'une ligne de texte, de commentaire ou d'instruction de traitement | celui de son élément (alors que of_selected_value rendait son texte à elle) ; "" avant la racine | le sien : /r/text()[2], /r/comment()[1], /r/processing-instruction('x')[1], /comment()[1] — n_pbt_xml.of_get_value y lit la même valeur |
| xmltree : élément dont le préfixe est redéclaré avec un autre URI, ou qui a un frère de même nom dans un autre espace de noms | un chemin qui ne trouvait rien, ou un AUTRE nœud | *[local-name()='x' and namespace-uri()='urn:…'], qui désigne ce nœud-là |
xmltree : of_select_path | seulement la forme exacte que l'arbre écrit, sinon -5 | tout XPath (//line[@sku='A'], /r/a[1]…) : le premier nœud trouvé est sélectionné, of_selected_path rend ensuite le chemin de l'arbre |
xmltree : le message de ue_error | « XML invalide : error on line 1 at column 11: … » ; l'arbre restait vide | « XML invalide : ligne 1, colonne 11 (…) », dans la langue d'affichage ; l'arbre montre la source, la ligne fautive marquée |
xmltree : <?xml-stylesheet?> sans déclaration XML | affiché deux fois | une seule ligne |
n_pbt_xml : un XPath avec @xml:lang | refusé (préfixe xml inconnu), valeur vide | lu : le préfixe xml est lié d'office, comme le veut la norme |
| xmltree : Flèche droite sur une balise à attributs | les attributs ne s'atteignaient qu'à la souris | parcourt les attributs un par un (Flèche gauche revient), un ue_selection_changed par pas, puis descend |
| xmltree : Ctrl+C, plier par chemin | rien ; Ctrl+C partait comme raccourci de la fenêtre | of_selected_xml, of_copy et Ctrl+C (le XML de la sélection), of_expand_path, of_collapse_path, of_is_expanded (tout XPath), ue_node_toggled (un geste, ou of_expand_path / of_collapse_path) |
La recette : (1) si vous remontiez d'une ligne de texte ou de commentaire à son élément par of_selected_path, prenez le parent du chemin (ce qui précède le dernier /) ; (2) n'analysez pas le texte de ue_error : il suit la langue ; (3) un chemin mémorisé en 3.0 sous des espaces de noms mêlés se retrouve en le redonnant à of_select_path, qui accepte désormais tout XPath, puis en relisant of_selected_path.
11.31 xml : des refus qui se disent, des espaces de noms qui tiennent #
La seconde revue de n_pbt_xml ne change aucune signature. Elle rend of_remove fidèle aux codes de la bibliothèque, refuse une lecture tant qu'un document bâti a un élément ouvert, et fait tenir les espaces de noms : ce qu'un mutateur écrit se relit dans le bon espace, ou se refuse en le disant.
| Quoi | 3.0 | 4.0 |
|---|---|---|
xml : of_remove(""), of_remove sans document chargé, page qui ne répond pas | 0, comme « rien retiré » | -5 ; -4 ; -2 — le compte (>= 0) seulement quand la suppression a été tentée |
xml : une lecture (of_exists, of_count, of_get_value, of_is_valid…) ou une modification pendant qu'un élément bâti est encore ouvert | le document était chargé tel quel, et la suite de la construction commençait un NOUVEAU document : ce qui précédait était perdu sans erreur | refusée : -4, vide ou false, et is_last_error = « the document is still being built : close its elements first » ; rien n'est perdu (of_xml et of_pretty restent permis) |
xml : of_add_xml sous un élément à espace de noms par défaut, ou avec le préfixe de la cible (<s:Item/> sous s:Body) | le fragment restait hors espace de noms (xmlns=''), invisible pour le XPath qui avait servi à l'ajouter ; le préfixe de la cible était refusé (-4) | le fragment hérite des espaces de noms en vigueur à sa cible, comme of_add_child |
xml : of_set_attr / of_add_child avec un nom préfixé (xsi:type, p:item) | 0, un attribut hors espace de noms que le XPath ne trouvait pas ; avec un préfixe non déclaré, un document qui ne se rechargeait plus | dans l'espace de noms du préfixe (déclaré au nœud, ou lié par of_register_namespace) ; -4 et « undeclared prefix : xsi » s'il n'est lié nulle part |
xml : of_pretty après un of_add_child sous un parent préfixé (s:Body) | <Item> sans préfixe : relu, hors espace de noms | <s:Item>, comme dans of_xml |
xml : of_element_attr deux fois du même nom ; un nom :x, x: ou a:b:c | deux attributs (document mal formé) ; nom accepté (document qui ne se charge pas) | un seul attribut, avec la seconde valeur ; nom refusé (is_last_error), l'élément laissé de côté |
xml : of_transform | applique une feuille XSLT au document chargé | retiré : XSLT n'existe plus dans le moteur (Chromium le retire) ; transformez côté serveur ou en PowerScript |
La recette : (1) un of_remove testé = 0 (rien retiré) marche toujours ; testez < 0 (rien n'a pu être tenté) ; (2) si vous lisiez un document PENDANT sa construction (un of_exists pour contrôler, un of_count pour numéroter), fermez d'abord ses éléments, ou tenez le compte vous-même ; (3) un fragment de of_add_xml n'a plus à redéclarer l'espace de noms de sa cible : celui qui écrivait xmlns='' pour en sortir le peut toujours ; (4) testez le retour de of_set_attr / of_add_child avec un nom préfixé, et déclarez le préfixe (ou liez-le par of_register_namespace).
11.32 json : un tableau qui se bâtit vite, des nombres qui gardent leurs chiffres, un texte vide qui se dit vide #
La seconde revue de n_pbt_json ne retire ni ne change aucune signature. Elle ajoute les surcharges decimal et longlong d'of_set_number et d'of_add_number, et les lecteurs of_get_decimal et of_get_longlong. Bâtir un tableau par of_add_* ne reparcourt plus tout le tableau à chaque ajout : 2 000 lignes se bâtissent en une fraction de seconde au lieu de plusieurs minutes. Deux règles sont désormais écrites dans la doc, sans changer : une chaîne nulle s'écrit "" (pour un null JSON, of_set_raw(path, "null")), et une clé écrite deux fois est lue à sa PREMIÈRE occurrence.
| Quoi | 3.0 | 4.0 |
|---|---|---|
json : of_load d'un texte fait de blancs (fin de ligne, tabulation) | -4, comme un texte malformé | -5, comme un texte vide |
json : of_load d'un texte précédé d'un BOM (fichier UTF-8 avec BOM) | -4 | 0, le BOM est ignoré |
json : of_load d'un document imbriqué sur plus de 512 niveaux | 0 — et un texte très profond pouvait arrêter l'application | -4, l'application reste vivante |
json : of_set_number / of_add_number d'un decimal (ou d'un littéral comme 690.50) | converti en double : une quinzaine de chiffres, un centime pouvait se perdre | surcharge decimal : tous les chiffres, zéros finaux ôtés (690.5) ; un longlong a aussi la sienne |
json : of_get_number d'un exposant à zéros de tête (1E-0000005) | 0, ou une valeur géante | la bonne valeur (0.00001) |
tous les composants : \u suivi de quatre caractères qui ne sont pas hexadécimaux, dans une chaîne lue | les quatre caractères disparaissaient | gardés tels quels, comme un échappement inconnu |
La recette : (1) si vous testiez of_load(...) = -4 pour reconnaître un fichier vide, testez < 0 ; (2) un montant decimal passé à of_set_number s'écrit désormais avec tous ses chiffres, sans rien changer à votre code ; (3) un identifiant de plus de 15 chiffres se relit par of_get_longlong, un montant par of_get_decimal, plus par of_get_number.
11.33 crosstab : of_from_datastore, des refus qui se disent, la valeur affichée au double-clic #
La seconde revue du crosstab renomme of_set_data en of_from_datastore, le nom du datagrid et du scheduler qui lisent un DataStore de la même façon (mêmes arguments, mêmes codes). Tout ajout, filtre ou format qui nomme un champ inconnu rend désormais -5 au lieu de 0. Le tableau ne dessine plus que les colonnes visibles : un code client ou une date au jour posés en Colonnes ne figent plus l'écran. Nouveautés : is_thousands, CF_ICONS, les constantes VALUEFILTER_* et DATE_*.
| Quoi | 3.0 | 4.0 |
|---|---|---|
| crosstab : alimenter depuis un DataStore | of_set_data(ds) | of_from_datastore(ds), mêmes codes |
| crosstab : un champ inconnu (faute de frappe) passé à un ajout, un filtre, un format, un ordre, un groupement de date | 0, rien ne se passe | -5 |
crosstab : un type de filtre, une agrégation, une partie de date hors liste ; une mesure calculée en Lignes ; of_set_value_filter à une position sans mesure | 0 ; la position glissait sur la dernière mesure | -5 |
crosstab : of_set_layout d'un texte qui n'est pas du JSON ; of_remove_calc_field / of_remove_calc_measure d'un nom inconnu | 0 | -5 |
crosstab : ad_value de ue_cell_double_clicked | l'agrégat brut (400 sous « 80,0 % »), 0 pour une cellule vide | la valeur affichée (0.8), NULL pour une cellule vide ; un membre vide vaut null dans les tuples |
| crosstab : un même champ en Lignes (ou Colonnes, Filtres) ET en Valeurs | dépendait de l'ordre des appels : la valeur disparaissait ; « Retirer » d'une dimension retirait tout | permis partout ; ajouter ou déplacer une dimension garde les valeurs, « Retirer » ne retire que la puce visée |
| crosstab : un membre vide (NULL, texte vide) ; un texte avec tabulation ou retour à la ligne dans le DataStore | « null » ou une ligne sans libellé ; des lignes fantômes, des colonnes décalées | un seul membre (vide), en dernier ; le texte reste dans sa ligne |
crosstab : is_label après un nouvel of_from_datastore ; is_label = "" | perdu ; une puce sans texte | gardé ; "" rend l'en-tête du DataWindow |
| crosstab : Ctrl+C en mode démo | ue_copy levé, presse-papiers écrit | refusé comme un export, la grille le dit |
crosstab : of_collapse_all ; filtre de membres de la grille ; of_export_xlsx au-delà de 16 384 colonnes ; exports en VALUES_ROWS | les lignes seulement ; appliqué à chaque case ; un fichier illisible ; les mesures côte à côte | lignes et colonnes ; appliqué par OK (10 000 membres listés au plus) ; refusé, ue_xlsx_saved dit pourquoi ; comme à l'écran |
La recette : (1) remplacez of_set_data par of_from_datastore ; (2) si vous ignoriez le code rendu par of_add_*_field ou un filtre, vérifiez < 0 — une faute de frappe se voit enfin ; (3) dans ue_cell_double_clicked, testez IsNull(ad_value) avant de calculer, et relisez ad_value comme la valeur affichée (une part, pas une somme, en mode « % »).
11.34 Socle : of_count et of_keys_at comptent en long #
of_count rendait un integer et of_keys_at prenait un rang integer — sur tous les composants, sur leurs poignées et sur les objets non visuels qui les portent (commandpalette, messagebox, speechout, toaster). Au-delà de 32 767 éléments, le compte débordait : un calendrier (scheduler) alimenté par un DataStore de 40 000 lignes ne se comptait plus, et ses dernières lignes ne se parcouraient pas. Les quatre signatures sont désormais en 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 ).
La recette : un appel ne change pas. Une variable integer qui reçoit of_count() reste juste jusqu'à 32 767 : déclarez-la long. Un descendant qui surchargeait of_count ou of_keys_at reprend la nouvelle signature — le compilateur vous montre la ligne.
11.35 shellexplorer : un rafraîchissement qui garde l'état, chaque ligne pour elle #
La revue de l'explorateur du shell garde ses événements : of_select, of_expand et of_collapse les lèvent comme un clic, comme en 3.0. of_refresh et ib_show_files ne referment plus l'arbre. Nouveautés : is_file_filter (les fichiers montrés), ib_show_hidden (les éléments masqués, par défaut le réglage de l'Explorateur), ib_enabled et of_refresh(chemin), qui relit une seule branche.
| Quoi | 3.0 | 4.0 |
|---|---|---|
shellexplorer : of_refresh(), ou ib_show_files changé | l'arbre se refermait, la sélection disparaissait sans événement | les branches ouvertes et la sélection reviennent, retrouvées par leur chemin ; une sélection qui n'existe plus lève ue_selected("", "") |
shellexplorer : of_expand, of_select, of_collapse d'un chemin vide | 0 | -5 |
shellexplorer : of_expand d'un fichier ou d'un dossier sans enfant | rien | ue_path_not_found(…, "expand", "not a branch") |
shellexplorer : of_keys_at(chemin, i) | chemin/enfant, que rien ne relisait | le chemin de l'enfant, entier : il se réinjecte dans of_has, of_select, of_expand |
| shellexplorer : un même dossier affiché à deux niveaux (la Musique du Bureau et celle du dossier de l'utilisateur) | un clic, un chevron ou un double-clic sur l'une agissait sur l'autre | chaque ligne répond pour elle-même |
| shellexplorer : fichiers et dossiers masqués | jamais montrés | montrés selon le réglage de l'Explorateur du poste, ou ib_show_hidden |
| shellexplorer : une branche sur un partage qui ne répond pas | « … » pour toujours, même refermée et rouverte | ue_error « … : timeout » au bout de 30 secondes ; la branche se redemande à sa réouverture |
La recette : (1) ue_selected et ue_expanded sont levés aussi après un of_select ou un of_expand de votre code, une fois le dossier atteint (un chemin encore à lire arrive un peu plus tard) : faites votre travail dans l'événement, pas juste derrière l'appel, ou relisez of_selected_key() une fois la sélection arrivée ; (2) dans ue_selected, traitez as_path = "" (la sélection a disparu) ; (3) testez -5 sur un chemin vide ; (4) posez ib_show_hidden = false si vos utilisateurs ne doivent jamais voir les éléments masqués, quel que soit leur Explorateur.
11.36 commandpalette : chaque objet ne touche qu'à sa palette, une fermeture qui se dit toujours, la langue de l'application #
Il n'y a qu'une palette à l'écran à la fois : quand un second objet palette ouvre la sienne, elle remplace la première. Un objet ne répond plus désormais que de sa palette et de son accord. ue_opened et ue_closed restent levés sur of_open / of_close : ils disent le cycle de vie de la fenêtre, pas un geste (même règle que les lecteurs audio et vidéo). Nouveautés : of_insert_command (une commande à un rang choisi), of_anchor_under (la palette sous un contrôle, en pixels écran), et sur la poignée is_group, is_hint, is_keywords, is_icon.
| Quoi | 3.0 | 4.0 |
|---|---|---|
commandpalette : ue_closed | sans argument ; rien du tout quand la palette mourait avant de paraître (fermée ou remplacée aussitôt, fenêtre refusée) | ue_closed(string as_reason) suit chaque of_open qui a rendu 0 : "", cancelled, failed ou blocked |
commandpalette : of_is_open, of_close, of_reset, la destruction d'un objet dont la palette a été remplacée par celle d'un autre | répondaient VRAI pour la palette de l'autre, et la fermaient | ne répondent que de leur palette : FAUX, et rien n'est fermé |
| commandpalette : détruire (ou remettre à zéro) un objet dont l'accord a été repris par un autre objet palette de la même fenêtre | retirait l'accord de l'autre : la touche ne répondait plus nulle part | l'accord de l'autre reste |
commandpalette : of_register_shortcut d'une touche que le crochet ne nomme pas (ctrl+ù, win+k, space sans modificateur) | 0 (« posé »), et la touche ne faisait jamais rien | -5 ; l'accord précédent est gardé |
commandpalette : of_register_shortcut avec is_shortcut vide et aucun accord posé | 0 | 2 |
commandpalette : of_add_command d'une clé qui contient une virgule | 0 — et is_recent la relisait comme deux commandes | -5 |
| commandpalette : un groupe déclaré en plusieurs fois (File, Edit, puis File) | deux en-têtes « File » | un seul, à la place de sa première apparition ; l'ordre des commandes est gardé dans le groupe |
| commandpalette : la recherche et les accents | preferences ne trouvait pas « Préférences » | les accents ne comptent pas |
| commandpalette : la langue | la palette parlait anglais quelle que soit PBT_SetLanguage | la langue de l'application ; is_placeholder posé par vous n'est pas traduit |
| commandpalette : le raccourci affiché sur une ligne, clavier AZERTY ; AltGr dans la recherche | Ctrl+A se lisait Ctrl+Q ; taper € (AltGr+E) lançait la commande montrée en Ctrl+Alt+E | la touche est lue comme la DLL la nomme ; un caractère tapé n'est jamais un accord |
commandpalette : un accord posé sans ipo_owner (toute l'application) avant l'accord d'une fenêtre, sur la même touche | gagnait aussi dans cette fenêtre | l'accord de la fenêtre passe d'abord |
commandpalette : POSITION_ABSOLUTE | une commande de clé x ou y ramenait la palette au bord de l'écran ; sur deux écrans, elle allait sur celui de la fenêtre | il_x / il_y tiennent, sur l'écran du point demandé |
commandpalette : inv_pump | variable interne restée publique | protégée : elle ne fait pas partie de l'API, rien à lire ni à écrire |
commandpalette : une commande dont le texte cite __license | la palette ne s'ouvrait pas (-5) | s'ouvre : seul un mot réservé écrit en clé reste refusé |
La recette : (1) si votre code déclenche lui-même ue_closed(), passez-lui une raison ("") ; un script qui ne fait que le recevoir compile tel quel ; (2) si vous attendiez ue_opened après of_open, traitez aussi ue_closed("cancelled"), "failed", "blocked" ; (3) testez 2 et -5 au retour d'of_register_shortcut ; (4) renommez une clé de commande qui contient une virgule.
11.37 radialmenu : une clé nue qui vise juste, une roue ouverte qui suit l'application, un libellé qui se lit #
La roue est dessinée une fois, et l'application continue de tourner pendant qu'elle est à l'écran : ce que vous changez entre-temps compte désormais. Le moyeu n'épelle plus rien (il ne l'avait jamais fait depuis la 3.0, contrairement à ce que disait la documentation) : c'est la branche pointée qui montre son libellé entier.
| Quoi | 3.0 | 4.0 |
|---|---|---|
radialmenu : of_item avec une clé nue (of_item("csv")) | changeait le libellé et l'état de la seule branche qui la portait, mais ses couleurs ne se dessinaient pas, et la poignée survivait au retrait du parent | est résolue en l'adresse complète (import/csv) : même poignée, couleurs comprises, libérée avec le parent. Ambiguë ou inconnue : la poignée ne change rien |
| radialmenu : une poignée sur une adresse pas encore ajoutée | une couleur posée par elle colorait la branche ajoutée ensuite à cette adresse, même après of_clear | n'écrit rien ; of_clear oublie toutes les couleurs |
radialmenu : of_show | ne rendait rien | rend un long : 0, ou -2 si le composant n'est pas créé |
radialmenu : of_show(-1, -1) | ouvrait la roue au curseur | l'ouvre au point (-1, -1), comme toute coordonnée négative ; au curseur, c'est of_show() |
| radialmenu : une branche retirée, vidée, grisée ou masquée pendant que la roue est ouverte | se choisissait encore : ue_item_selected pour une branche disparue | ne se choisit plus : ue_dismissed ; of_clear, of_remove_item et of_reset ferment la roue ouverte |
| radialmenu : clic droit ailleurs pendant que la roue est ouverte | la nouvelle roue remplaçait l'ancienne sans ue_dismissed | ue_dismissed pour l'ancienne, puis la nouvelle roue |
| radialmenu : libellé long ou vide | coupé à deux lignes et lisible nulle part ; vide, une tranche sans rien | la branche pointée le montre entier ; vide, la clé s'affiche |
| radialmenu : lecture de droite à gauche | la couronne tournait dans le sens horaire, ← remontait | la couronne tourne dans l'autre sens, ← et → s'échangent |
La recette : (1) of_show rend un long : un appel seul compile tel quel ; (2) remplacez of_show(-1, -1) par of_show() ; (3) si vous défaisiez dans ue_dismissed ce que vous posiez à l'ouverture, rien à faire : il arrive désormais aussi quand une roue en remplace une autre ; (4) une poignée obtenue par une clé portée par deux branches ne change plus rien : donnez l'adresse.
11.38 toaster : des événements qui disent quel toast, des refus qui se disent, un toast qui attend d'être vu #
Les trois événements du toast changent de signature, of_add_button rend un code, et un toast ne se perd plus quand l'application est réduite.
| Quoi | 3.0 | 4.0 |
|---|---|---|
toaster : ue_toast_clicked, ue_toast_action, ue_toast_dismissed | (string as_key …) : as_key portait l'identifiant du toast, en texte | (long al_id, string as_key …) : al_id est le nombre rendu par of_show, as_key le is_key du toast (vide sans clé) |
toaster : of_add_button | rendait le nombre de boutons ; une clé déjà prise, une clé vide et un quatrième bouton passaient sans un mot | rend 0 ; -5 sans rien ajouter pour une clé vide, déjà prise, qui contient / ou une barre verticale, ou un quatrième bouton. Le nombre se lit par of_count() |
toaster : of_show avec un réglage hors bornes | montrait quand même : une nature inconnue en information, un coin inconnu en haut à gauche | rend -5 et ne montre rien : is_kind, is_position ou is_sound hors de leurs constantes, il_max_visible hors de 0 à 20, il_timeout sous TIMEOUT_AUTO |
toaster : la même is_key montrée par deux toasters | le second remplaçait le toast du premier et lui prenait ses événements ; une mise à jour gardait le coin d'origine | chaque toaster a le sien ; une mise à jour reprend aussi le coin, l'ancrage à l'écran et la fenêtre d'ancrage |
| toaster : le son | un son système à chaque toast, information et succès compris ; aucun pour un toast sorti de la file d'attente | is_sound : SOUND_AUTO (défaut) = erreur et avertissement seulement, SOUND_ALWAYS, SOUND_NEVER ; joué quand le toast est demandé, même s'il attend sa place |
toaster : toasts ancrés à l'écran depuis deux fenêtres, ou sans ipo_owner | se posaient exactement l'un sur l'autre ; le plafond se comptait par fenêtre | une seule pile par moniteur et par coin, et il_max_visible compte par moniteur |
| toaster : un toast ancré à une fenêtre réduite | partait à -32 000 px et s'éteignait sans avoir été vu | attend : il ne s'affiche pas et son compte à rebours ne tourne pas avant le retour de la fenêtre |
| toaster : un toast qui arrive pendant que la pile est survolée | décomptait son temps et pouvait partir pendant la lecture | naît en pause, comme les autres |
toaster : PBT_SetLanguage, PBT_SetDefaultTheme, PBT_SetDefaultThemeAccent, PBT_SetDefaultFont, PBT_SetFlowDirection pendant qu'un toast est affiché | ne l'atteignaient pas ; la taille de police restait 11 pt | le suivent aussitôt, taille de police comprise |
| toaster : le texte d'un toast | ne se sélectionnait pas ; saisir la barre de défilement d'un long texte pouvait répondre et fermer | se sélectionne et se copie (clic droit, Copier) ; la sélection et la barre de défilement ne ferment plus le toast |
toaster : il_button_count | variable publique | privée : lisez le nombre de boutons par of_count() |
toaster : un toast dont le texte cite __license | ne s'affichait pas | s'affiche : seul un mot réservé écrit en clé reste refusé |
toaster : of_close d'un toast affiché | aucun événement | ue_toast_dismissed, comme sa croix ; un toast encore en attente, jamais vu, ne lève rien |
La recette : (1) ajoutez long al_id en premier argument de vos ue_toast_clicked, ue_toast_action et ue_toast_dismissed, et remplacez Long(as_key) par al_id ; as_key est désormais la clé du toast ; (2) si vous lisiez le retour de of_add_button comme un nombre de boutons, lisez of_count() ; (3) pour garder un son sur une information ou un succès, posez is_sound = SOUND_ALWAYS.
11.39 messagebox : un champ obligatoire vide n'est jamais validé, des choix aux clés uniques #
Cinq comportements changent ; aucune signature.
| Quoi | 3.0 | 4.0 | |
|---|---|---|---|
messagebox : ib_input_required et un bouton par défaut à compte à rebours (of_add_button_timed) | le clic automatique validait la boîte champ vide (of_show rendait le bouton, of_input_value une chaîne vide) | le compte à rebours s'épuise sans cliquer ; le bouton attend que le champ soit rempli | |
| messagebox : Échap ou Alt+F4 sur un bouton à la fois par défaut et d'annulation, champ obligatoire vide | rendait 1, comme un clic sur ce bouton | rend 0 : fermée sans choix | |
messagebox : of_add_choice | acceptait une clé déjà prise, ou qui contient / ou ` | ` | rend -5 et n'ajoute rien |
messagebox : of_choose sans aucun choix | montrait une boîte à un seul bouton Annuler | ne montre rien : chaîne vide, et of_get_last_error dit « no choice to show » | |
messagebox : les boutons des boîtes prêtes (of_info, of_confirm, of_yes_no, of_prompt…) quand l'application n'a posé aucune langue | « Ok » | « OK », le mot du catalogue anglais (« Cancel », « Yes », « No » pour les autres) |
La recette : (1) un champ obligatoire se valide désormais à la main — si vous comptiez sur le clic automatique pour avancer, donnez une valeur par défaut au champ (is_input_value) ; (2) donnez à chaque choix une clé unique, sans / ni |, et testez -5 si vos clés viennent de données.
11.40 Socle : la langue annoncée par défaut, les nombres décimaux de tout poste #
Deux corrections du socle, valables pour tous les composants ; aucune signature ne change.
| Quoi | 3.0 | 4.0 |
|---|---|---|
la langue annoncée par un composant (<html lang>) | le français, tant que l'application ne choisissait pas de langue | l'anglais, la langue par défaut des composants ; la langue choisie par l'application la remplace toujours |
un nombre décimal donné à une propriété (id_*) | seule la virgule décimale était convertie : sur un poste dont le séparateur décimal est un autre caractère, la valeur n'arrivait pas | le séparateur décimal du poste, quel qu'il soit, est converti |
La recette : rien à changer. Une application qui choisit sa langue la voit toujours appliquée ; une lecture d'écran qui lisait en français un composant d'une application anglaise le lit désormais en anglais.
11.41 Clavier, focus et place : ce que les tests de la 4.0 ont redressé #
Des comportements vus en jouant les composants dans une vraie application ; aucune signature ne change, une méthode s'ajoute.
| Quoi | 3.0 | 4.0 |
|---|---|---|
Entrée sur un CommandButton PowerBuilder qui a le focus | rien (la touche se perdait) | le bouton est pressé (clicked), comme dans une boîte de dialogue Windows ; le bouton par défaut d'une barre ne l'est pas |
Un composant désactivé (ib_enabled = false) | Tab s'arrêtait dessus | il sort de l'ordre de tabulation ; réactivé (ou of_reset), il y reprend sa place |
Un raccourci Ctrl + un caractère sans touche à lui (AZERTY : - sur la touche 6, + avec Maj) | Ctrl+- ne trouvait pas son accord | après la touche, le caractère de la disposition active : Ctrl+-, Ctrl++, Ctrl+,, Ctrl+. partent sur tout clavier |
| La frappe dans un menu ouvert (recherche, lettres soulignées) | une touche de la rangée du haut donnait son chiffre (AZERTY : - arrivait en 6) | le caractère de la disposition ; une lettre soulignée &1 répond toujours à la touche 1 |
Le calendrier du sélecteur de date (toolbar) | à la souris seulement | au clavier aussi : flèches (jour, semaine), Page préc./suiv. (mois), Début/Fin, Entrée choisit, Échap ferme |
| Alt avec deux rubans (ou barres de menus) dans la même fenêtre, sans sous-fenêtre entre eux | le premier créé | le plus proche du focus à l'écran |
breadcrumb dans une barre très étroite | le premier segment ne cédait jamais : le lieu courant tombait à rien, le fil débordait | après le milieu, le lieu courant cède jusqu'à une lettre et « … », puis le premier segment ; rien ne sort de la barre |
menubar : un choix fait à la souris dans le déroulant | le focus restait sur la barre (Édition > Coller ne collait nulle part) | le focus revient au contrôle qui l'avait |
ribbon : taper dans une zone de liste puis choisir dans sa liste | deux ue_combo_changed : le texte tapé, puis le choix | un seul, le choix |
toolbar : is_text = "" posé dans ue_text_changed, le focus dans le champ | l'ancien texte revenait, avec un second événement | le champ reste vide |
| Le focus rendu à un composant après une fenêtre de la bibliothèque (palette de commandes) | la page, sans élément : le curseur de l'éditeur de code avait disparu | l'élément qui l'avait |
Un libellé à lettre soulignée (&File) en droite-à-gauche | les lettres en désordre (« ileF ») | le mot se lit dans l'ordre |
n_pbt_utils.of_dir_folders(as_dir, ref as_names[]) | DirList dans une ListBox : 4,5 s pour 30 000 dossiers, et le dossier courant de l'application changeait | nouveau : les sous-dossiers comme l'Explorateur les montre, triés, en une passe ; -4 illisible, -5 chemin vide |
breadcrumb : of_add_children(as_keys, as_child_keys[], as_texts[]) | une branche à la fois (of_add_child) : 30 000 sous-dossiers ouvraient le menu en plus de 20 s | nouveau : toute une rangée en un appel |
La recette : rien à changer. Une application dont un CommandButton focalisé devait ignorer Entrée le rend inactif (Enabled = false) ou lui retire le focus ; pour un dossier volumineux, remplacez la boucle d'of_add_child par un seul of_add_children.
11.42 Données et clavier : ce que le second lot de tests de la 4.0 a redressé #
Des comportements vus en jouant les composants de données dans une vraie application ; aucune signature ne change.
| Quoi | 3.0 | 4.0 |
|---|---|---|
| Entrée ou Échap frappées dans un composant qui ne les prend pas | perdues : le bouton Default ou Cancel d'un CommandButton PowerBuilder ne partait pas | la touche revient à la fenêtre PowerBuilder, comme le promet le chapitre 6.4 : Échap presse le bouton Cancel, Entrée le bouton Default |
PBT_SetLanguage suivi aussitôt d'un ordre au composant | le texte d'un événement levé par cet ordre (ue_error de xmltree) restait dans l'ancienne langue | la langue vaut dès l'ordre suivant |
jsontree, xmltree : le focus donné au composant (of_focus_webview, la touche Tab) | restait sur la page : les flèches et Entrée n'allaient nulle part | l'arbre le prend ; F3 et Maj+F3 passent à l'occurrence suivante ou précédente de la recherche |
xmltree : une erreur signalée APRÈS le dernier caractère de sa ligne | aucune colonne soulignée | la fin de la ligne est marquée (↵) |
codeeditor en retour à la ligne (ib_wrap) | la bande il_doc_line pouvait se poser une rangée trop haut, sur la fin de la ligne précédente | les bandes couvrent toutes les rangées de leur ligne et suivent le redimensionnement |
codeeditor : « Tout remplacer » et of_replace_all sur un long texte | plusieurs secondes quand le focus était dans l'éditeur ; au-delà, of_replace_all pouvait rendre 0 | instantané, et le nombre de remplacements est rendu ; Ctrl+H avec une recherche vide met le curseur dans « Rechercher » |
crosstab : « Ajouter aux colonnes » (ou aux lignes, aux filtres) depuis le menu d'une puce, au clavier | le focus se perdait ; seule Entrée ouvrait le menu d'une puce | le focus suit la puce dans sa nouvelle zone ; Maj+F10 et la touche Menu ouvrent aussi son menu |
json : des milliers de of_add_* à la suite | près d'une seconde pour 2 000 lignes | quelques dizaines de millisecondes ; le document est le même |
La recette : rien à changer. Une application qui traitait elle-même Échap frappée dans un composant (un raccourci enregistré par of_register_shortcut) le garde : un raccourci passe avant la fenêtre.
11.43 Contenu et médias : ce que le troisième lot de tests de la 4.0 a redressé #
Des comportements vus en jouant les composants de contenu et de médias dans une vraie application ; aucune signature ne change.
| Quoi | 3.0 | 4.0 |
|---|---|---|
speechout : of_speak puis of_pause dans le même script | ue_started et la première ue_sentence arrivaient avant ue_paused : la voix était annoncée partie | ue_started est levé quand la voix se met réellement à parler ; une pause posée avant la tient : rien n'est dit ni annoncé jusqu'à of_resume |
speechout : une langue sans voix exacte sur le poste (fr-CA là où il n'y a que fr-FR) | ue_started annonçait la langue demandée (fr-CA) | ue_started annonce la langue réellement lue (fr-FR), après ue_voice_fallback |
webbrowser : Tab après le dernier contrôle de la barre d'adresse | le focus quittait le composant pour la fenêtre PowerBuilder | le focus entre dans le site, comme dans un navigateur ; Maj+Tab depuis le site revient à la barre |
webbrowser : ib_private = true posé alors que la navigation est déjà privée | sans effet : le site retrouvait ce qu'il avait gardé dans la session privée | une session privée neuve et vide à chaque fois |
video en plein écran : une boîte modale de l'application (un MessageBox dans ue_ended) | cachée sous le film, qui lui reprenait le clavier : l'application semblait figée | le film cède : la boîte passe devant et reçoit le clavier ; à sa fermeture, le film reprend l'écran et Échap en sort |
tilesbox : une flèche au bord, à côté d'une tuile plus petite | Flèche haut sur une grande tuile du premier rang sautait de côté, sur la petite voisine | seule une tuile entièrement au-delà du bord compte : au bord, le focus reste en place |
dockcontainer : il_back_color / il_text_color d'un panneau empilé | perdues dès que son onglet était actif (il prenait l'accent du thème) | l'onglet actif garde les couleurs du panneau, comme sa languette sur le rail |
progressbar : id_value posé à chaque tour d'une longue boucle | environ 2,5 s pour 20 000 lignes | bien moins d'une seconde ; la barre avance toujours pendant la boucle |
La recette : rien à changer. Une application qui comptait sur ue_started pour savoir qu'of_speak avait été accepté lit plutôt son code retour (0) ; celle qui compare la langue de ue_started à is_lang apprend désormais le repli par cette différence, en plus de ue_voice_fallback.
11.44 Données : lire une cellule, trier et chercher plus finement (json, jsontree, xmltree, crosstab) #
Ces ajouts de la 4.0 ne retirent aucune signature. Deux changent ce que vous voyez sans que vous ayez rien écrit : Ctrl+C du crosstab copie désormais les en-têtes, et ue_cell_double_clicked reçoit un argument de plus.
| Quoi | 3.0 | 4.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 est la position de la valeur double-cliquée dans Valeurs, à partir de 1 ; avec les deux tuples, c'est l'adresse que relit of_get_cell_value |
crosstab : Ctrl+C sur des cellules | les chiffres seuls | les en-têtes avec : les noms des colonnes sur une première ligne, le membre de chaque ligne (son chemin, « North / Boston ») dans une première colonne ; ib_copy_headers = false rend les chiffres seuls |
crosstab : of_remove_field, of_get_cell_value | — | nouveaux : retirer un champ de toutes les zones sans reconstruire la disposition ; lire la valeur d'une cellule par ses tuples et la position de sa mesure |
json : of_get_array, of_pretty (long al_indent) | — | nouveaux : tous les éléments d'un tableau en un appel ; une indentation en espaces, ou le document compact sur une ligne avec 0 |
jsontree, xmltree : la recherche | toujours sans casse, et à l'intérieur des mots | ib_find_match_case et ib_find_whole_word, comme le codeeditor, avec les bascules Aa et ab de la zone de recherche ; par défaut, la recherche reste celle de la 3.0 |
La recette : un script de ue_cell_double_clicked n'a rien à changer, l'argument s'ajoute avant la valeur. Une application qui recolle le résultat de Ctrl+C dans un traitement qui n'attend que des chiffres pose ib_copy_headers = false.
11.45 Navigation et conteneurs : onglets au clavier, panneaux au double-clic (tab, dockcontainer, ribbon, stepbar, listbar) #
Ces ajouts de la 4.0 ne retirent aucune signature. Trois gestes changent ce que voit un utilisateur sans que vous ayez rien écrit : le clic du milieu et Ctrl+Tab sur un tab, et le double-clic sur un panneau de dockcontainer ou sur la barre de titre de sa fenêtre flottante.
| Quoi | 3.0 | 4.0 |
|---|---|---|
tab : clic du milieu sur un onglet | rien | ferme un onglet fermable, par le même chemin que sa croix : ue_tab_closing si ib_veto_close = true, puis ue_tab_closed ; un onglet non fermable ou désactivé ne fait rien |
tab : Ctrl+Tab, Ctrl+Maj+Tab, Ctrl+Page suivante / précédente | rien | l'onglet suivant ou précédent qui peut devenir actif, en boucle, que le clavier soit dans la bande ou dans une page hébergée (un contrôle PowerBuilder, un autre composant) ; c'est un geste : ue_selection_changing si ib_veto_selection = true, puis ue_selection_changed. Depuis un contrôle hors du tab, la touche reste à la fenêtre |
dockcontainer : double-clic sur un en-tête ou un onglet ; sur la barre de titre d'une fenêtre flottante | rien ; la fenêtre flottante s'agrandissait | détache le panneau dans une fenêtre flottante (ue_panel_floated) ; sur la barre de titre de cette fenêtre, le raccroche à sa place (ue_panel_docked), comme sa croix |
dockcontainer : clic droit sur un en-tête ou un onglet | rien | un menu Détacher, Fermer, Fermer les autres (les autres panneaux fermables du même groupe) ; chaque fermeture pose sa propre question |
dockcontainer : ib_veto_close, ue_panel_closing, of_close_panel, n_pbt_dock_panel.ib_closable | — | nouveaux : la question avant la fermeture d'un panneau (facultative, false par défaut), la fermeture par le code (-4 si vous la refusez, -5 pour le panneau principal ou un panneau non fermable), et la possibilité de fermer changée en direct |
ribbon : n_pbt_ribbon_item.is_image, id_min, id_max, id_step | fixés à l'ajout | l'image d'un contrôle et la plage d'un compteur se changent en direct et se relisent ; la valeur d'un compteur est ramenée dans sa nouvelle plage sans lever ue_value_changed, comme id_value |
stepbar : STATE_WARNING, STATE_SKIPPED, NAV_VISITED | — | nouveaux : une étape passée avec un avertissement (pastille ambre), une étape ignorée (anneau en pointillés), et un mode où toute étape déjà atteinte reste cliquable, même en avant après un retour |
listbar : n_pbt_listbar_section.ib_pinned | — | nouveau : une section épinglée en bas de la barre (Paramètres, Aide), qui reste visible quand la barre défile, dans le rail aussi ; la disposition enregistrée garde l'ordre des sections |
La recette : rien à changer. Dans une page de tab, Ctrl+Tab change désormais d'onglet même quand le focus est dans un contrôle de la page, comme dans une boîte de propriétés de Windows. Pour agrandir une fenêtre flottante du dockcontainer, son bouton Agrandir reste là.
11.46 Menus : le chevron de la barre de menus, une liste déroulante dans la barre d'outils, dix mille commandes (menubar, toolbar, commandpalette) #
Ces ajouts de la 4.0 ne retirent aucune signature. Un comportement par défaut change : une barre de menus trop étroite ne revient plus à la ligne, elle replie ses derniers titres dans un chevron. Et of_open d'une palette de commandes ne refuse plus de s'ouvrir sans ipo_owner ni ipo_receiver.
| Quoi | 3.0 | 4.0 |
|---|---|---|
menubar : une barre trop étroite pour ses titres | revenait à la ligne : sa hauteur suivait les rangées, ue_auto_height à chaque changement de largeur | reste sur une ligne : les titres qui ne tiennent pas, depuis la fin, passent dans le déroulant d'un chevron (leurs entrées en cascade, Alt + leur lettre les ouvre depuis le chevron, les flèches s'y arrêtent comme sur un titre, à gauche en RTL) ; la hauteur ne change plus. ib_wrap = true rend l'ancien comportement |
menubar : ib_track_hover, ue_item_hover, of_add_header, n_pbt_menubar_menu.is_align | — | nouveaux : l'entrée pointée dans un déroulant, pour l'aide d'une barre d'état (facultatif, false par défaut ; une adresse vide à la fermeture), des en-têtes de section dans un déroulant, et un menu calé au bout de la barre (ALIGN_END, Aide) |
toolbar : of_add_combo, of_add_combo_item, of_remove_combo_item, of_clear_combo, ue_combo_changed, n_pbt_toolbar_item.ib_editable | — | nouveaux : une liste déroulante dans la barre, modifiable ou non, que l'on garde vivante repliée dans le chevron ; son event porte le nom et le sens de celui du ruban |
commandpalette : deux palettes posées sur le même ipo_owner | partageaient leur file d'événements (l'une pouvait recevoir le ue_closed de l'autre), et la seconde reprenait l'accord de la première | chacune ses événements et son accord ; un même accord posé par la seconde lui revient (1). of_open et of_register_shortcut ne rendent plus -5 sans ipo_owner ni ipo_receiver : les événements arrivent toujours |
commandpalette : des milliers de commandes | chaque ligne dessinée : l'ouverture et la frappe ralentissaient | seules les lignes visibles sont dessinées : 10 000 commandes s'ouvrent et se filtrent sans attente |
| Raccourcis de toute la bibliothèque : AltGr + une touche qui écrit un caractère (l'euro d'un clavier français) | lançait le raccourci Ctrl+Alt de cette touche, dans un champ PowerBuilder aussi | le caractère est tapé, aucun raccourci ne part ; AltGr seul ne donne plus le clavier à une barre de menus ou à un ruban |
La recette : une barre de menus dont la hauteur variable était voulue pose ib_wrap = true ; une application qui repositionnait ses contrôles dans ue_auto_height à chaque redimensionnement n'a plus rien à faire après le premier. Un raccourci de l'application posé sur Ctrl+Alt + une lettre que le clavier de l'utilisateur écrit avec AltGr ne part plus pendant qu'il tape : choisissez une autre touche.
11.47 Contenu et médias : un journal ligne à ligne, la taille réelle d'une image, l'attente d'un film (statictext, picture, video) #
Ces ajouts de la 4.0 ne retirent aucune signature et ne changent aucun défaut. Deux comportements se précisent : le zoom d'une image peut dépasser son plafond pour atteindre la taille réelle, et un film d'une URL lu sans réseau le dit.
| Quoi | 3.0 | 4.0 |
|---|---|---|
statictext : of_append_text, ib_follow_end | — | nouveaux : un journal qui s'écrit ligne à ligne — seule la ligne ajoutée part vers le composant — et, sur option, la vue qui reste en bas tant que le lecteur ne remonte pas |
statictext : ib_auto_width, ue_auto_width | — | nouveaux : l'userobject prend la largeur de son texte (facultatif, false par défaut), comme ib_auto_height le fait pour la hauteur |
picture : id_max_zoom, la touche 1 | le zoom s'arrêtait à 8.0 fois le cadrage, quoi qu'il arrive : un grand scan affiché petit ne se lisait jamais pixel pour pixel | le plafond se règle (id_max_zoom, 8.0 par défaut) ; la touche 1 montre la taille réelle, et le plafond s'élève jusqu'à elle quand elle demande davantage |
video : ue_buffering, of_is_buffering | un film qui attendait ses données restait figé sans rien dire | un anneau à l'accent du thème tourne sur l'image, et ue_buffering(true) puis ue_buffering(false) encadrent l'attente |
video : un film d'une URL alors que le poste n'a pas de réseau | « ce fichier est introuvable ou illisible », ue_failed avec REASON_UNSUPPORTED | « pas de connexion réseau » sur l'image, ue_failed avec REASON_NETWORK |
La recette : rien à changer. Un journal qui réécrivait is_text à chaque ligne passe à of_append_text ; un code qui testait REASON_UNSUPPORTED pour un film d'une URL hors réseau teste REASON_NETWORK.
11.48 Revue du 03/10/2026 : une disposition remise se dit, un export vers un chemin relatif tombe au bon endroit (toolbar, crosstab) #
Aucune signature ne change. Deux comportements s'alignent sur la règle de la bibliothèque : tout changement de disposition se dit, et un chemin relatif d'écriture se résout comme pour les autres exports.
| Quoi | 3.0 | 4.0 |
|---|---|---|
toolbar : of_set_layout | aucun événement | lève ue_layout_changed, comme le dockcontainer et le datagrid : tout changement de disposition se dit, qu'il vienne de l'utilisateur ou de votre code |
crosstab : of_export_csv, of_export_xlsx vers un chemin relatif | écrit dans le dossier courant du moment — qu'un DirList ou une boîte de fichiers avait pu déplacer ; ue_csv_saved / ue_xlsx_saved rendaient le chemin tel que donné | écrit dans le dossier où l'application a démarré, comme les autres exports natifs ; l'event rend le chemin complet ; un chemin qui dépend du dossier courant d'un lecteur (\x, C:x) est refusé |
La recette : une application qui enregistre la disposition dans ue_layout_changed réenregistre la même valeur après of_set_layout — sans conséquence. Un export qui comptait sur le dossier courant du moment donne un chemin complet.