radialmenu — u_pbt_radialmenu #
← Référence des composants · Sommaire du guide
Menu contextuel radial : les commandes en couronne autour du curseur, choisies par leur direction plutôt qu'en parcourant une liste.
▶ Le voir en vrai — Application de démonstration, tuile Radial menu : l'aperçu, le code qui le produit et cette page, côte à côte.
En bref #
| Userobject | u_pbt_radialmenu |
| Classe d'items | n_pbt_radialmenu_item (of_item(keys)) |
| Sert à | Offrir quelques commandes fréquentes là où se trouve déjà la main |
| Principe | Vous décrivez les branches ; la roue, la forme et la navigation sont à nous |
Démarrage rapide #
// Une roue montée pour ce qui est sous le curseur
uo_wheel.of_add_item(/*keys*/ "cut", /*text*/ "Cut")
uo_wheel.of_add_item(/*keys*/ "copy", /*text*/ "Copy")
uo_wheel.of_add_item(/*keys*/ "paste", /*text*/ "Paste")
uo_wheel.of_add_item(/*keys*/ "delete", /*text*/ "Delete")
// Elle s'ouvre au relâchement du bouton droit : voir ue_rclicked
Ouvrir, puis choisir #
La roue s'ouvre au relâchement du bouton droit, et il faut un second clic pour choisir. Ce n'est pas un détail d'implémentation : si elle s'ouvrait sur l'appui, le relâchement de ce même clic sélectionnerait aussitôt le secteur tombé sous le curseur, et l'utilisateur déclencherait une commande sans l'avoir vue.
C'est pourquoi of_show() s'appelle depuis l'événement clic droit du contrôle sur lequel l'utilisateur fait le geste (un statictext, un button, une grille…) : à cet instant le bouton est déjà relâché, et le clic suivant est bien celui qui choisit. Le menu radial, lui, n'a aucune surface — donc aucun événement de souris à lui.
Le composant lui-même est invisible : il n'occupe aucune place dans la fenêtre. Posez-le n'importe où, donnez-lui une largeur et une hauteur nulles, il n'existe que le temps où la roue est ouverte.
La branche pointée — à la souris ou au clavier — montre son libellé entier, au-dessus de ses voisines : un secteur garde deux lignes au plus (une seule au-delà de huit branches), et c'est ce qui lui permet de porter un libellé court sans mentir sur ce qu'il fait. Le moyeu au centre est la sortie : le cliquer referme la roue ; dans une sous-roue, il fait remonter d'un niveau. Tant que la roue est ouverte, l'application continue de tourner : une branche retirée, grisée ou masquée entre-temps ne se choisit plus (ue_dismissed à la place) ; of_clear, of_reset, of_close et un second of_show ferment la roue ouverte, of_remove_item aussi quand elle montrait la branche retirée — ue_dismissed est levé aussitôt, et of_is_open répond faux dès le retour de l'appel.
// Elle s'ouvre au relâchement du bouton droit : voir ue_rclicked
uo_wheel.of_show()
Au clavier, les flèches parcourent les branches en sautant les grisées, Entrée ou Espace choisit, ← et Retour arrière remontent d'un cran, et Échap remonte d'un cran dans une sous-roue — il ne ferme la roue qu'à la racine. Aucune de ces touches ne file dans l'application tant que la roue est ouverte. En lecture de droite à gauche, la couronne tourne dans l'autre sens et ← et → s'échangent : → remonte d'un cran.
Pour l'ouvrir au clavier — la touche Menu, Maj+F10 —, appelez of_show_over depuis le contrôle qui a le focus, avec ab_keyboard à vrai : la roue se centre sur ce contrôle, où que soit la souris, et sa première branche qui répond est allumée — Entrée la choisit, les flèches continuent de là. of_show_over(contrôle) sans ce second argument sert aussi à un bouton de barre d'outils : la roue s'ouvre sur lui.
Les sous-roues #
Une adresse accroche des branches sous une autre : export/pdf. Choisir la branche parente ne choisit rien : la roue est remplacée par celle de ses enfants, et le moyeu devient le retour.
Pourquoi remplacer plutôt qu'ajouter une seconde couronne ? Parce qu'un anneau extérieur diviserait les secteurs par deux à chaque niveau. Huit branches est déjà le maximum lisible ; on n'a pas les moyens d'en afficher deux niveaux à la fois.
L'événement ue_item_selected rapporte l'adresse complète (export/pdf), pas la clé de la feuille seule. Deux sous-roues peuvent donc nommer leurs branches pareil sans ambiguïté.
// A branch, then two entries at its address
uo_wheel.of_add_item(/*keys*/ "export", /*text*/ "Export")
uo_wheel.of_add_item(/*keys*/ "export/pdf", /*text*/ "PDF")
uo_wheel.of_add_item(/*keys*/ "export/csv", /*text*/ "CSV")
Pour retirer une branche — sa sous-roue avec elle — sans reconstruire toute la roue : of_remove_item("export/csv"). Les autres branches restent en place.
Quand il y a trop de branches #
Une roue se lit par direction, et au-delà de huit secteurs les tranches cessent d'être distinguables. ii_max_sectors fixe ce plafond (de 3 à 12, 8 par défaut).
Les branches en trop ne sont pas perdues : la dernière place de la couronne devient une branche qui les contient toutes, et qui s'ouvre comme une sous-roue. Un menu qui laisserait tomber sa queue serait un menu qui ment sur ce qu'il propose.
Resserrer la couronne est souvent un gain : quatre branches larges se visent plus vite que huit étroites.
Propriétés #
| Propriété | Type | Défaut | Rôle |
|---|---|---|---|
ii_max_sectors | integer | 8 | Nombre de branches que une couronne peut porter (3 à 12). Ce qui dépasse passe sous une dernière branche qui s'ouvre en sous-roue. Au-delà de huit, la roue se resserre (icône plus petite, libellé sur une ligne) pour que les libellés ne se chevauchent pas |
is_theme_style | string | "" | Style visuel du composant (constantes THEME_STYLE_*) ; vide = celui de l'application, suivi à chaque changement |
is_theme_mode | string | "" | Variante claire ou sombre (constantes THEME_MODE_*) ; vide = celle de l'application, suivie à chaque changement |
il_theme_accent | long | -1 | Couleur d'accent de ce composant (-1 = l'accent de l'application, ou celui du thème) |
Méthodes #
| Méthode | Rôle | |
|---|---|---|
of_add_item (string as_keys, string as_text) | Ajoute une branche à son adresse : format/strike s'accroche sous Format, dont le parent doit déjà exister. C'est cette même adresse qui revient dans ue_item_selected quand elle est choisie. Renvoie 0 une fois ajoutée, -5 si l'adresse est refusée (parent introuvable, adresse déjà prise, niveau vide comme export/ ou /pdf, clé contenant ` | ou commençant par __), -2` si le composant n'est pas créé |
of_add_item (string as_keys, string as_text, string as_image, boolean ab_checked) | Même chose, avec l'icône et la marque — l'icône en troisième argument, comme partout ailleurs dans la bibliothèque. Une branche se grise par sa poignée : of_item(keys).ib_enabled = false. Renvoie 0 une fois ajoutée, -5 si l'adresse est refusée (parent introuvable, adresse déjà prise, niveau vide comme export/ ou /pdf, clé contenant ` | ou commençant par __), -2` si le composant n'est pas créé |
of_clear ( ) | Vide la roue et libère les poignées obtenues par of_item (chacune désignait une branche qui n'existe plus). Renvoie 0 une fois appliqué, -2 si le composant n'est pas créé | |
of_close ( ) | Ferme la roue du composant si elle est à l'écran, sans choix : ue_dismissed est levé aussitôt, comme pour un clic dehors — même pour une roue demandée un instant plus tôt et pas encore dessinée — et of_is_open répond faux dès le retour. Rien d'ouvert, rien ne se passe. Renvoie 0 une fois fait, -2 si le composant n'est pas créé | |
of_is_open ( ) | true tant qu'une roue du composant est à l'écran — ouverte, et pas encore répondue par un choix ou un renvoi. of_show et of_close sont appliqués avant de rendre la main : il répond vrai juste après of_show, faux juste après of_close | |
long of_show ( ) | Ouvre la roue centrée sur le curseur. À appeler depuis le clic droit du contrôle qui reçoit le geste. Une roue sans aucune branche visible ne s'ouvre pas : ue_dismissed est levé. Une roue encore à l'écran est remplacée : elle lève d'abord ue_dismissed. Renvoie 0 une fois demandé, -2 si le composant n'est pas créé | |
long of_show (long al_x, long al_y) | Même chose, centrée sur une position écran en pixels. Des coordonnées négatives sont un vrai point, (-1, -1) compris : un écran placé à gauche ou au-dessus du principal. Renvoie 0 une fois demandé, -2 si le composant n'est pas créé | |
long of_show_over (dragobject ado_control) | Ouvre la roue centrée sur un contrôle — une image, un champ, un bouton de barre d'outils —, où que soit la souris. Renvoie 0 une fois demandé, -5 si le contrôle n'est pas valide, -2 si le composant n'est pas créé | |
long of_show_over (dragobject ado_control, boolean ab_keyboard) | Idem, en disant comment la roue a été demandée : ab_keyboard à vrai quand l'utilisateur l'a ouverte au clavier (touche Menu, Maj+F10) — sa première branche qui répond est allumée, Entrée la choisit. Renvoie 0 une fois demandé, -5 si le contrôle n'est pas valide, -2 si le composant n'est pas créé | |
of_item (string as_keys) | Renvoie la poignée d'une branche, par son adresse, pour la modifier ensuite (libellé, état, couleurs). Une clé nue est résolue en l'adresse complète de la seule branche qui la porte : of_item("csv") et of_item("import/csv") rendent alors la même poignée. Ambiguë, ou portée par aucune branche, elle rend une poignée qui ne change rien plutôt que la mauvaise branche. of_key() de la poignée rend la clé du dernier niveau | |
of_remove_item (string as_keys) | Retire une branche et sa sous-roue ; les autres restent. Une clé nue est résolue comme par of_item. La roue à l'écran ne se ferme (ue_dismissed) que si elle montrait cette branche : retirer une branche masquée la laisse ouverte. Renvoie 0 une fois retirée, -5 si aucune branche ne vit à cette adresse, -2 si le composant n'est pas créé | |
of_reset ( ) | Vide la roue et remet toutes les propriétés à leur valeur d'origine. Renvoie 0 une fois appliqué, -2 si le composant n'est pas créé |
Événements #
| Événement | Déclenché quand |
|---|---|
ue_item_selected (string as_keys) | L'utilisateur a choisi une branche. as_keys est son adresse complète (export/pdf), pas la clé de la feuille seule. Un seul événement par geste : un double-clic ne choisit qu'une fois |
ue_dismissed ( ) | La roue s'est refermée sans qu'aucune branche soit choisie : clic sur le moyeu, clic hors de la roue, Échap à la racine — ou par le code : of_close, of_clear, of_reset, of_remove_item d'une branche qu'elle montrait, un second of_show qui la remplace, un of_show qui n'avait rien à montrer. Une fois par ouverture, aussitôt : of_is_open répond déjà faux |
Un clic dans un coin de la fenêtre, hors du disque, compte comme un clic dehors : la roue se referme (
ue_dismissed) et le clic passe à l'application qui se trouve derrière, au lieu d'être avalé par un carré invisible.
Propriétés d'item #
| Propriété | Type | Défaut | Rôle |
|---|---|---|---|
is_text | string | "" | Libellé de la branche. Gardez-le court : un secteur est une tranche, pas une ligne, et montre deux lignes au plus — la branche pointée montre son libellé entier. Vide, c'est la clé qui s'affiche |
ib_enabled | boolean | true | À faux, la branche est grisée et son secteur ignore les clics. Se relit tel que vous l'avez posé : une branche grisée faute de sous-entrée visible relit toujours vrai |
ib_visible | boolean | true | À faux, la branche sort de la roue — sous-roue comprise — sans être supprimée ; les secteurs se resserrent, et elle revient telle quelle. Une branche dont toutes les sous-entrées sont masquées reste sur la roue, grisée : elle n'a plus rien à ouvrir et ne se choisit pas comme une feuille |
ib_checked | boolean | false | À vrai, un arc fin sous la bande marque la branche comme active |
is_image | string | "" | L'icône de la branche, changée en place — un Lecture qui devient Pause. "" la retire. Mêmes chemins que of_add_item (mono:, tint:, fichier.dll:NOM) |
is_tone | string | "" | Le ton de la commande : son libellé coloré par le thème, clair ou sombre, au repos — u_pbt_radialmenu.TONE_DANGER pour celle qui détruit, TONE_WARN, TONE_SUCCESS, TONE_INFO. "" ou TONE_NEUTRAL = le texte du thème ; une couleur à vous (il_text_color) l'emporte |
is_group | string | "" | Un groupe radio : les branches d'un même groupe sous le même parent s'excluent — en cocher une (ib_checked = true, ou le choix de l'utilisateur, avant ue_item_selected) décoche les autres. "" = une branche ordinaire |
il_accent | long | -1 | Accent de cette branche : sa bande quand elle est pointée, et sa marque (-1 = celui du composant) |
il_back_color | long | -1 | Bande de cette branche au repos (-1 = celle du thème) |
il_text_color | long | -1 | Couleur du libellé de cette branche (-1 = celle du thème) |
il_back_color_hover | long | -1 | Quartier de cette branche quand elle est pointée (-1 = celui du thème). Donnée sans il_text_color_hover, elle reçoit un texte lisible dessus |
il_text_color_hover | long | -1 | Couleur du libellé de cette branche quand elle est pointée (-1 = celle du thème) |
Une branche n'affiche aucune info-bulle : la fenêtre ronde n'a pas de place hors du disque. Le is_tooltip hérité par la poignée est gardé et se relit, mais il n'est jamais montré — c'est la branche pointée qui montre son libellé entier.
Exemples #
Une roue par contexte #
// Une roue montée pour ce qui est sous le curseur
uo_wheel.of_clear()
uo_wheel.of_add_item(/*keys*/ "bold", /*text*/ "Bold", /*image*/ "mono:img\packimages.dll:svg/samples/bold", /*checked*/ true)
uo_wheel.of_add_item(/*keys*/ "find", /*text*/ "Find", /*image*/ "mono:img\packimages.dll:svg/samples/find", /*checked*/ false)
Réagir au chemin choisi #
// Événement ue_item_selected du menu radial
// as_keys porte le chemin complet, ex. "export/pdf"
choose case as_keys
case "export/pdf"
of_export_pdf()
case "delete"
of_delete_selection()
end choose
Marquer, griser, resserrer #
// Marquer une branche comme active
uo_wheel.of_item(/*keys*/ "bold").ib_checked = true
// Griser celle qui n'a pas de sens ici
uo_wheel.of_item(/*keys*/ "paste").ib_enabled = false
// Quatre branches au plus sur une couronne
uo_wheel.ii_max_sectors = 4
Au clavier, et refermer par le code #
// Touche Menu sur la grille : la roue s'ouvre centree sur elle, premiere branche allumee
uo_wheel.of_show_over(/*control*/ dw_orders, /*keyboard*/ true)
// Plus tard, depuis un minuteur : la roue se ferme sans choix (ue_dismissed)
if uo_wheel.of_is_open() then uo_wheel.of_close()
Un groupe radio, une commande qui détruit #
// Trois branches exclusives : en choisir une decoche les deux autres
uo_wheel.of_item(/*keys*/ "align/left").is_group = "align"
uo_wheel.of_item(/*keys*/ "align/center").is_group = "align"
uo_wheel.of_item(/*keys*/ "align/right").is_group = "align"
// Delete prend le ton du danger, clair ou sombre
uo_wheel.of_item(/*keys*/ "delete").is_tone = u_pbt_radialmenu.TONE_DANGER
Bonnes pratiques #
- Ouvrez au clic droit RELÂCHÉ du contrôle, jamais sur l'appui. C'est ce qui sépare « ouvrir » de « choisir », et l'utilisateur a besoin des deux.
- Des libellés courts. Un ou deux mots. Le secteur est là pour la direction ; le texte entier ne se lit qu'en pointant la branche.
- Quatre à six branches valent mieux que huit. Une roue se retient par la position ; moins il y a de positions, plus vite elles s'apprennent.
- Mettez les commandes les plus fréquentes en haut et en bas. Ce sont les deux directions que la main atteint sans réfléchir.
- Gardez l'ordre stable d'une ouverture à l'autre : tout l'intérêt d'une roue est que le geste finisse par précéder la lecture.
- Un menu radial ne remplace pas un menu en liste. Vingt commandes rares se lisent mieux dans une liste ; réservez la roue à la poignée qu'on utilise sans cesse.
Hérité du socle commun #
Ces membres existent sur tous les composants visuels — ils ne sont pas propres à celui-ci. Ils sont détaillés une seule fois, dans les chapitres transverses ; cette table dit seulement où les lire.
| Membres | Rôle | Détaillé dans |
|---|---|---|
of_count · of_keys_at · of_has | Parcourir ce que le composant contient | 3.2 Les items |
of_reset | Remettre le composant à zéro | 3.6 Remettre un composant à zéro : of_reset() |
of_register_shortcut · of_clear_shortcuts | Raccourcis clavier du composant | 3.5 Les raccourcis clavier |
of_is_created · of_is_ready · of_get_last_error | S'il est né, s'il est prêt, ce qui a échoué | 3.7 Diagnostic |
of_save_as_png · of_save_as_jpg | Exporter le rendu en image | 3.8 Exporter le rendu en image |
of_set_redraw | Grouper les modifications en un seul repaint | 3.10 Bonnes pratiques |
of_preload_icons | Icônes affichées sans délai | Affichage instantané : of_icon |
of_set_translation | Traduire un libellé du composant | 5.2 Adapter un libellé : of_set_translation |
of_focus_webview | Donner le focus au composant | 6.4 Clavier et focus |
of_print · of_print_to_pdf | Imprimer, ou écrire un PDF | 6.9 Imprimer |
of_set_property · of_get_property · of_component_name | Piloter une propriété par son nom | 3.1 Le moteur de propriétés |
is_accessible_name · is_accessible_description | Le nom et la description que dit un lecteur d'écran | 3.4 Propriétés et events optionnels |
Sur ce lanceur invisible, of_count et of_keys_at parcourent ce que vous avez ajouté, branches masquées comprises — pas ce que la roue dessinera. L'export en image, l'impression, of_focus_webview et of_set_redraw n'y ont pas d'effet utile : le composant ne dessine rien de lui-même, la roue vit dans sa propre fenêtre.
Deux aides ne sont pas héritées : of_icon et of_escape_markup vivent sur n_pbt_utils. Déclarez-en une — n_pbt_utils lnv_utils, rien à créer — et appelez-les dessus.