Marcatori di riga e clic sul margine¶
Un marcatore di riga è un glifo nella colonna dei segnalibri dell'editor e/o
una tinta sull'intera riga, agganciato a una riga di un documento e portato con
essa mentre l'utente modifica il testo. I punti di interruzione (pieni, vuoti,
condizionali), una freccia dell'istruzione corrente e le strisce delle modifiche
del controllo di versione sono tutti marcatori di riga. Si raggiunge il servizio
tramite IPluginContext::lineMarkers() (PluginApi/ILineMarkerService.h).
Il plugin descrive come appare un tipo di marcatore e dove si trovano i marcatori di quel tipo; il disegno, il margine e i posti per i marcatori del motore di modifica appartengono all'editor. Nessuna dimensione in pixel, nessun numero del motore e nessun tipo Qt attraversa il confine.
Tipi¶
Registra ogni tipo di marcatore una sola volta, in initialize(), e conserva il
token:
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 */ }
- I colori sono id di categorie del tema che hai registrato tu stesso con
ctx.themePalette(), così i marcatori seguono il tema attivo e l'utente può ricolorarli nell'Editor dei temi; l'editor li riapplica a ogni cambio di tema. - I glifi sono un piccolo insieme fisso che qualsiasi host sa disegnare in un
margine stretto a qualsiasi zoom:
Circle(un punto di interruzione),CircleHollow(un punto di interruzione disattivato o non verificato),CircleBar(un punto di interruzione condizionale),Arrow(l'istruzione corrente),Strip(una riga modificata).Nonecrea un tipo che applica solo la tinta. - zOrder decide quale marcatore viene disegnato sopra quando più marcatori condividono una riga (la freccia dell'istruzione corrente sopra un punto di interruzione, un punto di interruzione sopra una striscia delle modifiche); a parità di valore sta sopra il tipo registrato per ultimo. L'ordine vale anche tra plugin diversi.
- La registrazione può fallire. Il motore di modifica ha un numero piccolo e fisso di posti per i marcatori condivisi da tutti i plugin: nove; un tipo con glifo e tinta ne usa due. Quando finiscono, il token torna vuoto e il log dice perché. Registra una volta sola i tipi che ti servono e conservali; non registrare mai un tipo per documento o per marcatore.
- Distruggere il token rimuove tutti i marcatori di quel tipo da tutti i documenti e ritira il tipo.
Marcatori¶
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 restituisce 0, e non posiziona nulla, quando il tipo non è vivo, il
documento non è aperto o la riga è fuori dal documento. Una riga non viene
limitata: un punto di interruzione sulla riga sbagliata è peggio di nessuno. Un
handle è un semplice valore, non un token; tienine quanti te ne servono.
Che cosa succede poi a un marcatore:
- Le modifiche lo spostano con la sua riga: le righe inserite o eliminate sopra lo spostano, scrivere sulla riga lo lascia dov'è; è così che un punto di interruzione segue il codice.
- La sua riga viene eliminata (fa parte di un intervallo rimosso): sopravvive e si unisce alla riga in cui iniziava l'intervallo eliminato. Due marcatori possono così finire sulla stessa riga.
- L'intero buffer viene sostituito (un ricaricamento dopo una modifica
esterna,
IEditorService::setText): ogni marcatore resta sullo stesso numero di riga; quello la cui riga non esiste più viene rimosso. L'editor non confronta il testo vecchio con quello nuovo. - Il documento viene chiuso: i suoi marcatori vengono rimossi e i loro
handle non valgono più. L'editor non salva mai i marcatori: un plugin che
conserva dei segni oltre una chiusura (punti di interruzione) li tiene nel
proprio modello e li aggiunge di nuovo su
DocumentOpened(un file riaperto è un nuovoDocumentId).
I marcatori appartengono al documento: posizionati una volta, compaiono in
ogni riquadro di un documento diviso. La
minimappa, la sua anteprima al passaggio del mouse e la vista di confronto non
li mostrano mai. I segnalibri non sono marcatori di questo servizio; mantengono
le proprie chiamate di IEditorService.
Il clic sul margine¶
Ogni clic sinistro nella colonna dei segnalibri di un editor viene pubblicato
come 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
};
I nomi dei modificatori seguono le stringhe portabili delle scorciatoie:
kControl è Ctrl su Windows e Linux e Comando (⌘) su macOS, kAlt è Alt /
Opzione (⌥), kMeta è il tasto Windows / Control (⌃) su macOS. I clic nella
colonna di piegatura piegano e i clic sui numeri di riga selezionano; nessuno
dei due viene pubblicato.
Per impostazione predefinita l'editor attiva o disattiva anche un segnalibro sulla riga cliccata, come ha sempre fatto. Un plugin che dà al clic un significato proprio, come un debugger che attiva o disattiva un punto di interruzione con un clic semplice e ne modifica la condizione con un clic con modificatore, lo rivendica:
m_marginClaim = ctx.lineMarkers().claimMarginClick(); // MarginClickToken
m_clicks = ctx.events().subscribe<events::MarginClicked>(
[this](const events::MarginClicked& e) { onMarginClick(e); });
Finché è viva almeno una rivendicazione, di qualsiasi plugin, l'editor lascia il clic ai plugin e si limita a pubblicare l'evento; distruggere l'ultimo token ripristina l'attivazione dei segnalibri. L'evento viene pubblicato che qualcuno detenga una rivendicazione o no, quindi un plugin che si limita a osservare i clic non ne ha bisogno. I comandi dei segnalibri e le loro scorciatoie non sono interessati. Le rivendicazioni vengono contate, non messe in coda: due plugin che rivendicano entrambi il clic lo ricevono entrambi e non devono reagire entrambi.
Python¶
Lo stesso servizio è ctx.line_markers per i
plugin Python, e il clic è
@ctx.events.on_margin_clicked.