Základním widgetem pro zobrazování a editaci textu v Gtk2 je textové pole. Umí řadu zajímavých věcí - mimo očekávaných funkcionalit také formátování textu a vkládání widgetů. Na to vše je potřeba dobře rozumět reprezentaci pozic, což jsou objekty určující místa v textovém poli.
27.1.2011 00:00 | Jiří Václavík | přečteno 11319×
Gtk2::TextView je často používaný widget na zobrazování a editaci textu. Tento widget má ještě podstatně více různých funkcí a nastavení, než textová pole v Tk a Wx, která jsme si již představovali.
Na úvod se podívejme na základní použití.
$text = Gtk2::TextView->new;
$buffer = $text->get_buffer();
$buffer->set_text("zkus sem neco napsat");
Právě jsme vytvořili textové pole a napsali do něj text. Uživatel ho může libovolně editovat.
.Ukázka Gtk2::TextView
Na první pohled je kód zdánlivě zbytečně složitý (Proč nepoužít něco jako Gtk2::TextView->set_text? Proč to musíme řešit přes buffer?). Uvidíme, že to zas o tolik složitější není a že lze takto s textem pohodlně manipulovat. Text (společně s informací, jak má být zobrazen) je zde reprezentován speciálním widgetem Gtk2::TextBuffer a až ten můžeme nastavit pomocí set_buffer.
Podívejme se na úvod jen ve stručnosti na některé základní metody Gtk2::TextView.
Metoda | Význam |
---|---|
set_wrap_mode | metoda zalamování; možné hodnoty jsou none, char nebo word |
set_editable | false pro read-only, true pro editaci |
set_cursor_visible | viditelnost kurzoru (kurzor bychom měli schovat vždy, když používáme read-only režim) |
set_justification | zarovnání textu; možné hodnoty jsou left, right, center |
set_left_margin, set_right_margin | okraje |
set_indent | odsazení |
Existuje-li metoda s názvem set_něco, pak také často existuje metoda get_něco, která detekuje aktuální nastavení.
Objekt typu Gtk2::TextIter reprezentuje pozici v bufferu. Pozice je platná vždy jen do okamžiku, kdy se obsah bufferu (tj. text v Gtk2::TextView) změní. Protože často chceme pozice zachovat i po změně obsahu bufferu, byly vytvořeny objekty typu Gtk2::TextMark, což jsou hýbající se pozice.
Gtk2::TextBuffer je vytvořen vždy, když vytvoříme Gtk2::TextView. Samozřejmě si ale můžeme standardní cestou vytvořit TextBuffer vlastní.
$textbuffer = Gtk2::TextBuffer->new;
Podívejme se na metody, kterými lze s textem v TextBufferu manipulovat.
Metoda | Význam |
---|---|
set_text | nastaví text |
insert($textiter, $text) | vloží $text na pozici $textiter (jak víme, pozice je objekt typu Gtk2::TextIter; více o tom dále) |
insert_at_cursor | vloží text na aktuální pozici |
insert_range($kam, $zacatek, $konec) | výsek určený dle $zacatek, $konec se uloží na pozici $kam |
get_text($zacatek, $konec, $ukazat_skryte) | vrátí výsek |
get_slice($zacatek, $konec, $ukazat_skryte) | vrátí výsek, obrázky budou vráceny jako znak 0xFFFC; více o tom dále |
get_line_count, get_char_count | zjistí počet řádků, resp. znaků |
Již víme, že pozici v TextBufferu uchovává objekt Gtk2::TextIter. Podívejme se na to, jak s pozicemi pracovat. Pozici v bufferu definujeme jednou z následujících metod zavolaných nad TextBufferem.
Příkaz | Kde se vytvoří TextIter? |
---|---|
$textiter=$textbuffer->get_start_iter; | před prvním znakem |
$textiter=$textbuffer->get_end_iter; | za posledním znakem |
$textiter=$textbuffer->get_iter_at_offset(50); | před padesátým znakem |
$textiter=$textbuffer->get_iter_at_line($radek); | před prvním znakem řádku č. $radek |
$textiter=$textbuffer->get_iter_at_line_offset(5, 10); | před 10. znak na 5. řádku |
$textiter=$textbuffer->get_iter_at_mark($mark); | na pozici existujícícho Gtk2::TextMark |
($ti1,$ti2)=$buffer->get_selection_bounds; | na hranice vybrané oblasti |
Nad objekty typu Gtk2::TextIter lze pak volat obrovské množství dalších metod. Podívejme se na seznam těch, které se mohou hodit.
Metoda | Význam |
---|---|
Detekce pozice v kontextu slov a řádků | |
starts_word, ends_word, inside_word, starts_sentence, ends_sentence, inside_sentence, starts_line, ends_line, is_end, is_start, is_cursor_position | zjišťování informací o pozici |
in_range($zacatek, $konec) | jsme v daném rozmezí? |
Zjišťování absolutní pozice | |
get_offset | vrátí číslo znaku |
get_line | vrátí číslo řádku |
get_line_offset | vrátí číslo znaku na řádku |
Pohyb TextIterů | |
forward_char, backward_char, forward_word_end, backward_word_start, forward_sentence_end, backward_sentence_start, forward_line, backward_line, forward_to_line_end, forward_cursor_position, backward_cursor_position, forward_to_end | pohyb na pozici v kontextu slov a řádků |
forward_chars($kolik), backward_chars($kolik), forward_word_ends($kolik), backward_word_starts($kolik), forward_sentence_ends($kolik), backward_sentence_starts($kolik), forward_lines($kolik), backward_lines($kolik), forward_cursor_positions($kolik), backward_cursor_positions($kolik) | to samé vícenásobně |
set_offset($offset) | nastaví na danou pozici |
set_line($radek) | nastaví na daný řádek |
set_line_offset($offset_na_radku) | nastaví na pozici na aktuálním řádku |
Získávání textu z TextBufferu | |
get_char | vrátí znak |
get_text($do) | vrátí úsek textu |
get_slice($do) | vrátí úsek textu |
Detekce dalších objektů na dané pozici | |
get_marks | vrátí seznam objektů Gtk2::TextMark |
get_tags | vrátí seznam tagů, tj. objektů typu Gtk2::TextTag |
get_pixbuf | vrátí seznam objektů Gtk2::PixBuf |
get_child_anchor | vrátí seznam objektů Gtk2::TextChildAnchor |
has_tag, begins_tag, ends_tag | detekce tagů |
Hledání v textu | |
forward_search($retezec, 'text-only', $kam_az) | vrátí dvouprvkový seznam s počáteční a koncovou pozicí typu Gtk2::TextIter, hledání dopředu; druhý parametr je typu Gtk2::TextSearchFlags |
backward_search($retezec, 'text-only', $kam_az) | vrátí dvouprvkový seznam s počáteční a koncovou pozicí typu Gtk2::TextIter, hledání dozadu; druhý parametr je typu Gtk2::TextSearchFlags |
Ostatní | |
get_attributes | vrátí objekt typu Gtk2::TextAttributes |
Gtk2::TextMark je podobný jako Gtk2::TextIter, avšak zachovává pozice při změnách TextBufferu.
Podívejme se opět, jak lze vytvořit objekt typu Gtk2::TextMark.
Příkaz | Kde se vytvoří TextMark? |
---|---|
$textmark = $textbuffer->get_insert | aktuální pozice kurzoru |
$textmark = $textbuffer->get_selection_bound | druhý konec výběru (prvním koncem je pozice kurzoru) |
$textmark = $textbuffer->create_mark($nazev, $textiter, $zleva) | Gtk2::TextMark s názvem $nazev se vytvoří na pozici $textiter s tím, že při vkládání na tuto pozici se posouvá/neposouvá doleva |
TextMarky lze ručně posouvat pomocí TextIterů jedním z následujících příkazů.
$textbuffer->move_mark($mark, $kam);
$textbuffer->move_mark_by_name($nazev, $kam);
Metodou get_mark získáme objekt typu Gtk2::TextMark na základě svého názvu.
Objekty typu Gtk2::TextTag jsou dalším obsahem TextBufferů. Obsahují informace o formátování. Každý tag funguje na daných pozicích a má nějaký svůj efekt (například zvětšuje písmo na druhém řádku).
Společně s bufferem se vždy vytvoří i objekt typu Gtk2::TextTagTable. Zde je uchováván seznam tagů.
Tag vytvoříme metodou create_tag zavolanou nad TextBufferem.
$tag = $textbuffer->create_tag($nazev, %vlastnosti);
Poté jsou dvě možnosti, jak tagy aplikovat. Buď přímo nebo opět podle názvu.
$textbuffer->apply_tag($tag, $zacatek, $konec);
$textbuffer->apply_tag_by_name($nazev, $zacatek, $konec);
Stejně tak můžeme tagy odstraňovat.
$textbuffer->remove_tag($tag, $zacatek, $konec);
$textbuffer->remove_tag_by_name($nazev, $zacatek, $konec);
Vlastnosti lze nastavovat metodou set_property.
Pomocí set_priority nastavujeme pro tag prioritu mezi 0 a velikostí Gtk2::TextTagTable.
Podívejme se na hash parametrů, které ovlivňují chování tagu. Jaké všechny paramety můžeme používat?
Klíč | Význam a hodnoty |
---|---|
background, background-gdk | barva pozadí pomocí řetězce, resp, Gtk2::Gdk::Color |
foreground, foreground-gdk | barva popředí pomocí řetězce, resp, Gtk2::Gdk::Color |
background-stipple, foreground-stipple | použije se na pozadí / popředí maska (bitmapa) |
font | písmo (například Times 12) |
size, size-points | velikost písma v Pango bodech, resp. v bodech |
scale | relativní velikost písma oproti okolí |
font-desc | styl písma (jako objekt typu Gtk2::Pango::FontDescription) |
family | například Times |
underline | podtržené písmo |
style, variant, weight, stretch | hodnoty jsou v dokumentaci |
pixels-above-lines, pixels-below-lines | mezera v pixelech nad / pod řádkem |
wrap-mode | jak zalamovat (none, word, char) |
justification | left, right, center |
direction | směr toku textu (right-to-left, left-to-right) |
left-margin, right-margin | okraje |
indent | odsazení odstavce |
strikethrough | dělení |
rise | posunutí nahoru (dolů při záporném parametru) |
background-full-height | má-li tag ovlivnit celý řádek ve smyslu barvy pozadí |
Zkusíme si vytvořit nějaký tag.
$tag = $buffer->create_tag("zvyrazneny_text",
"font" => "Helvetica 30",
"underline" => PANGO_UNDERLINE_DOUBLE,
"foreground" => "darkgreen",
"background-gdk" => Gtk2::Gdk::Color->new(60, 0, 200));
$buffer->apply_tag($tag, $buffer->get_iter_at_offset(3), $buffer->get_iter_at_offset(7));
Výsledek spatříme po spuštění aplikace.
TextView s otagovaným úsekem textu
Do textového pole lze vkládat také obrázky nebo rovnou celé widgety. Obrázky reprezentované jako Gtk2::PixBuf můžeme vkládat metodou insert_pixbuf na danou pozici. Takový obrázek se bude chovat jako unicodový znak 0xFFFC.
$textbuffer->insert_pixbuf($textiter, $pixbuf)
S widgety je to trochu složitější, ale v zásadě podobné. Vytvoříme objekt typu Gtk2::TextChildAnchor na místě, kde chceme, aby byl widget. Poté na toto místo widget vložíme.
$anchor = Gtk2::TextChildAnchor->new;
$textbuffer->insert_child_anchor($iter, $anchor);
$textview->add_child_at_anchor($widget, $anchor);
Ukážeme si konkrétnější příklad - TextView, do kterého vložíme text, obrázek a tlačítko.
$textview = Gtk2::TextView->new;
$buffer = $textview->get_buffer();
$buffer->set_text("nejaky text");
$tlacitko = Gtk2::Button->new("tlacitko");
$anchor = Gtk2::TextChildAnchor->new;
$buffer->insert_child_anchor($buffer->get_start_iter, $anchor);
$textview->add_child_at_anchor($tlacitko, $anchor);
$buffer->insert_pixbuf($buffer->get_start_iter, Gtk2::Gdk::Pixbuf->new_from_file("./Ghost.png"));
Text pak můžeme samozřejmě libovolně editovat. Vložené objekty se chovají jako znaky.
TextView s widgetem a obrázkem
Ukážeme si, jak lze zachytávat pozici myši a manipulovat s textem (popřípadě tagem) na jejím aktuálním místě.
Díky signálu motion_notify_event, který je emitován při změně pozice myši nad daným widgetem můžeme vyvolat akci. Ta bude například zvýrazňovat místo, kde je aktuálně myš.
$textview->signal_connect(motion_notify_event => \&akce);
Díky metodě window_to_buffer_coords zjistíme pixelové souřadnice. Ty nám metoda get_iter_at_location převede na pozici v textu, tj. objekt typu Gtk2::TextIter. Pak není nic jednoduššího, než s danou pozicí manipulovat.
Vytvoříme tedy tag, kterým budeme zvýrazňovat aktuální znak.
my $tag = $buffer->create_tag(undef, "foreground" => "white", "background" => "black");
Aplikujeme ho. Na to ovšem musíme mít dva TextItery (potřebujeme začátek i konec tagu). Vytvoříme si tedy kopii našeho TextIteru a posuneme ho o pozici vpřed. TextIter nemá vhodný konstruktor na kopírování objektů a poradíme si tak trochu oklikou.
my $textiter2 = $buffer->get_iter_at_offset($textiter->get_offset);
$textiter->forward_char;
$buffer->apply_tag($tag, $textiter2, $textiter);
Ještě bychom měli uchovávat starý tag a mazat ho po opuštění pozice. Zavedeme si tedy proměnnou $kurzor_tag, která bude reprezentovat týž objekt jako $tag do doby, než $tag zapomeneme. Tag pak smažeme přes tabulku tagu, kterou získáme z bufferu.
my $table = $buffer->get_tag_table;
$table->remove($kurzor_tag) if defined $kurzor_tag;
Podívejme se na celý zdrojový kód naší akce.
sub akce {
my($textview, $udalost) = @_;
my $textiter = $textview->get_iter_at_location(
$textview->window_to_buffer_coords("widget",$udalost->x, $udalost->y));
my $textiter2 = $buffer->get_iter_at_offset($textiter->get_offset);
$textiter->forward_char;
my $tag = $buffer->create_tag(undef, "foreground" => "white", "background" => "black");
my $table = $buffer->get_tag_table;
$table->remove($kurzor_tag) if defined $kurzor_tag;
$buffer->apply_tag($tag, $textiter2, $textiter);
$kurzor_tag=$tag;
}
Vidíme, že na místě kurzoru se aplikoval náš tag