commandpalette — n_pbt_commandpalette #
← Référence des composants · Sommaire du guide
Palette de commandes : l'utilisateur presse un raccourci, tape trois lettres, et atteint n'importe quelle action de votre application — sans la chercher dans les menus.
▶ Le voir en vrai — Application de démonstration, tuile Command palette : l'aperçu, le code qui le produit et cette page, côte à côte.
En bref #
| Objet | n_pbt_commandpalette — non visuel : rien à poser dans la fenêtre |
| Sert à | Rendre toutes les actions de l'application atteignables au clavier, en trois lettres |
| Retour | Non bloquant : of_open() rend la main aussitôt ; le choix revient par événement |
La palette est une fenêtre détachée, à elle : elle flotte au-dessus de votre application, prend le focus le temps que l'utilisateur tape, et le rend en se refermant.
Démarrage rapide #
// Une fois, au demarrage : les actions de votre application
inv_palette.ipo_owner = this
inv_palette.of_add_command(/*key*/ "new", /*label*/ "N", /*group*/ "F")
inv_palette.of_add_command(/*key*/ "open", /*label*/ "O", /*group*/ "F")
inv_palette.of_add_command(/*key*/ "save", /*label*/ "S", /*group*/ "F")
inv_palette.of_register_shortcut()
// event ue_command_selected : (string as_key)
choose case as_key
case "new"; of_new()
case "open"; of_open()
case "save"; of_save()
end choose
Le branchement : la fenêtre hôte #
La palette est un objet non visuel, mais vous n'avez aucun receveur ni message à câbler : réglez ipo_owner sur votre fenêtre, et la palette draine elle-même ses événements et les lève sur cet objet.
Deux lignes, une fois, à l'ouverture de la fenêtre :
// La fenetre a laquelle la palette appartient
inv_palette.ipo_owner = this
// La palette doit repondre a sa touche
inv_palette.of_register_shortcut()
C'est tout ce qu'il y a à brancher. Le choix de l'utilisateur, l'ouverture et la fermeture vous reviennent ensuite par de simples événements sur
ipo_owner— sans receveur, sans message à mapper, sans timer.
Le raccourci : c'est la DLL qui l'entend #
Le raccourci qui ouvre la palette n'est pas écouté par la page : il est enregistré auprès de la DLL, qui seule voit les frappes pendant que le focus est sur un autre contrôle. C'est toute la différence entre une palette qu'on trouve et une palette qui ne répond que si on a déjà cliqué dessus.
La DLL entend l'accord, mais elle n'ouvre rien d'elle-même : elle vous prévient par ue_shortcut, et c'est vous qui décidez. Ouvrir une palette par-dessus un dialogue modal n'arrangerait personne.
// event ue_shortcut : la touche est tombee
if not ib_dialog_open then inv_palette.of_open()
is_shortcut choisit l'accord ; of_register_shortcut() le pose. Appelez-le une fois à l'ouverture de la fenêtre — sans quoi la palette ne répond à sa touche qu'après avoir déjà été ouverte une fois. of_open le repose au passage, donc un accord changé plus tard n'a besoin de rien de plus.
// Le raccourci de l'habitude, celui des editeurs de code
inv_palette.is_shortcut = inv_palette.SHORTCUT_DEFAULT
// Ou le votre
inv_palette.is_shortcut = "ctrl+shift+p"
// Ou aucun : la palette ne s'ouvre plus que par of_open()
inv_palette.is_shortcut = inv_palette.SHORTCUT_NONE
L'accord de la palette n'est consulté qu'après les raccourcis des autres composants de la fenêtre, focus ou non : un bouton de barre d'outils posé sur le même accord l'emporte. L'accord de cette fenêtre passe avant un accord posé sans
ipo_owner(toute l'application). Seules les touches que le crochet sait nommer sont acceptées — lettres, chiffres, F1 à F24, Entrée, Échap, Suppr, Inser, Début, Fin, Pg préc., Pg suiv. et, avec Ctrl ou Alt, les flèches, Espace, Tab, Retour arrière,+ - , .; toute autre rend-5. Le chapitre clavier le détaille.
Où la palette s'affiche #
is_position dit où la fenêtre se pose. Elle est toujours ramenée dans l'écran : une palette ancrée sous un champ en bas de fenêtre ne disparaît pas sous la barre des tâches.
| Constante | Où |
|---|---|
POSITION_WINDOW_CENTER | Centrée sur ipo_owner — le défaut, et ce qu'attend l'œil |
POSITION_SCREEN_CENTER | Centrée sur l'écran, quelle que soit la fenêtre |
POSITION_ABSOLUTE | À il_x / il_y, en pixels écran |
PowerBuilder travaille en PBU, et la position d'un contrôle est relative à sa fenêtre : pour ancrer la palette sous un contrôle, of_anchor_under(contrôle) fait la conversion et pose les trois propriétés. Sur deux écrans, elle s'ouvre sur l'écran du point demandé.
Sa hauteur suit le nombre de commandes affichées, et se réduit à mesure que l'on filtre — sans que son coin haut bouge, sinon la zone de saisie se déroberait sous les doigts. Elle est plafonnée à la moitié de l'écran : au-delà, la liste défile à l'intérieur et la zone de saisie reste en haut.
Un clic ailleurs dans l'application referme la palette, et ce clic atteint quand même sa cible — comme un menu qu'on quitte. Rien à faire pour cela.
Propriétés #
| Propriété | Type | Défaut | Rôle |
|---|---|---|---|
ipo_owner | powerobject | — | La fenêtre à laquelle la palette appartient : elle possède la fenêtre popup et lui sert d'ancre, son accord répond dans cette fenêtre, et c'est sur elle que les événements de la palette sont levés. À poser avant of_open — c'est le seul branchement à faire. Laissée vide, l'accord répond dans toutes les fenêtres de l'application. Deux objets palette posés sur la même fenêtre gardent chacun leur accord et leurs événements : aucun ne reçoit ceux de l'autre |
ipo_receiver | powerobject | — | Optionnel, hérité : un objet visuel distinct sur lequel livrer les événements, à la place de ipo_owner — c'est aussi la seule fenêtre dont le pbm_custom02 est sonné à chaque événement. Laissez-le vide — la palette livre désormais ses événements toute seule via ipo_owner |
is_shortcut | string | "ctrl+k" | Accord de touches qui ouvre la palette, depuis n'importe où dans la fenêtre. Constantes SHORTCUT_DEFAULT (ctrl+k) et SHORTCUT_NONE (aucun). Prend effet à of_register_shortcut |
is_position | string | window-center | Où la fenêtre se pose (constantes POSITION_*) |
il_x · il_y | long | 0 | Position en pixels écran, lue par POSITION_ABSOLUTE seulement |
is_placeholder | string | "" | Texte gris affiché dans la zone de saisie tant que rien n'est tapé |
is_recent | string | "" | Mémoire d'usage : les identifiants les plus récemment lancés, du plus récent au plus ancien, séparés par des virgules. La palette les fait remonter en tête, et la récence départage au filtrage — elle ne renverse jamais la pertinence. Relisez-la après usage et persistez-la ; reposez-la au démarrage. Une palette qui repart vierge chaque matin n'apprend rien |
il_max_recent | long | 8 | Combien le bloc « Récemment utilisé » en garde. 8 par défaut. 0 l'éteint : une application dont les utilisateurs préfèrent voir leurs groupes intacts peut le dire. Une entrée du bloc reste dans son groupe et y porte son nom — un raccourci ne déplace pas ce qu'il raccourcit |
Méthodes #
| Méthode | Rôle | |
|---|---|---|
of_add_command (string as_key, string as_label, string as_group) | Déclare une action : son identifiant, son libellé, et le groupe sous lequel elle apparaît. Renvoie 0 une fois ajoutée, -5 si la clé est vide, contient /, ` | ou une virgule (is_recent` est une liste séparée par des virgules), ou est déjà prise. Des dizaines de milliers de commandes restent fluides : la palette ne dessine que les lignes visibles |
of_add_command (string as_key, string as_label, string as_group, string as_hint, string as_shortcut, string as_keywords) | Même chose, avec l'explication à droite, le raccourci à afficher, qui lance sa commande tant que la palette est ouverte — c'est ainsi qu'on l'apprend ; en dehors, votre application garde ses propres accélérateurs, et pendant la frappe Ctrl+C, Ctrl+V, Ctrl+Z et Ctrl+A restent à la zone de recherche — et des mots-clés que la recherche prend en compte sans qu'ils soient visibles. Renvoie 0 une fois ajoutée, -5 si la clé est vide, contient /, ` | ou une virgule (is_recent` est une liste séparée par des virgules), ou est déjà prise |
of_insert_command (string as_key, string as_label, string as_group, integer ai_index) | Déclare une action à un rang choisi (1 = en tête) plutôt qu'à la fin : un module partagé range ses commandes où elles vont. 0 ou moins, ou au-delà de la fin, ajoute au bout. Indication, raccourci, mots-clés et icône se posent ensuite par of_command. Renvoie 0 une fois ajoutée, -5 pour les mêmes clés que of_add_command | |
of_remove_command (string as_key) | Retire une action ; les autres restent. Renvoie 0 une fois retirée, -5 si aucune commande ne porte cette clé | |
of_command (string as_key) → n_pbt_commandpalette_command | La poignée d'une commande, pour la renommer, changer son raccourci, la griser ou la masquer par ses propriétés. Griser plutôt que retirer : retirer ce que l'utilisateur ne peut pas faire lui retire aussi toute chance de découvrir que ça existe. L'état voyage avec les commandes : un changement fait palette ouverte se voit à l'ouverture suivante | |
of_key ( ) → string | Sur la poignée que rend of_command : la clé de la commande qu'elle désigne — ce qu'of_command a reçu pour l'obtenir, et ce qu'on garde quand la poignée passe de main en main | |
of_clear_commands ( ) | Vide la palette. Renvoie 0 | |
of_count ( ) → long | Rend le nombre de commandes que la palette porte | |
of_keys_at ( long al_index ) → string | L'identifiant de la commande de rang al_index (à partir de 1), ou "" au-delà de l'un ou l'autre bout. Avec of_count, c'est ce qui permet de parcourir une palette qu'on n'a pas remplie soi-même — un module partagé y ajoute les siennes | |
of_has ( string as_key ) → boolean | Une commande existe-t-elle sous cet identifiant ? Demander vaut mieux que deviner : of_add_command refuse (-5) un identifiant déjà pris | |
of_anchor_under ( dragobject ado_control ) | Ancre la palette sous un contrôle — un champ, un bouton : pose is_position à POSITION_ABSOLUTE et il_x / il_y sur le coin bas gauche du contrôle, en pixels écran. À appeler avant of_open ; la palette reste ramenée dans l'écran. Renvoie 0, ou -5 si le contrôle n'est pas valide | |
of_open ( ) | Ouvre la palette : une fenêtre à elle, possédée par ipo_owner, placée par is_position. Elle prend le focus, et le rend en se refermant. Renvoie 0 une fois demandée — ue_opened (à l'écran) ou ue_closed (avec la raison pour laquelle elle n'a pas paru) suit toujours —, -6 si le runtime WebView2 manque, -4 si sa fenêtre n'a pas pu être créée | |
of_is_open ( ) | VRAI tant que la palette est à l'écran. C'est ce qui permet à l'accord de basculer : pressé une seconde fois, une palette se ferme — rappeler of_open détruirait la fenêtre pour la reconstruire à l'identique, ce qui se voit comme un clignotement, pas comme une fermeture. La DLL ne décide toujours rien : elle informe. Une fois ouverte, la palette tient le focus dans sa propre fenêtre : l'accord pressé là la referme d'elle-même. Il répond pour la palette de cet objet : quand un autre objet palette ouvre la sienne, elle remplace celle-ci et of_is_open répond FAUX ici | |
of_close ( ) | Referme la palette que cet objet a ouverte — jamais celle qu'un autre objet palette a ouverte depuis. Perdre le focus la referme aussi, comme un menu. Renvoie 0 | |
of_register_shortcut ( ) | Confie l'accord de is_shortcut à la DLL. À appeler une fois à l'ouverture de la fenêtre. Un seul accord par objet palette : le rappeler après avoir changé is_shortcut remplace le précédent, qui cesse aussitôt de répondre — il n'y a jamais rien à retirer d'abord. Rend 0 s'il est posé, 1 s'il en a remplacé un, 2 si un is_shortcut vide ne laisse aucun accord (retiré, ou il n'y en avait pas), -5 si la touche est de celles que le crochet ne voit pas (voir plus haut) — l'accord précédent est alors gardé. Sans ipo_owner, l'accord répond dans toutes les fenêtres de l'application. Deux objets palette d'une même fenêtre gardent chacun le leur ; le même accord posé par un second objet lui revient (1). Détruire l'objet retire son accord — jamais celui qu'un autre objet palette tient | |
of_process_events ( ) | Draine les événements en attente et les lève sur ipo_owner. Le composant l'appelle lui-même tant que la palette vit : vous n'avez normalement pas à le faire | |
of_reset ( ) | Vide les commandes et la mémoire d'usage (is_recent), remet les propriétés à leur défaut, referme une palette ouverte et ramène un accord posé à SHORTCUT_DEFAULT. ipo_owner et ipo_receiver sont laissés tels quels : c'est le câblage, pas le contenu |
Propriétés d'une commande — n_pbt_commandpalette_command #
Obtenue par of_command(clé). La palette rebâtit sa fenêtre depuis sa liste à chaque of_open : une propriété changée pendant qu'elle est ouverte se voit à l'ouverture suivante.
| Propriété | Type | Défaut | Rôle |
|---|---|---|---|
is_label | string | — | Le texte de la ligne |
is_shortcut | string | "" | L'accord affiché à droite de la ligne, et honoré tant que la palette est ouverte (Ctrl+Shift+S) |
is_group | string | — | Le groupe sous lequel la commande est rangée ; le changer la déplace sans la retirer (elle garde son rang parmi les commandes) |
is_hint | string | "" | La petite ligne sous le libellé |
is_keywords | string | "" | Les mots que la recherche lit sans les montrer — les mots de l'utilisateur |
is_icon | string | "" | Une icône à gauche de la ligne : un fichier, une image de bibliothèque, ou mono: / tint: pour une icône qui suit le thème |
ib_enabled | boolean | true | Commande grisée : visible, cherchable, et inerte — ni clic, ni Entrée, ni son accord |
ib_visible | boolean | true | Commande masquée : hors de la liste et des accords, sans être retirée ; elle revient telle quelle |
Événements #
| Événement | Déclenché quand |
|---|---|
ue_command_selected (string as_key) | L'utilisateur a choisi une action. La palette s'est déjà refermée : à vous de faire ce qu'elle annonce |
ue_shortcut ( ) | L'accord a été pressé. La DLL relaie, PB décide. Une palette bascule sur sa propre touche : if of_is_open() then of_close() else of_open() — pressé dans la palette ouverte, l'accord la referme de lui-même. Vous pouvez aussi refuser |
ue_opened ( ) | La palette est à l'écran — par of_open |
ue_closed (string as_reason) | Elle vient de se refermer, choix fait ou non. Il suit chaque of_open qui a rendu 0 : as_reason est vide pour une palette qui était à l'écran, cancelled si elle a été fermée — ou remplacée par une autre palette — avant de paraître, failed si sa fenêtre n'a pas pu naître, blocked sous débogage distant sans licence |
La palette ne fait rien d'elle-même. Elle rapporte l'identifiant choisi, et referme. C'est votre application qui agit — la même action, déclenchée depuis un menu ou depuis la palette, passe donc par le même code.
Au clavier #
| Touche | Effet |
|---|---|
L'accord de is_shortcut | Prévient votre code par ue_shortcut ; c'est lui qui ouvre |
| Frappe au clavier | Filtre au fil de la saisie : les lettres tapées n'ont pas à se suivre, nvf trouve « Nouveau fichier », et les accents ne comptent pas (preferences trouve « Préférences ») |
| Flèches haut / bas | Déplacent la sélection dans la liste |
| Entrée | Choisit l'action sélectionnée (ue_command_selected) |
| Le raccourci affiché sur une ligne | Lance cette commande, sans avoir à la sélectionner |
| Échap | Referme sans rien choisir |
Exemples #
Alimenter la palette depuis votre menu #
// Les mots-cles ne s'affichent pas, mais la recherche les lit :
// taper "pdf" trouvera l'export meme si le libelle ne le dit pas
inv_palette.of_add_command(/*key*/ "export", /*label*/ "E", /*group*/ "F", /*hint*/ "H", /*shortcut*/ "Ctrl+E", /*keywords*/ "pdf csv xlsx")
Choisir un autre raccourci #
// Ctrl+K est deja pris par votre application ? Choisissez-en un autre.
// of_register_shortcut le pose, et l'ancien s'en va tout seul.
inv_palette.is_shortcut = "ctrl+shift+p"
inv_palette.of_register_shortcut()
L'ancrer sous un champ #
// Ancree sous un champ de saisie, en pixels ecran
// La palette est ramenee dans l'ecran si elle depassait
inv_palette.of_anchor_under(/*control*/ sle_1)
inv_palette.is_placeholder = "P"
inv_palette.of_open()
// Remove one command, empty the list, close the palette
inv_palette.of_remove_command(/*key*/ "print")
inv_palette.of_clear_commands()
inv_palette.of_close()
Bonnes pratiques #
- Donnez à chaque commande le même identifiant que dans votre menu : une seule fonction traite les deux, et l'utilisateur obtient exactement la même chose.
- Appelez
of_register_shortcut()à l'ouverture de la fenêtre, pas au premierof_open: une palette qui ne répond à sa touche qu'après avoir été ouverte à la souris ne sert à rien. - Renseignez les mots-clés : c'est ce qui fait la différence entre une palette qu'on utilise et une palette où l'on ne trouve rien. Pensez aux mots que l'utilisateur emploie, pas aux vôtres.
- Affichez le raccourci de l'action dans
as_shortcut: la palette devient alors le moyen de les apprendre. - N'y mettez que des actions immédiates. Une commande qui ouvre un dialogue de configuration, oui ; une commande qui a besoin de trois paramètres, non.
- Retirez les commandes qui n'ont plus de sens plutôt que de les laisser échouer : une palette qui propose l'impossible perd la confiance en une fois.