PBToolboxAI v4 ← Site

4. Thèmes et apparence #

← Socle commun · Sommaire · Langue et RTL →


4.1 Les thèmes : deux axes #

Un thème se compose d'un style et d'un mode :

AxeValeurs
Style (is_theme_style)fluent · metro · office · office2007 · office2003
Mode (is_theme_mode)light · dark

Soit dix thèmes, nommés <style>-<mode> : fluent-light, fluent-dark, office2007-light, metro-dark…


4.2 Le thème par défaut de l'application (recommandé) #

Posez le thème une fois pour toute l'application, avant l'ouverture de la première fenêtre. Il est injecté dans chaque composant avant son premier rendu : aucun flash de style clair sur une application sombre.

// Event open de l'objet application
PBT_SetDefaultTheme("fluent-dark")
PBT_SetDefaultThemeAccent(RGB(0, 120, 212))   // optionnel

Le changement à chaud est possible à tout moment : tous les composants déjà ouverts se re-thèment instantanément.

Changer de thème par défaut ne touche pas à l'accent de l'application : celui posé par PBT_SetDefaultThemeAccent reste, d'un thème à l'autre, jusqu'à ce que vous en posiez un autre ou -1.

// Bascule clair / sombre depuis un bouton de l'application
PBT_SetDefaultTheme("fluent-light")
FonctionEffet
PBT_SetDefaultTheme (string as_name)Thème par défaut du processus, diffusé à tous les composants ; un nom inconnu est refusé (-5) et le thème précédent reste
PBT_GetDefaultTheme ( ) → stringThème par défaut courant
PBT_SetDefaultThemeAccent (long al_accent)Accent de l'application, que suivent tous les composants — ceux à thème local compris, tant qu'ils n'ont pas le leur ; -1 le retire (chaque thème reprend son accent) ; une couleur système PowerBuilder (au-delà de 0xFFFFFF) est refusée (-5)
PBT_GetDefaultThemeAccent ( ) → longAccent de l'application, -1 si aucun n'est posé
PBT_SetDefaultFont (string as_family, long al_size_px)Police de toute l'application ; taille en pixels, 0 = celle du thème (voir 4.4)

4.3 Le thème d'un composant précis #

Un composant peut s'écarter du thème de l'application, axe par axe :

// Le style seul : le mode reste celui de l'application, et le suit
uo_editor.is_theme_style = uo_editor.THEME_STYLE_OFFICE2007

// Les deux axes : un theme entierement local
uo_editor.is_theme_mode  = uo_editor.THEME_MODE_DARK
uo_editor.il_theme_accent = RGB(200, 60, 40)     // -1 = accent de l'application

// Revenir au theme de l'application : les deux axes vides
uo_editor.is_theme_style = ""
uo_editor.is_theme_mode  = ""
PropriétéTypeDéfautRôle
is_theme_stylestring""Style visuel (constantes THEME_STYLE_*). Vide = le style de l'application, suivi à chaque changement
is_theme_modestring""Variante claire ou sombre (constantes THEME_MODE_*). Vide = le mode de l'application, suivi à chaque changement
il_theme_accentlong-1Couleur d'accent de ce composant. -1 = l'accent de l'application, ou celui du thème si l'application n'en pose pas

Un of_reset() rend les deux axes et l'accent à l'application : le composant suit de nouveau son thème et son accent.

Les deux axes sont indépendants : un axe laissé vide suit le thème de l'application à chacun de ses changements, pas seulement celui du moment où l'autre axe a été posé. Espaces et majuscules ne comptent pas ; une valeur inconnue ("office2010", "sombre") est ignorée et l'axe garde sa valeur. Relire is_theme_style ou is_theme_mode rend ce que le composant affiche — le style et le mode de l'application pour un axe vide —, et il_theme_accent rend -1 tant que le composant n'a pas d'accent à lui. Un composant à thème local suit lui aussi l'accent de l'application tant qu'il n'a pas le sien.

