« Tk: alcuni widget: comando menuAppunti di Tcl/TkTk: alcuni widget: comando scrollbar »

Tk

comando text

text pathName ?options?

Il widget di tipo text consente la visualizzazione e l'editing di una o più linee di testo. Si tratta di un widget molto potente, che consente di inserire diversi tipi di annotazioni nel testo, addirittura di avere immagini o finestre incorporate. È quindi possibile scrivere editor con supporto di syntax highlighting o interi word processor con questo controllo. Consultate la manpage text(n) per sfruttare queste funzionalità avanzate.

Alcune opzioni specifiche di questo widget:

-wrap
Indica come mostrare le linee di testo che sono troppo lunghe per essere visualizzate interamente in una linea singola del widget text. I possibili valori sono uno tra none, char, word. none fa sì che ciascuna linea di testo appaia esattamente come una singola linea sullo schermo; eventuali caratteri extra non saranno visualizzati (occorrerà muovere il puntatore per ottenere uno scrolling). Negli altri due modi invece ogni linea di testo verrà automaticamente suddivisa in diverse linee quando necessario, in modo da mantenere sempre visibili tutti i caratteri. Nel modo char le linee possono essere spezzate dopo qualsiasi carattere; nel modo word vengono spezzate solo ai confini di una parola. La suddivisione viene fatta solo all'atto della visualizzazione: i caratteri newline vengono inseriti nel testo solo quando l'utente preme il tasto Return (Invio).
-width
Specifica la larghezza desiderata dell'area di testo in unità di caratteri riferite al font stabilito tramite l'opzione -font. Se il font non ha una larghezza uniforme dei caratteri, viene usata la larghezza del carattere "0" per tradurre da unità di carattere in unità di schermo.
-height
Specifica l'altezza desiderata in unità di caratteri del font impostato indicato dall'opzione -font. Deve essere almeno uno.
-state
Stato del testo scelto tra normal e disabled. Quando il testo è disabilitato non può essere modificato e non sarà visualizzato il cursore che marca il punto di inserzione, anche se il widget riceve il focus.

Alcuni comandi di widget più importanti:

