# 6. Fonctions transverses #

← Langue et RTL · Sommaire · FAQ →

Ce chapitre regroupe les services disponibles quel que soit le composant : info-bulles, hébergement de contrôles PowerBuilder, glisser-déposer depuis l'Explorateur, focus clavier, export image, et les objets utilitaires non visuels.

## 6.1 Info-bulles simples et enrichies #

### Info-bulle du composant #

    uo_bouton.is_tooltip = "Enregistrer le dossier (Ctrl+S)"

Le texte accepte le balisage riche : `[b]`, `[br]`, `[color=…]`, `[picture=…]`…

### Super info-bulle (titre + texte + image) #

Pour une aide contextuelle façon ruban Office :

    uo_bouton.is_super_tooltip_title = "Enregistrer"
    uo_bouton.is_super_tooltip_text  = "Écrit les modifications sur le serveur." &
                                       + "[br][br][size-=15]Raccourci : Ctrl+S[/size-=15]"
    uo_bouton.is_super_tooltip_image = "img\help_save.png"

Les trois propriétés sont indépendantes : un titre seul, ou un titre + texte sans image, sont valides. Dès qu'une propriété `is_super_tooltip_*` est posée, elle prend le pas sur `is_tooltip`.

### Info-bulle d'un item #

Toute classe d'item (`n_pbt_item` et ses descendants) porte les quatre mêmes propriétés :

    uo_toolbar.of_bar("main").of_item("save").is_tooltip = "Enregistrer"

    uo_tab.of_item("clients").is_super_tooltip_title = "Clients"
    uo_tab.of_item("clients").is_super_tooltip_text  = "128 fiches, dernière synchro à 09:12"

### Comportement #

- L'info-bulle est une fenêtre native thémée, identique dans tous les composants (elle n'est pas limitée par les frontières du webview).
- Elle ne s'affiche pas si votre application n'a pas le focus : passer la souris au-dessus d'une fenêtre en arrière-plan ne déclenche rien.
- Elle disparaît quand vous basculez sur une autre application, et au plus tard au bout de 10 secondes.
- `of_reset()` supprime les info-bulles posées sur l'instance et sur ses items.

## 6.2 Héberger de vrais contrôles PowerBuilder #

Les composants conteneurs n'affichent pas du HTML à la place de vos écrans : ils accueillent de vrais contrôles PowerBuilder (userobjects, DataWindows, groupes de contrôles), reparentés comme fenêtres Win32 enfants. Vos écrans existants sont réutilisés tels quels.

    // Onglets : chaque page est un userobject PB
    uo_tab.of_add_page("clients",   "Clients",   uo_page_clients)
    uo_tab.of_add_page("factures",  "Factures",  uo_page_factures, /*fermable*/ true)

    // Panneaux dockables : idem
    uo_dock.is_main = "doc"                       // zone centrale
    uo_dock.of_add_panel("doc",      uo_dock.POSITION_STACK, "", 0,   "Document",    uo_editeur)
    uo_dock.of_add_panel("explorer", uo_dock.POSITION_START, "", 240, "Explorateur", uo_arbre)
    uo_dock.of_add_panel("props",    uo_dock.POSITION_END,   "", 260, "Propriétés",  uo_props)

Ce qui est pris en charge pour vous :

- le positionnement et le redimensionnement du contrôle hébergé quand l'onglet ou le panneau change de taille ;
- l'affichage / masquage au changement d'onglet ou de panneau actif ;
- les panneaux flottants, dockés ou masqués automatiquement (dockcontainer) ;
- la destruction propre à la fermeture de la fenêtre.

> ⚠️ Un contrôle hébergé est une fenêtre native : il se dessine au-dessus de la couche web. C'est voulu (votre DataWindow reste nette et rapide), mais cela signifie qu'aucun effet web — ombre, transparence, animation — ne peut passer par-dessus lui.

## 6.3 Recevoir des fichiers depuis l'Explorateur Windows #

Un composant peut devenir cible de dépôt pour des fichiers glissés depuis l'Explorateur. La bibliothèque installe une cible de dépôt native : vous recevez les chemins complets, ce qu'un dépôt HTML ne permet pas.

    uo_editeur.ib_allow_drop = true

    // event ue_drop_files de uo_editeur : (string as_files[])
    long ll_i

    for ll_i = 1 to UpperBound(as_files)
        of_ouvrir_fichier(as_files[ll_i])
    next

