# 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 :

Axe | Valeurs
--- | ---
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.

    // Bascule clair / sombre depuis un bouton de l'application
    PBT_SetDefaultTheme("fluent-light")

Fonction | Effet
--- | ---
`PBT_SetDefaultTheme (string as_nom)` | Thème par défaut du processus (diffusé à tous les composants)
`PBT_GetDefaultTheme ( )` → string | Thème par défaut courant
`PBT_SetDefaultThemeAccent (long al_couleur)` | Couleur d'accent par défaut (`-1` = accent natif du thème)
`PBT_GetDefaultThemeAccent ( )` → long | Accent par défaut courant

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

Un composant peut s'écarter du thème par défaut :

    uo_editeur.is_theme_style = uo_editeur.THEME_STYLE_OFFICE2007
    uo_editeur.is_theme_mode  = uo_editeur.THEME_MODE_DARK
    uo_editeur.il_theme_accent = RGB(200, 60, 40)     // -1 = accent du theme

Propriété | Type | Défaut | Rôle
--- | --- | --- | ---
`is_theme_style` | string | `fluent` | Style visuel (constantes `THEME_STYLE_*`)
`is_theme_mode` | string | `light` | Variante claire ou sombre (constantes `THEME_MODE_*`)
`il_theme_accent` | long | `-1` | Couleur d'accent de ce composant (`-1` = accent du thème)

Un `of_reset()` ramène le composant au thème par défaut du processus.

> 💡 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_ruban.il_theme_accent = RGB(0, 120, 90)

    // Un groupe : tout ce qu'il contient suit
    uo_ruban.of_tab("accueil").of_group("presse-papiers").il_accent = RGB(0, 120, 90)

    // Un item
    uo_liste.of_item("supprimer").il_text_color = RGB(200, 70, 70)
    uo_liste.of_item("supprimer").il_back_color = RGB(255, 235, 235)

    // Les memes, sous le pointeur
    uo_liste.of_item("supprimer").il_back_color_hover = RGB(255, 220, 220)

    // Revenir a la couleur du composant
    uo_liste.of_item("supprimer").il_text_color = -1

Propriété | Où | Ce qu'elle recolore
--- | --- | ---
`il_theme_accent` | le composant | son accent, et tout ce qui en dérive : le texte lisible dessus, le fond applicatif, le souligné d'onglet
`il_accent` | un handle d'item, de groupe, d'onglet, de barre | ce que cette zone peint à l'accent, descendants compris
`il_back_color` · `il_text_color` | idem | le fond et le texte de l'item
`il_back_color_hover` · `il_text_color_hover` | idem | les 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.

## 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_titre.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 :

Forme | Exemple | Usage
--- | --- | ---
Fichier | `img\logo.png` | Image telle quelle (png, jpg, gif, bmp, ico, svg, webp)
Ressource de DLL | `img\packimages.dll:RIBBON` | Image packagée dans une DLL de ressources
`mono:` | `mono:img\save.svg` | Aplat à la couleur du thème : seule la forme compte
`tint:` | `tint:img\logo_couleur.png` | Duotone : le relief interne module la couleur du thème

- `mono:` s'utilise pour tous les glyphes monochromes (icônes blanches ou noires) : ils se recolorent automatiquement en clair comme en sombre.
- `tint:` harmonise une icône couleur avec le thème en conservant ses dégradés. À ne jamais utiliser sur un glyphe blanc (il resterait blanc).
- Sans préfixe, l'image multicolore est laissée intacte.

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.

    lnv_bar.of_add_button("save", "Enregistrer", uo_toolbar.of_icon("mono:img\save.svg"))

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.

Balise | Effet
--- | ---
`[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
`[br]` / `[linebreak]` / `[br:3]` | Saut de ligne (ou n sauts)
`[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_id)`, 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_texte.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("clients", "[b]Clients[/b] [size-=20](128)[/size-=20]", 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 des crochets : elle serait interprétée. `of_escape_markup()`, disponible sur tout composant, les double pour vous — enveloppez la donnée, jamais le balisage que vous écrivez vous-même.

    // Une donnee metier peut contenir des crochets : sans echappement, elle est
    // INTERPRETEE et le texte entre crochets disparait.
    ls_libelle = "Solde [net]"
    uo_st.is_text = "Compte : " + ls_libelle                          // affiche : Compte :
    uo_st.is_text = "Compte : " + uo_st.of_escape_markup(ls_libelle)  // affiche : Compte : Solde [net]

Un texte sans balise n'a aucun surcoût (chemin rapide). Les balises inconnues sont ignorées et leur contenu conservé. 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. 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.

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 →