pathName get index1 ?index2?
Ritorna un intervallo di caratteri del testo contenuto nel widget pathName. index1 specifica l'indice di partenza, mentre index2 specifica l'indice finale (il carattere all'indice index2 è escluso, non viene ritornato). Se manca index2 ritorna il singolo carattere all'indice index1. Se il range specificato non individua alcun carattere (ad es. index1 è oltre la fine del file o index2 è minore o uguale di index1), ritorna la stringa vuota senza che si verifichi alcun errore.
pathName insert index chars ?tagList chars tagList ...?
Inserisce la stringa chars subito prima del carattere alla posizione index. Se index si riferisce alla posizione finale del testo (il carattere dopo l'ultimo newline), il nuovo testo viene inserito comunque prima dell'ultimo newline, in modo da garantire che l'ultima linea sia sempre terminata da newline. Per altri dettagli consultare la manpage text(n).

Un indice indica una posizione nel testo e deve consistere di un valore base (obbligatorio) che indica un punto di partenza e da zero o più modificatori che indicano uno scostamento dal punto di partenza. I valori di base possono essere di vario tipo. line.char indica il carattere numero char della linea numero line. Le linee sono numerate a partire da 1, mentre i caratteri all'interno di una linea sono numerati a partire da 0. Il valore end per char indica il numero di colonna del carattere newline che termina la linea. Se l'intero valore base è end, questo indica la fine del testo (l'indice del carattere che si dovrebbe trovare proprio dopo l'ultimo finelinea).

Come modificatori si possono usare le forme + count chars per muoversi in avanti di count caratteri, - count chars per muoversi indietro e analogamente + count lines, - count lines per muoversi di un certo numero di linee.

Un marcatore (MARK) è un tipo di annotazione fluttuante nel testo. Ha un nome e si riferisce ad una singola posizione del testo, ma non è associato permanentemente ad un certo carattere. È associato invece allo spazio tra due caratteri. Se i caratteri attorno ad un mark vengono cancellati, il mark rimane e avrà nuovi caratteri confinanti. L'indice equivalente di un mark può variare nel tempo mentre il testo viene editato. Il nome di un mark può essere usato come base di un indice e indica il carattere che si trova giusto dopo il marcatore specificato. Se i caratteri attorno ad un mark vengono cancellati, il mark rimane e avrà nuovi caratteri confinanti. Il comando:

pathName mark set markName index

imposta il mark chiamato markName alla posizione che precede il carattere di indice index. Se il mark markName esiste già viene spostato, altrimenti viene creato un nuovo mark di nome markName. Ritorna una stringa vuota.

Il mark insert è speciale: rappresenta la posizione del cursore di inserzione, che verrà automaticamente disegnato in questo punto ogni volta che il widget di tipo text riceve il focus. Viene definito automaticamente quando viene creato un widget text e non può essere rimosso tramite il comando di widget pathName mark unset.

Esempi: leggere il carattere subito dopo il cursore e la posizione del cursore in un widget .t di tipo testo:

.t get insert
.t index insert

Adesso mostrerò un espediente per intercettare i vari comandi che vengono invocati su un widget dai suoi binding predefiniti:

text .t
pack .t
rename .t .t.orig
proc .t {option args} {
  puts "$option $args"
  eval ".t.orig $option $args"
}

Ad esempio supponendo che cliccate sul widget per dargli il focus, sotto sotto vengono invocati comandi di widget simili ai seguenti:

index @112,77
bbox 1.0
mark set insert 1.0
mark set anchor insert
cget -state
tag remove sel 0.0 end

Quello che avviene è che viene chiesto l'indice del carattere che occupa uno spazio che include il punto di coordinate @112,77 (il vostro valore può essere diverso a seconda di dove cliccate) misurate rispetto all'angolo superiore sinistro del widget. Col sottocomando bbox vengono chieste coordinate e dimensioni dell'area occupata dal carattere di indice 1.0. Poi il cursore viene posizionato all'indice 1.0 e così pure il marcatore anchor, usato per indicare l'inizio di una eventuale selezione. Viene chiesto al widget qual'è il suo stato e viene rimossa una eventuale selezione.

Supponendo ora che uno digiti la lettera T si ottiene:

cget -state
compare sel.first <= insert
insert insert T
see insert

Il sottocomando compare confronta due indici. Il sottocomando insert inserisce la lettera T nella posizione del cursore. Il sottocomando see aggiusta la vista in modo che il carattere all'indice insert sia visibile.

Se premete backspace avrete:

tag nextrange sel 1.0 end
compare insert != 1.0
delete insert-1c
see insert

Qui notate come la cancellazione del carattere che precede il cursore sia ottenuta tramite il sottocomando delete e notate anche l'espressione indice insert-1c.

Questa tecnica di intercettazione, basata sul comando rename(n), non è solo utile didatticamente. Sarebbe possibile usarla nel nostro editor di testi tpad per implementare la funzione di undo. Poiché però Tk, a partire dalla versione 8.4 incorpora le funzionalità di undo nel widget text(n), ho preferito usare queste, semplificando notevolmente il programma. tpad richiederà quindi una versione di Tk >=8.4

Ispezioniamo ora gli eventi legati ai widget text(n). È raccomandabile la lettura della manpage bind(n) per una migliore comprensione. Questo comando restituisce la lista delle sequenze per le quali esiste un binding di classe Text (ossia un binding che si applica a tutti i widget di tipo Text). Ho dovuto spezzare l'output su più linee per una migliore presentazione.

% bind Text
<Button-5> <Button-4> <MouseWheel> <B2-Motion> <Button-2> <Control-Key-h>
<Meta-Key-Delete> <Meta-Key-BackSpace> <Meta-Key-greater> <Meta-Key-less>
<Meta-Key-f> <Meta-Key-d> <Meta-Key-b> <Control-Key-v> <Control-Key-t>
<Control-Key-p> <Control-Key-o> <Control-Key-n> <Control-Key-k>
<Control-Key-f> <Control-Key-e> <Control-Key-d> <Control-Key-b>
<Control-Key-a> <Key-KP_Enter> <Key-Escape> <Control-Key> <Meta-Key>
<Alt-Key> <Key> <Key-Insert> <<PasteSelection>> <<Clear>> <<Paste>>
<<Copy>> <<Cut>> <Control-Key-backslash> <Control-Key-slash>
<Shift-Key-Select> <Control-Shift-Key-space> <Key-Select>
<Control-Key-space> <Key-BackSpace> <Key-Delete> <Key-Return>
<Control-Key-i> <Control-Shift-Key-Tab> <Control-Key-Tab> <Shift-Key-Tab>
<Key-Tab> <Control-Shift-Key-End> <Control-Key-End>
<Control-Shift-Key-Home> <Control-Key-Home> <Shift-Key-End> <Key-End>
<Shift-Key-Home> <Key-Home> <Control-Key-Next> <Control-Key-Prior>
<Shift-Key-Next> <Key-Next> <Shift-Key-Prior> <Key-Prior>
<Control-Shift-Key-Down> <Control-Shift-Key-Up> <Control-Shift-Key-Right>
<Control-Shift-Key-Left> <Control-Key-Down> <Control-Key-Up>
<Control-Key-Right> <Control-Key-Left> <Shift-Key-Down> <Shift-Key-Up>
<Shift-Key-Right> <Shift-Key-Left> <Key-Down> <Key-Up> <Key-Right>
<Key-Left> <Control-Button-1> <ButtonRelease-1> <B1-Enter> <B1-Leave>
<Triple-Shift-Button-1> <Double-Shift-Button-1> <Shift-Button-1>
<Triple-Button-1> <Double-Button-1> <B1-Motion> <Button-1>

Questo restituisce la lista di tutti gli eventi virtuali definiti:

% event info
<<Cut>> <<Paste>> <<PrevWindow>> <<PasteSelection>> <<Copy>>

Ecco poi le liste delle sequenze degli eventi fisici associati ai vari eventi virtuali:

% event info <<Copy>>
<Control-Key-c> <Key-F16> <Meta-Key-w>

% event info <<PasteSelection>>
<ButtonRelease-2>

% even info <<PrevWindow>>
<Shift-Key-Tab>

% event info <<Paste>>
<Control-Key-v> <Key-F18> <Control-Key-y>

% event info <<Cut>>
<Control-Key-x> <Key-F20> <Control-Key-w>

Vediamo ora gli script legati agli evento virtuali specificati per la classe Text:

% bind Text <<Copy>>

    tk_textCopy %W

% bind Text <<PasteSelection>>

    if {!$tkPriv(mouseMoved) || $tk_strictMotif} {
        tkTextPaste %W %x %y
    }

% bind Text <<Paste>>

    tk_textPaste %W

% bind Text <<Cut>>

    tk_textCut %W

Control-v è legato allo scrolling di una pagina avanti:

% bind Text <Control-Key-v>

    if {!$tk_strictMotif} {
        tkTextScrollPages %W 1
    }

Per usarlo per fare il pasting è sufficiente distruggere il binding precedente, con l'istruzione:

% bind Text <Control-Key-v> {}

Analogamente il binding <Control-Key-o> viene usato per spezzare una linea senza spostare il cursore.

% bind Text <Control-Key-o>

    if {!$tk_strictMotif} {
        %W insert insert \n
        %W mark set insert insert-1c
    }

Se vogliamo usarlo per richiamare la finestra di apertura dei file, dobbiamo distruggere il binding di default, onde evitare che quando si preme Control-o mentre .text ha il focus, vengano eseguite entrambe le azioni!

% bind Text <Control-Key-o> {}

Il file /usr/local/lib/tk8.4/text.tcl definisce in Tcl i binding per il widget text(n).

« Tk: alcuni widget: comando menuAppunti di Tcl/TkTk: alcuni widget: comando scrollbar »