Event | Déclenché quand
--- | ---
`ue_drag_enter ( )` | Un glisser de fichiers entre dans le composant
`ue_drag_leave ( )` | Il en sort sans déposer
`ue_drop_files (string as_files[])` | Les fichiers sont déposés — chemins complets

Le composant fournit lui-même le retour visuel de survol (surbrillance de la zone de dépôt). Publié par les composants où le dépôt a du sens : `statictext`, `codeeditor`.

## 6.4 Clavier et focus #

Un composant PBToolboxAI participe à la navigation clavier de votre fenêtre comme un contrôle natif :

- la touche Tab l'atteint dans l'ordre de tabulation de la fenêtre ;
- les touches (flèches, Entrée, Échap, saisie) sont traitées par le composant qui a le focus ;
- les raccourcis de fenêtre (Entrée = bouton par défaut, Échap = annuler) continuent de fonctionner même quand le focus est dans un composant.

Pour donner explicitement le focus au contenu d'un composant (par exemple après avoir ouvert un panneau de recherche) :

    uo_editeur.of_focus_webview()

## 6.5 Exporter le rendu en image #

Rappel du socle commun : tout composant s'exporte tel qu'affiché.

    uo_pivot.of_save_as_png("C:\temp\tableau.png")
    uo_pivot.of_save_as_jpg("C:\temp\tableau.jpg")

Utile pour joindre un graphique à un e-mail, alimenter un état, ou tracer un incident utilisateur.

## 6.6 Boîtes de dialogue et notifications #

Deux services sans contrôle à poser dans la fenêtre :

    // Boite de dialogue modale themee (retour synchrone)
    n_pbt_messagebox lnv_mb

    lnv_mb = create n_pbt_messagebox
    lnv_mb.is_title   = "Suppression"
    lnv_mb.is_message = "Supprimer définitivement [b]12 dossiers[/b] ?"
    lnv_mb.is_icon    = lnv_mb.ICON_QUESTION
    lnv_mb.of_add_button("Supprimer", /*par defaut*/ false, /*annulation*/ false)
    lnv_mb.of_add_button("Annuler",   /*par defaut*/ true,  /*annulation*/ true)

    if lnv_mb.of_show(Handle(this)) = 1 then of_supprimer()
    destroy lnv_mb

    // Notification "toast" en coin d'ecran, non bloquante
    n_pbt_toaster lnv_toast

    lnv_toast = create n_pbt_toaster
    lnv_toast.is_title = "Import terminé"
    lnv_toast.is_text  = "1 240 lignes intégrées."
    lnv_toast.is_kind  = lnv_toast.KIND_SUCCESS
    lnv_toast.of_show()
    destroy lnv_toast

Détails complets : messagebox et toaster.

## 6.7 Objets utilitaires non visuels #

Livrés dans la même PBL, sans webview ni rendu : ce sont de simples enveloppes autour de la DLL. Créez-les, utilisez-les, détruisez-les.

### Encodage Base64 — `u_pbt_base64` #

    u_pbt_base64 lnv_b64
    string ls_encode

    lnv_b64 = create u_pbt_base64
    ls_encode = lnv_b64.of_encode("Bonjour")     // texte -> UTF-8 -> Base64
    ls_texte  = lnv_b64.of_decode(ls_encode)     // "" si l'entree est invalide
    destroy lnv_b64

### Empreintes — `u_pbt_hash` #

    u_pbt_hash lnv_hash

    lnv_hash = create u_pbt_hash
    ls_empreinte = lnv_hash.of_sha256("mot de passe")            // hexa minuscule
    ls_fichier   = lnv_hash.of_file("SHA256", "C:\livraison.zip")
    destroy lnv_hash

Méthode | Rôle
--- | ---
`of_string (string as_algo, string as_texte)` | Empreinte d'une chaîne — `MD5`, `SHA1`, `SHA256`, `SHA384`, `SHA512`
`of_file (string as_algo, string as_chemin)` | Empreinte du contenu d'un fichier (`""` si illisible)
`of_md5` · `of_sha1` · `of_sha256` · `of_sha512` | Raccourcis

### Expressions régulières — `u_pbt_regex` #

