# dockcontainer — `u_pbt_dockcontainer` #

← Référence des composants · Sommaire du guide

> Panneaux dockables façon Visual Studio : docking par glisser-déposer, empilement en onglets, séparateurs redimensionnables, fenêtres flottantes, masquage automatique — et sauvegarde/restauration de la disposition.

> ▶ Le voir en vrai — Application de démonstration, tuile Dock container : l'aperçu, le code qui le produit et cette page, côte à côte.

## En bref #

Userobject | `u_pbt_dockcontainer`
Classe d'items | `n_pbt_dock_panel` (un panneau)
Sert à | Donner à une fenêtre PowerBuilder l'ergonomie d'un IDE moderne : l'utilisateur organise lui-même ses panneaux, et la disposition se retrouve à la session suivante

Comme tab, chaque panneau héberge un vrai contrôle PowerBuilder (`dragobject`) : userobject, DataWindow, ou un autre composant PBToolboxAI. Voir Héberger de vrais contrôles PowerBuilder.

## Démarrage rapide #

    // event open de la fenetre : les controles de panneau sont deja poses dessus
    uo_dock.of_add_panel(/*cle*/ "document", /*position*/ "", /*relatif_a*/ "", &
                         /*taille*/ 0, /*titre*/ "Document", /*contenu*/ uo_editeur)

    uo_dock.of_add_panel(/*cle*/ "explorateur", /*position*/ uo_dock.POSITION_START, &
                         /*relatif_a*/ "document", /*taille*/ 260, &
                         /*titre*/ "Explorateur", /*contenu*/ uo_arbre)

    uo_dock.of_add_panel(/*cle*/ "proprietes", /*position*/ uo_dock.POSITION_END, &
                         /*relatif_a*/ "document", /*taille*/ 300, &
                         /*titre*/ "Proprietes", /*contenu*/ uo_props)

    // La zone centrale : ni fermable par le docking, ni flottante, ni masquable
    uo_dock.is_main = "document"

## Propriétés #

Propriété | Type | Défaut | Rôle
--- | --- | --- | ---
`is_main` | string | `""` | Clé du panneau principal : la zone de document centrale, qui ne peut être ni masquée ni détachée. Le panneau doit déjà exister
`is_theme_style` | string | `fluent` | Style visuel du composant (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)
`is_tooltip` | string | `""` | Info-bulle simple affichée au survol du composant
`is_super_tooltip_title` | string | `""` | Titre de l'info-bulle enrichie (prend le pas sur `is_tooltip`)
`is_super_tooltip_text` | string | `""` | Texte de l'info-bulle enrichie (balisage riche accepté)
`is_super_tooltip_image` | string | `""` | Image de l'info-bulle enrichie

### Positions de docking #

Les constantes du composant alimentent `as_position` dans `of_add_panel` et `of_move_panel` :

Constante | Valeur | Effet
--- | --- | ---
`POSITION_START` | `"start"` | Le nouveau panneau se docke du côté début de lecture du panneau de référence (à gauche en écriture de gauche à droite)
`POSITION_END` | `"end"` | … du côté fin de lecture
`POSITION_TOP` | `"top"` | … au-dessus
`POSITION_BOTTOM` | `"bottom"` | … en dessous
`POSITION_STACK` | `"stack"` | Le panneau est empilé en onglet sur le panneau de référence. Valeur retenue si `as_position` est vide

### Propriétés d'un panneau — `n_pbt_dock_panel` #

Obtenues par `of_item(cle)`.