💡 Le plus soigné reste un seul thème pour toute l'application. Réservez le thème local aux cas particuliers (une zone volontairement contrastée, une prévisualisation de thème).


4.4 Recolorer un composant, un groupe ou un item #

Trois portées, les mêmes propriétés. Rien à nommer, rien à deviner.

// Le composant entier
uo_ribbon.il_theme_accent = RGB(0, 120, 90)

// Un groupe : tout ce qu'il contient suit
uo_ribbon.of_group(/*keys*/ "home/clipboard").il_accent = RGB(0, 120, 90)

// Un item
uo_list.of_item(/*keys*/ "delete").il_text_color = RGB(200, 70, 70)
uo_list.of_item(/*keys*/ "delete").il_back_color = RGB(255, 235, 235)

// Les memes, sous le pointeur
uo_list.of_item(/*keys*/ "delete").il_back_color_hover = RGB(255, 220, 220)

// Revenir a la couleur du composant
uo_list.of_item(/*keys*/ "delete").il_text_color = -1
PropriétéOùCe qu'elle recolore
il_theme_accentle composantson accent, et tout ce qui en dérive : le survol et l'appui des boutons à l'accent, les sélections et les états cochés teintés, le texte lisible dessus, le fond applicatif, le souligné d'onglet
il_accentun handle d'item, de groupe, d'onglet, de barrece que cette zone peint à l'accent, descendants compris
il_back_color · il_text_coloridemle fond et le texte de l'item
il_back_color_hover · il_text_color_hoveridemles mêmes, sous le pointeur

-1 remet la couleur que donne le composant, elle-même issue du thème. La couleur d'un item survit à la reconstruction du composant : elle est portée par une règle de style visant l'item, pas par une propriété posée sur l'élément du moment. of_reset() efface tout.

il_accent ne repeint que ce que la zone peint avec l'accent — une sélection, un souligné actif, une barre de progression. Un composant qui n'y touche pas n'en montrera rien : pour « cette entrée en rouge », il_back_color et il_text_color sont les bons outils, lus par tous les composants à items.

La police de toute l'application #

PBT_SetDefaultFont("Segoe UI", 14)

Un seul appel habille chaque composant vivant et ceux créés ensuite — la police leur est injectée avant leur premier affichage. Une famille vide ou une taille de 0 rend cette moitié au thème. La taille s'exprime en pixels.


4.5 Le fond du composant remonte à PowerBuilder #

Chaque composant peint son fond selon le thème, puis notifie sa couleur : l'userobject adopte cette couleur (backcolor) et lève ue_bg_color, pour que la fenêtre et les contrôles PowerBuilder voisins s'accordent.

// event ue_bg_color d'un composant
parent.backcolor = al_color
st_title.backcolor = al_color

C'est ce qui permet de mélanger composants PBToolboxAI et contrôles PowerBuilder natifs sans démarcation visible en thème sombre.


4.6 Les images et les icônes #

Partout où un composant attend un chemin d'image (icône de bouton, tuile, [picture=…]…), quatre formes sont acceptées :

FormeExempleUsage
Fichierimg\logo.pngImage telle quelle (png, jpg, gif, bmp, ico, svg, webp)
Ressource de DLLimg\packimages.dll:RIBBONImage packagée dans une DLL de ressources
mono:mono:img\save.svgAplat à la couleur du thème : seule la forme compte
tint:tint:img\logo_couleur.pngDuotone : le relief interne module la couleur du thème

La forme chemin.dll:nom charge une ressource d'une DLL d'images (façon packimages.dll), ouverte en lecture seule (LOAD_LIBRARY_AS_DATAFILE, aucun code exécuté). Cela évite d'expédier des centaines de fichiers en vrac.

Affichage instantané : of_icon #

Un petit glyphe passé par of_icon() est incorporé dans la commande (aucun aller-retour de chargement) : il apparaît dès le premier rendu, sans le clignotement d'une icône chargée après coup.