Syntaxe ECMAScript, absente de PowerScript.

    u_pbt_regex lnv_re
    string ls_trouves[]

    lnv_re = create u_pbt_regex

    if lnv_re.of_is_match("^[\w.]+@[\w.]+\.\w{2,}$", ls_email) then …

    ls_annee = lnv_re.of_match("(\d{4})-(\d{2})-(\d{2})", ls_date, /*groupe*/ 1)
    ls_propre = lnv_re.of_replace("\s+", ls_saisie, " ")
    ll_nb = lnv_re.of_match_all("[A-Z]{2}\d{6}", ls_texte, ls_trouves)

    destroy lnv_re

Méthode | Rôle
--- | ---
`of_is_match (motif, texte [, ab_ignore_case | al_flags])` | Le texte correspond-il ?
`of_match (motif, texte, ai_groupe [, al_flags])` | Première correspondance (`0` = tout, `1..` = groupe capturant)
`of_match_all (motif, texte [, ai_groupe, al_flags], ref as_res[])` | Toutes les correspondances ; renvoie le nombre
`of_replace (motif, texte, remplacement)` | Remplace toutes les occurrences (`$1`… autorisés)

Options combinables : `FLAG_IGNORE_CASE` (1), `FLAG_SINGLELINE` (2, le `.` traverse les sauts de ligne).

### Identifiants uniques — `u_pbt_guid` #

    u_pbt_guid lnv_guid

    lnv_guid = create u_pbt_guid
    ls_id = lnv_guid.of_new()          // 1b4e28ba-2fa1-11d2-883f-0016d3cca427
    ls_id = lnv_guid.of_new_braces()   // {1b4e28ba-...}
    ls_id = lnv_guid.of_new_plain()    // 1b4e28ba2fa111d2883f0016d3cca427
    destroy lnv_guid

### Archives ZIP — `u_pbt_zip` #

    u_pbt_zip lnv_zip

    lnv_zip = create u_pbt_zip
    lnv_zip.of_compress("C:\temp\livraison.zip", "C:\appli\export")   // fichier OU dossier
    lnv_zip.of_extract("C:\temp\livraison.zip",  "C:\appli\import")   // dossier cree si absent
    destroy lnv_zip

Les deux méthodes renvoient un `boolean`.

## 6.8 Informations sur la bibliothèque #

    // Version de la DLL
    string ls_version

    ls_version = Space(32)
    PBT_GetVersion(ls_version, 32)

Fonction globale | Rôle
--- | ---
`PBT_GetVersion (ref string, long)` | Version de la bibliothèque
`PBT_CheckRuntime (ref string, long)` | Version du runtime WebView2 installé (`≤ 0` = absent)
`PBT_GetLastErrorMessage (ref string, long)` | Dernier message d'erreur du processus
`PBT_LicenseStatus ( )` | État de la licence — voir Licence

## 6.9 Imprimer #

Tout composant sait s'imprimer, tel qu'affiché, sans DataWindow intermédiaire.

    // Silencieux : un PDF sur disque, rien à cliquer
    uo_grille.of_print_to_pdf("C:\etats\ventes.pdf")

    // Paysage, pour une grille large
    uo_grille.of_print_to_pdf(/*fichier*/ "C:\etats\ventes.pdf", /*paysage*/ true)

    // Avec dialogue : l'utilisateur choisit son imprimante et voit l'aperçu
    uo_grille.of_print()

    // Directement le dialogue système d'impression
    uo_grille.of_print(/*dialogue systeme*/ true)

Méthode | Rôle
--- | ---
`of_print_to_pdf (string as_path)` · `of_print_to_pdf (string, boolean ab_paysage)` | Écrit un PDF sans rien afficher. Ne rend la main qu'une fois le fichier écrit (`0` = fait)
`of_print ( )` · `of_print (boolean ab_dialogue_systeme)` | Ouvre le dialogue d'impression. Rend la main dès que le dialogue est ouvert : ce que l'utilisateur en fait ensuite lui appartient

> ⚠️ Ce qui est imprimé est ce qui est RENDU. Une grille virtualisée n'imprime que les lignes qu'elle détient : pour un état complet, passez d'abord par une mise en page qui les affiche toutes (pagination), ou exportez les données plutôt que le rendu.

Le code retour `-6` signale un runtime WebView2 trop ancien pour imprimer (1.0.1108 pour le PDF, 1.0.1587 pour le dialogue). Tout le reste suit les codes retour communs.

← Langue et RTL · Sommaire · FAQ →