Propriété | Type | Défaut | Rôle
--- | --- | --- | ---
`is_title` | string | `""` | Titre affiché sur l'onglet du panneau. Accepte le balisage riche
`is_short_title` | string | `""` | Forme courte du titre, pour les endroits où la place manque : le rail d'un panneau épinglé (toujours — c'est un liseré) et l'onglet d'une pile, mais seulement une fois que le titre complet n'y tient plus, ce qui est mesuré et non deviné. L'en-tête du panneau garde le titre complet. `""` = pas de forme courte
`ib_visible` | boolean | `true` | Affiche ou masque ce panneau
`ib_pinned` | boolean | `true` | `true` = épinglé en place ; `false` = le panneau se replie en languette sur le bord et ne se déploie qu'au survol (masquage automatique)
`is_header_color` | string | `""` | Couleur de l'en-tête de ce panneau. Vide = le thème décide ; `HEADER_COLOR_ACCENT` suit l'accent du thème (et le suit encore après un changement de thème, ce qu'un code fixe ne ferait pas) ; sinon un `#rrggbb`. La couleur de texte lisible est calculée avec
`is_border_color` | string | `""` | Couleur de la bordure de ce panneau, même grammaire. Elle encadre le cadre et l'en-tête, qui portent chacun la leur : n'en colorer qu'une laisse une couture visible. Elle suit le panneau dans son volet glissant
`is_tooltip` | string | `""` | Info-bulle simple affichée au survol de l'item
`is_super_tooltip_title` | string | `""` | Titre de l'info-bulle enrichie de l'item (prend le pas sur `is_tooltip`)
`is_super_tooltip_text` | string | `""` | Texte de l'info-bulle enrichie de l'item (balisage riche accepté)
`is_super_tooltip_image` | string | `""` | Image de l'info-bulle enrichie de l'item

## Méthodes #

Méthode | Rôle
--- | ---
`of_add_panel (string as_key, string as_position, string as_relative_to, integer ai_size, string as_title, dragobject ado_content)` | Ajoute un panneau fermable et héberge le contrôle. `as_relative_to` = clé du panneau contre lequel il se docke (vide = la racine) ; `ai_size` = taille initiale en pixels (`0` = automatique)
`of_add_panel (…, boolean ab_closable)` | Idem, avec `ab_closable = false` pour un panneau sans bouton de fermeture
`of_select_panel (string as_key)` | Met un panneau au premier plan de son groupe d'onglets
`of_move_panel (string as_key, string as_target, string as_position)` | Réorganise depuis le code, exactement comme si l'utilisateur avait fait glisser l'onglet
`of_move_panel (string as_key, string as_target, string as_position, integer ai_index)` | Même chose, en donnant le rang du panneau dans la pile de la cible (0 = premier, `-1` = dernier). Seule une pile est une liste ordonnée : l'index est ignoré pour `POSITION_START` / `END` / `TOP` / `BOTTOM`
`of_float_panel (string as_key)` | Détache le panneau dans une vraie fenêtre flottante déplaçable et redimensionnable. La refermer le re-docke (`ue_panel_docked`)
`of_notify_panel (string as_key)` | Signale du contenu nouveau : si le panneau n'est pas visible, son onglet affiche un compteur d'éléments non vus, effacé à la sélection
`of_clear_notify (string as_key)` | Efface ce compteur sans afficher le panneau. Sélectionner l onglet était le seul autre moyen, ce qui est faux quand votre code sait que le contenu lui-même a disparu
`of_remove_panel (string as_key)` | Retire le panneau et rend le contrôle à sa fenêtre d'origine
`of_get_layout ( )` | Renvoie la disposition courante (arbre des découpes, ratios, onglets, visibilité) sous forme de chaîne, à stocker en fichier, base ou registre
`of_set_layout (string as_json)` | Restaure une disposition obtenue par `of_get_layout`. Les panneaux doivent avoir été recréés avant l'appel ; ceux absents de la disposition sont rattachés au premier groupe
`of_item (string as_id)` | Handle `n_pbt_dock_panel` du panneau (créé au premier accès)
`of_refresh_panel (string as_key)` | Rafraîchit le rendu d'un panneau construit hors écran, sans scintillement
`of_relayout ( )` | Recalcule et republie toutes les zones pour que les contrôles hébergés soient repositionnés
`of_reset ( )` | Vide le conteneur : chaque contrôle hébergé est rendu à sa fenêtre d'origine, les handles de panneaux sont libérés, la disposition mémorisée est effacée
`of_set_redraw (boolean)` | Regroupe une rafale de modifications en un seul rendu
`of_save_as_png (string)` · `of_save_as_jpg (string)` | Exporte le rendu en image

## Événements #

Événement | Déclenché quand
--- | ---
`ue_panel_selected (string as_key)` | Un panneau devient le panneau actif de son groupe
`ue_panel_closed (string as_key)` | L'utilisateur ferme un panneau
`ue_panel_pinned (string as_key, boolean ab_pinned)` | Un panneau est épinglé (`true`) ou dépunaisé en masquage automatique (`false`)
`ue_panel_floated (string as_key)` | Un panneau est détaché en fenêtre flottante
`ue_panel_docked (string as_key)` | Un panneau flottant est re-docké
`ue_layout_changed (string as_json)` | La disposition a changé (glissement, redimensionnement, docking). Le paramètre porte la nouvelle disposition, prête à être enregistrée
`ue_ready ( )` | Le composant a fini de charger ; tout ce qui a été envoyé avant a été rejoué
`ue_runtime_missing ( )` | Le runtime WebView2 est absent : le composant reste vide
`ue_bg_color (long al_color)` | Le composant a calculé sa couleur de fond de thème ; l'userobject l'a déjà adoptée (`backcolor`)

## Au clavier #

Le contenu d'un panneau est un vrai contrôle PowerBuilder : le clavier y appartient à votre code, comme dans n'importe quelle fenêtre. Ce que le composant prend en charge, c'est son propre habillage — en-têtes, onglets, splitters :

Touche | Effet
--- | ---
Flèches sur un onglet | Passent au panneau précédent / suivant du groupe, et le sélectionnent. Un bandeau d'onglets est un seul arrêt de tabulation
Début / Fin sur un onglet | Premier / dernier panneau du groupe
Flèches sur un splitter | Déplacent la séparation de 2 %, dans l'axe du splitter seulement
Page haut / Page bas sur un splitter | Même chose, par pas de 10 %
Échap | Referme le panneau glissant ouvert depuis un bouton de bord

Un splitter est un `separator` focalisable qui annonce sa position ; les boutons d'en-tête (détacher, masquer, fermer) portent un nom, et le panneau actif d'un groupe est marqué `aria-selected`. Un déplacement au clavier remonte dans `ue_layout_changed` exactement comme un glissement à la souris.

## Exemples #

### Une fenêtre façon IDE #

    // event open : chaque contenu est un vrai controle PB pose sur la fenetre
    uo_dock.of_add_panel("document", "", "", 0, "Document", uo_editeur)
    uo_dock.of_add_panel("explorateur", uo_dock.POSITION_START,   "document", 260, "Explorateur", uo_arbre)
    uo_dock.of_add_panel("proprietes",  uo_dock.POSITION_END,  "document", 300, "Proprietes",  uo_props)
    uo_dock.of_add_panel("sortie",      uo_dock.POSITION_BOTTOM, "document", 200, "Sortie",      uo_console)

    // Un second panneau empile en onglet sur "proprietes"
    uo_dock.of_add_panel("aide", uo_dock.POSITION_STACK, "proprietes", 0, "Aide", uo_aide)

    uo_dock.is_main = "document"

### Mémoriser et restaurer la disposition de l'utilisateur #

    // event close de la fenetre : enregistrer ce que l'utilisateur a organise
    string ls_disposition
    ls_disposition = uo_dock.of_get_layout()
    of_enregistrer_preference("dock_principal", ls_disposition)

    // event open : RECREER les panneaux d'abord, restaurer ensuite
    string ls_disposition

    of_creer_les_panneaux()          // les of_add_panel ci-dessus

    ls_disposition = of_lire_preference("dock_principal")
    if ls_disposition <> "" then uo_dock.of_set_layout(ls_disposition)

> L'ordre est impératif : `of_set_layout` ne crée aucun panneau, il ne fait que replacer ceux qui existent déjà.

### Suivre les changements en continu #

    // event ue_layout_changed de uo_dock : (string as_json)
    // Enregistrement immediat, sans attendre la fermeture de la fenetre.
    of_enregistrer_preference("dock_principal", as_json)

### Piloter un panneau par son handle #

    n_pbt_dock_panel lnv_panneau

    lnv_panneau = uo_dock.of_item("proprietes")
    lnv_panneau.is_title  = "Proprietes (3 modifiees)"
    lnv_panneau.ib_pinned = false        // se replie en languette sur le bord

    // Masquer un panneau reserve aux administrateurs
    uo_dock.of_item("audit").ib_visible = gb_administrateur

### Attirer l'attention sur un panneau en arrière-plan #

    // Un traitement de fond a ecrit dans la console : signaler sans voler le focus
    uo_dock.of_notify_panel("sortie")

### Réorganiser depuis le code #

    // Deplacer un panneau comme si l'utilisateur avait fait glisser son onglet
    uo_dock.of_move_panel(/*cle*/ "sortie", /*cible*/ "document", /*position*/ "bottom")

    // L'empiler en onglet sur un autre panneau
    uo_dock.of_move_panel(/*cle*/ "aide", /*cible*/ "proprietes", /*position*/ "stack")

### Détacher un panneau sur un second écran #

    // Fenetre flottante native : l'utilisateur peut la poser sur un autre ecran
    uo_dock.of_float_panel("proprietes")

    // event ue_panel_docked de uo_dock : (string as_key)
    // L'utilisateur a referme la fenetre flottante : le panneau est revenu.
    of_journaliser("Panneau re-docke : " + as_key)

### Fermer proprement un panneau #

    // event ue_panel_closed de uo_dock : (string as_key)
    // Retirer aussi le panneau du conteneur pour liberer le controle heberge.
    uo_dock.of_remove_panel(as_key)

## Bonnes pratiques #

- Désignez toujours un panneau principal avec `is_main` : sans zone centrale, l'utilisateur peut tout masquer et se retrouver devant une fenêtre vide.
- Enregistrez la disposition sur `ue_layout_changed` plutôt qu'à la seule fermeture : une fermeture anormale ne perd alors rien.
- `of_set_layout` vient après les `of_add_panel`, jamais avant.
- Utilisez `of_notify_panel` pour les traitements de fond : c'est moins intrusif qu'une boîte de dialogue et l'information reste visible.
- Un contrôle hébergé est une fenêtre native : il se dessine au-dessus de la couche web, aucun effet visuel du composant ne passe par-dessus lui.
- `of_reset()` avant toute reconstruction : sans lui, les contrôles précédemment hébergés restent reparentés dans le conteneur vidé et réutiliser une clé échoue.
- Pour de simples pages exclusives, sans docking ni flottement, tab est plus léger.

← Référence des composants · Sommaire du guide