n_pbt_utils lnv_utils   // autoinstantiate : rien a creer, rien a detruire

uo_toolbar.of_add_button(/*keys*/ "main/save", /*text*/ "Enregistrer", /*image*/ lnv_utils.of_icon(/*path*/ "mono:img\save.svg"), /*tooltip*/ "Enregistrer")

Pour un lot d'icônes connu d'avance, of_preload_icons() réchauffe le cache en une seule fois, au démarrage : le premier affichage n'attend alors plus rien.

Transparent à l'usage : au-delà d'une certaine taille, of_icon renvoie le chemin d'origine (l'image est alors chargée puis mise en cache normalement).


4.7 Le texte riche à balises #

N'importe quel libellé de n'importe quel composant accepte un balisage façon BBCode : titre d'onglet, libellé de bouton, texte de barre d'état, message de toast, titre de panneau, texte d'info-bulle…

Les entrées des menus intégrés suivent la même règle — menu contextuel d'un onglet, liste ··· des onglets qui ne tiennent plus, menus de colonne d'une grille : le libellé qu'affiche le menu est celui du contrôle, balises comprises.

Le texte est rendu en nœuds de texte et en <span> : aucune injection HTML n'est possible.

BaliseEffet
[b] [i] [u] [s] / [strike]Gras, italique, souligné, barré
[sub] [super]Indice, exposant
[red]…[/red] (couleurs nommées)Couleur de texte (red, green, blue, orange, teal…)
[accent]…[/accent]Couleur d'accent du thème courant
[color=#rrggbb] / [color=accent]Couleur de texte
[bk=#rrggbb] / [backcolor=accent]Couleur de fond
[font=Consolas]Police
[size=14]Taille absolue, en points (6 à 200)
[size+=30] / [size-=20]Taille relative en % (20 % par défaut)
[picture=chemin] / [picture=chemin,larg,haut]Image en ligne. Accepte aussi ce que rend of_icon() (une data URI) ; les dimensions se lisent en fin de valeur. Un chemin réseau y est refusé (voir plus bas)
[symbol=nom]Symbole intégré, monochrome, dessiné dans la couleur du texte qui l'entoure (il suit le thème, le survol, un [accent]) — aucun fichier à livrer : clock, location, person, people, calendar, repeat, lock, bell, phone, mail, video, note, tag, link, check, star, info, warning. Un nom inconnu s'affiche tel quel
[br] / [linebreak] / [br:3]Saut de ligne (ou n sauts)
[gap=N]Saut de ligne suivi d'un blanc de N % d'une ligne : [gap=100] vaut [br][br], [gap=50] une demi-ligne vide
[separator]Filet horizontal
[hyperlink=url]…[/hyperlink]Zone cliquable : le lien s'ouvre toujours dans le navigateur de l'utilisateur, dans tous les composants. L'event ue_hyperlink(as_url) est levé en plus, pour les composants qui l'exposent
[action=id]…[/action]Zone cliquable → event ue_action(as_key), présentée comme un lien
[invisibleaction=id]…[/invisibleaction]Zone cliquable → ue_action, sans le style lien
[bullet]…[/bullet]Puce : élément de liste dont les lignes suivantes s'alignent sur la première, et non sous le marqueur (retrait pendant). [bullet=-] change le marqueur
[foldarea:Titre]…[/foldarea]Bloc repliable : en-tête cliquable (− / +) au-dessus d'un contenu indenté. Le titre accepte les balises
[foldarea-closed:Titre]…[/foldarea]Le même bloc, replié à l'affichage
[[ / ]]Échappement : [[b]] affiche [b] sans l'interpréter
uo_text.is_text = "Bienvenue sur [b][accent]PBToolboxAI[/accent][/b] [size-=20]v1.0[/size-=20]" &
                   + "[br]Consultez la [hyperlink=https://pbtoolboxai.net]documentation[/hyperlink]."

uo_tab.of_add_page(/*key*/ "clients", /*title*/ "[b]Clients[/b] [size-=20](128)[/size-=20]", /*page*/ uo_clients)

uo_st.is_text = "La balise [[b]] met en [b]gras[/b]"   // affiche : La balise [b] met en gras

Afficher une donnée telle quelle. Une valeur venue de votre base peut contenir une balise connue — [b], [red], [picture=…] : elle serait interprétée (un mot inconnu entre crochets, lui, s'affiche tel quel). of_escape_markup(), fonction de n_pbt_utils, les double pour vous — enveloppez la donnée, jamais le balisage que vous écrivez vous-même.

n_pbt_utils lnv_utils   // autoinstantiate : rien a creer, rien a detruire
// Une donnee metier peut contenir une balise CONNUE : sans echappement, elle
// est INTERPRETEE -- le [b] disparait et la suite passe en gras.
ls_label = "Remise [b]VIP"
uo_st.is_text = "Client : " + ls_label                          // affiche : Client : Remise VIP (VIP en gras)
uo_st.is_text = "Client : " + lnv_utils.of_escape_markup(/*text*/ ls_label)  // affiche : Client : Remise [b]VIP

Un texte sans balise n'a aucun surcoût (chemin rapide). Une balise inconnue s'affiche telle quelle (Solde [net] reste Solde [net]). Une fermante ne ferme que sa balise, et une fermante sans ouvrante est ignorée. Un [hyperlink] s'ouvre partout — libellé, titre d'onglet, panneau de barre d'état, toast, boîte de dialogue : c'est le socle qui s'en charge. L'event ue_action, lui, n'est émis que par les composants de texte interactifs (statictext) ; ailleurs, [action] sert au seul formatage.

Seuls http, https et mailto sont ouverts, quelle que soit leur casse (HTTPS:// aussi). Un libellé transporte souvent une donnée venue de votre base : confier un schéma quelconque au système transformerait un libellé en lanceur de programmes. Pour la même raison, une image du balisage ne va jamais chercher un partage réseau ([picture=\\serveur\partage\x.png] est refusée) : un commentaire non échappé ferait sinon ouvrir une session réseau vers une machine quelconque au seul affichage. Une image réseau dans un texte passe par of_icon(), qui l'embarque ; is_picture et les icônes des composants gardent l'accès au réseau.

Un [foldarea] est un bloc : il occupe toute la largeur et se replie d'un clic sur son en-tête, sans aller-retour avec PowerBuilder. Les blocs s'imbriquent, et lorsque le composant suit la hauteur de son contenu (ib_auto_height), cette hauteur est renotifiée à chaque repli. Le titre est lui-même du texte à balises : rien n'est mis en gras à votre place, [foldarea:[b]Total[/b]] s'en charge.


← Socle commun · Sommaire · Langue et RTL →

4.8 Les animations et le réglage du poste #

Windows offre un réglage d'accessibilité — Paramètres > Accessibilité > Effets visuels > Effets d'animation — et les composants l'honorent : quand il est désactivé, aucune image-clé et aucune transition ne joue. Le graphique arrive à sa place, il n'y va pas.

C'est le bon comportement par défaut, et il ne se discute pas : quelqu'un qui a demandé moins de mouvement à son système le pensait. ib_animated = true n'y change rien.

Une application peut malgré tout insister :

// A declarer une fois : Function long PBT_SetAnimationPolicy (long al_policy)
//                       Library "pbtoolboxai.dll"
PBT_SetAnimationPolicy(1)   // 1 = toujours animer, 0 = respecter le poste (defaut)

L'appel vaut pour tout le processus et peut se faire à n'importe quel moment : les composants vivants suivent aussitôt, les suivants la reçoivent à leur ouverture.

Ne posez 1 que si votre application a une vraie raison de passer outre — une borne, un écran mural, une démonstration dont le métier est justement de montrer ces animations. Pour une application de gestion, laissez le défaut.


4.9 Composer l'apparence de votre application #

La bibliothèque livre dix thèmes et ne permet pas à une application d'en définir un onzième : le vocabulaire de jetons est interne, et il le reste. Ce qu'elle offre à la place tient en trois leviers, qui se combinent — c'est ainsi qu'on obtient « nos couleurs » sans écrire un thème.

// 1. LA BASE : le thème livré le plus proche de la cible.
PBT_SetDefaultTheme("office-light")

// 2. L'ACCENT : UNE couleur habille tous les composants, y compris ceux
//    créés ensuite, et tout ce que le thème en dérive.
PBT_SetDefaultThemeAccent(RGB(0, 105, 92))

// 3. LA POLICE de toute l'application, en un appel.
PBT_SetDefaultFont("Segoe UI Semibold", 0)

Posez ces trois lignes dans l'event open de l'objet application : elles atteignent chaque composant avant son premier rendu, donc sans le moindre clignotement.

LevierPortéeCe qu'il change
PBT_SetDefaultThemele processusle style et le mode : formes, arrondis, épaisseurs, toute la palette
PBT_SetDefaultThemeAccentle processusl'accent, et ce que le thème en dérive — survol et appui des boutons à l'accent, sélection, texte lisible dessus, souligné d'onglet ; composants à thème local compris, et il survit à un changement de thème
PBT_SetDefaultFontle processusla famille et la taille ; une famille vide ou une taille 0 rend cette moitié au thème
il_theme_accentun composantson accent à lui, quand une fenêtre doit se distinguer
il_back_color · il_text_colorun itemune entrée précise, en rouge parce qu'elle supprime (voir 4.4)

Ce que cela ne permet pas #

Redéfinir la palette complète — les gris de surface, les bordures, le rayon des arrondis — n'est pas offert. Un thème est un ensemble cohérent d'une soixantaine de valeurs qui se répondent : en ouvrir la moitié produirait des combinaisons illisibles que personne n'aurait vérifiées. Si votre charte demande davantage que ces trois leviers, écrivez-nous : un thème de plus dans la bibliothèque est une option, votre thème dans votre code n'en est pas une.

Dans l'application de démonstration : ruban Home > Apparence > Style > Corporate (composed). L'entrée compose ces trois leviers — rien qui nous soit réservé — avec deux écarts : le thème est office-light ou office-dark selon le bouton clair/sombre du ruban, et la police est posée en 16 pixels. Le code est celui de wf_apply_style, dans la fenêtre w_demo_home.


4.10 Le contraste élevé de Windows #

Quand l'utilisateur active un thème Contraste élevé de Windows, le moteur remplace les couleurs de la page par celles du système, quel que soit le thème de la bibliothèque. Les composants en tiennent compte : les icônes monochromes (mono:, tint:) prennent la couleur du texte du système — celle du texte en surbrillance sur une ligne sélectionnée —, les anneaux de focus et les repères dessinés en ombre portée reçoivent un vrai contour, et ce qui porte un sens par sa couleur (une pastille d'état, une couleur choisie par l'application) la garde. Rien à faire côté application : ni propriété, ni appel.


4.11 Le contenu tiers : navigateur web et visionneuse PDF #

webbrowser et pdfviewer affichent un contenu que la bibliothèque ne dessine pas : un site, ou la visionneuse PDF du moteur. Ce contenu ne connaît pas les thèmes de la bibliothèque, mais il lit la préférence claire ou sombre que le navigateur lui annonce, comme un site lit celle de Windows. Cette préférence suit le thème par défaut de l'application (PBT_SetDefaultTheme) — pas le mode de Windows, pas le thème local d'un composant : une application en fluent-dark montre la version sombre d'un site qui en propose une, et la visionneuse PDF dans ses couleurs sombres. Avant que la page tierce ait peint, la zone prend le fond du thème au lieu d'un rectangle blanc.