Marqueurs de ligne et clic dans la marge¶
Un marqueur de ligne est un glyphe dans la colonne des signets de l'éditeur
et/ou une teinte sur toute la ligne, attaché à une ligne d'un document et
emporté avec elle quand l'utilisateur modifie le texte. Les points d'arrêt
(pleins, creux, conditionnels), une flèche d'instruction courante et les bandes
de modifications du contrôle de version sont tous des marqueurs de ligne.
Atteignez le service via IPluginContext::lineMarkers()
(PluginApi/ILineMarkerService.h).
Le plugin décrit à quoi ressemble un type de marqueur et où se trouvent les marqueurs de ce type ; le dessin, la marge et les emplacements de marqueurs du moteur d'édition appartiennent à l'éditeur. Aucune taille en pixels, aucun numéro du moteur ni aucun type Qt ne franchit la frontière.
Types¶
Enregistrez chaque type de marqueur une seule fois, dans initialize(), et
conservez le jeton :
enum class LineMarkerGlyph : std::int32_t {
None, Circle, CircleHollow, CircleBar, Arrow, Strip
};
struct LineMarkerKind {
LineMarkerGlyph glyph = LineMarkerGlyph::Circle;
std::string glyphColor; // a theme category you registered
std::string lineTint; // a theme category; empty = no tint
std::int32_t zOrder = 0; // higher draws above lower
};
m_breakpoint = ctx.lineMarkers().registerKind(kind); // LineMarkerKindToken
if (!m_breakpoint.valid()) { /* no slot left: see below */ }
- Les couleurs sont des identifiants de catégories de thème que vous avez
vous-même enregistrées avec
ctx.themePalette(): les marqueurs suivent ainsi le thème actif et l'utilisateur peut les recolorer dans l'éditeur de thèmes ; l'éditeur les réapplique à chaque changement de thème. - Les glyphes forment un petit ensemble fixe que tout hôte sait dessiner
dans une marge étroite à n'importe quel zoom :
Circle(un point d'arrêt),CircleHollow(un point d'arrêt désactivé ou non vérifié),CircleBar(un point d'arrêt conditionnel),Arrow(l'instruction courante),Strip(une ligne modifiée).Nonecrée un type qui ne fait que teinter. - zOrder décide quel marqueur est dessiné au-dessus quand plusieurs partagent une ligne (la flèche de l'instruction courante au-dessus d'un point d'arrêt, un point d'arrêt au-dessus d'une bande de modifications) ; à valeur égale, le type enregistré le plus tard passe au-dessus. L'ordre vaut aussi entre plugins.
- L'enregistrement peut échouer. Le moteur d'édition dispose d'un petit nombre fixe d'emplacements de marqueurs partagés par tous les plugins — neuf ; un type avec un glyphe et une teinte en utilise deux. Quand ils sont épuisés, le jeton revient vide et le journal dit pourquoi. Enregistrez une seule fois les types dont vous avez besoin et conservez-les ; n'enregistrez jamais un type par document ou par marqueur.
- Détruire le jeton supprime tous les marqueurs de ce type de tous les documents et retire le type.
Marqueurs¶
using LineMarkerId = std::uint64_t; // 0 is never valid
LineMarkerId id = ctx.lineMarkers().addMarker(m_breakpoint.id(), doc, line);
std::int64_t now = ctx.lineMarkers().lineOf(id); // -1 = gone
auto all = ctx.lineMarkers().markers(m_breakpoint.id(), doc); // {id, line} by line
ctx.lineMarkers().removeMarker(id);
ctx.lineMarkers().clearMarkers(m_breakpoint.id(), 0); // 0 = every document
addMarker renvoie 0 — et ne place rien — lorsque le type n'est pas vivant,
que le document n'est pas ouvert ou que la ligne est hors du document. Une
ligne n'est pas bornée : un point d'arrêt sur la mauvaise ligne est pire
qu'aucun. Un identifiant est une simple valeur, pas un jeton ; gardez-en autant
qu'il vous faut.
Ce qui arrive ensuite à un marqueur :
- Les modifications le déplacent avec sa ligne : des lignes insérées ou supprimées au-dessus le décalent, taper sur la ligne le conserve — c'est ainsi qu'un point d'arrêt suit le code.
- Sa ligne est supprimée (elle fait partie d'une plage supprimée) : il survit et rejoint la ligne où commençait la plage supprimée. Deux marqueurs peuvent ainsi se retrouver sur une même ligne.
- Tout le tampon est remplacé (un rechargement après une modification
externe,
IEditorService::setText) : chaque marqueur reste sur le même numéro de ligne ; celui dont la ligne n'existe plus est supprimé. L'éditeur ne compare pas l'ancien et le nouveau texte. - Le document est fermé : ses marqueurs sont supprimés et leurs
identifiants deviennent caducs. L'éditeur ne conserve jamais les marqueurs —
un plugin qui garde des marques au-delà d'une fermeture (des points d'arrêt)
les tient dans son propre modèle et les rajoute à
DocumentOpened(un fichier rouvert est un nouveauDocumentId).
Les marqueurs appartiennent au document : placés une fois, ils
apparaissent dans chaque volet d'un
document scindé. La minicarte, son aperçu au
survol et la vue de comparaison ne les affichent jamais. Les signets ne sont
pas des marqueurs de ce service ; ils gardent leurs propres appels
IEditorService.
Le clic dans la marge¶
Chaque clic gauche dans la colonne des signets d'un éditeur est publié sous la
forme de events::MarginClicked :
struct MarginClicked {
DocumentId doc;
std::int64_t line; // 0-based, level with the click
EditorMargin margin; // EditorMargin::Symbol
KeyModifiers modifiers; // key_modifier::kShift | kControl | kAlt | kMeta
};
Les noms des modificateurs suivent les chaînes de raccourcis portables :
kControl est Ctrl sous Windows et Linux et Commande (⌘) sous macOS, kAlt
est Alt / Option (⌥), kMeta est la touche Windows / Contrôle (⌃) sous macOS.
Les clics dans la colonne de repli replient et les clics sur les numéros de
ligne sélectionnent ; ni les uns ni les autres ne sont publiés.
Par défaut, l'éditeur bascule aussi un signet sur la ligne cliquée, comme il l'a toujours fait. Un plugin qui donne au clic un sens propre — un débogueur qui bascule un point d'arrêt d'un simple clic et modifie sa condition d'un clic avec modificateur — le revendique :
m_marginClaim = ctx.lineMarkers().claimMarginClick(); // MarginClickToken
m_clicks = ctx.events().subscribe<events::MarginClicked>(
[this](const events::MarginClicked& e) { onMarginClick(e); });
Tant qu'au moins une revendication est vivante — de n'importe quel plugin —, l'éditeur laisse le clic aux plugins et se contente de publier l'événement ; détruire le dernier jeton rétablit la bascule des signets. L'événement est publié que quelqu'un détienne une revendication ou non : un plugin qui ne fait qu'observer les clics n'en a besoin d'aucune. Les commandes des signets et leurs raccourcis ne sont pas affectés. Les revendications sont comptées, pas mises en file : deux plugins qui revendiquent tous deux le clic le reçoivent tous deux et ne doivent pas tous deux y réagir.
Python¶
Le même service est ctx.line_markers pour les
plugins Python, et le clic est
@ctx.events.on_margin_clicked.