CL_ListBox
Třída CL_ListBox reprezentuje výběrový seznam. Patří ke komponentám, jejichž atributy je možné nastavovat v GDF. V případě listboxu může takový záznam v GDF vypadat třeba nějak takto:
<listbox name="ListBox" x="65" y="75" width="80" height="70">
<item value="Panacek" />
<item value="Alien" />
</listbox>
Kromě toho, že v GDF nastavíme rozměry listboxu, můžeme zde uvést i seznam položek. Položky jsou textové řetězce, které jsou v listboxu zobrazeny na jednotlivých řádcích.
Pokud je počet položek listboxu tak velký, že je není možné zobrazit všechny najednou, je třeba, aby se objevil scrollbar umožňující posun po položkách. Ve verzi 0.7.8 nastává s objevováním se scrollbaru trochu problém. Pokud totiž vytvoříme listbox pouze z GDF, který má tolik položek, že bychom potřebovali scrollbar, tento se automaticky nevytvoří.
Jelikož je možné analogickou akci provést pomocí metody insetr_item(), kterou si popíšeme za okamžik, s tím, že se scrollbar objeví, jsem přesvědčen, že záměrem tvůrců je, aby tomu tak bylo v obou případech. Procházel jsem si proto zdrojové kódy listboxu a zjistil jsem, že řešení této situace by pro nás uživatele mohlo být následující.
Poté, co vytvoříme listbox z GDF, získáme si na něj ukazatel:
LBox = (CL_ListBox*)CompMan.get_component("ListBox");
Třída CL_ListBox obsahuje metodu sig_item_added(), která vrací ukazatel na signál CL_Signal_v1<int>, který by měl být vyslán po každém přidání položky do listboxu. Vyslání tohoto signálu má jako vedlejší efekt akci, která zkontroluje, jestli není třeba scrollbar. Celý problém podle mne tkví v tom, že po přidání položek při konstrukci z GDF tento signál vyslán není, a nedojde tedy k potřebné aktualizaci scrollbaru.
Z tohoto popisu již možná většinu z vás napadá řešení. Nešlo by, abychom tento signál vyslali sami? Já myslím, že ano. Řešení by tedy mohlo vypadat takto:
Lbox->sig_item_added()(-1);
Pokud se vám nelíbí výraz ()(-1), vězte, že operátor () třídy CL_Signal_vx je zkratkou za call(), tedy je možné ekvivalentně napsat:
Lbox->sig_item_added().call(-1);
Mně osobně toto řešení funguje a je na něm hezké, že představuje pouze jeden řádek kódu, avšak předpokládám, že takhle to autoři knihovny nechtěli, a že se tedy nejspíš v příští verzi knihovny objeví mnohem čistší řešení a budeme tohoto řádku ušetřeni.
Mimochodem scrollbar se objeví i v případě, že nad listboxem pootočíme scrollovacím tlačítkem na myši (tedy samozřejmě v případě, že je počet položek dost velký).
Pověděli jsme si o jedné mušce, kterou ClanLibovský listbox v současnosti má, a o tom, jak ji napravit. Zbývá nám tedy ještě popsat si metody, které nám nabízí třída CL_Listbox:
int get_count() const;
Vrací celkový počet položek.
std::vector<CL_ListItem *> &get_items() const;
Vrací vektor všech položek v listboxu reprezentovaných ukazateli na objekty třídoy CL_ListItem, což je velice jednoduchá třída uchovávající text položky jako string v proměnné str (CL_ListItem::str je public). Třída CL_ListItem definuje operátory < a == pro porovnání ekvivalentní s porovnáváním std::string. Také obsahuje dva veřejné příznaky bool selected a bool delete_item.
std::vector<std::string> get_selected_items() const;
Vrací vektor textů všech vybraných položek, což se nám hodí zejména tehdy, je-li listbox v režimu umožňujícím výběr několika položek najednou.
const std::string &get_current_text() const;
Vrací text první vybrané položky, nebo "".
CL_ListItem *get_item(int index) const;
Vrací položku zadaného indexu.
const std::string &get_text(int index) const;
Vrací text položky na zadané pozici.
int get_current_item() const;
Vrací index první vybrané položky, nebo –1.
bool is_selected(int index) const;
Odpoví, zda je položka zadaného indexu vybraná.
bool is_multi_selection() const;
Odpoví, zda je listbox v režimu pro výběr více položek najednou.
int get_top_item() const;
Vrací index nejvýše umístěné viditelné položky.
int insert_item(CL_ListItem *item, int index = -1, bool delete_item = false);
int insert_item(const std::string &text, int index = -1);
Pomocí insert_item() můžeme vložit do listboxu novou položku, a to buď jako string, nebo jako CL_ListItem* na uvedenou pozici (index). Pokud nezadáme druhý parametr, bude položka vložena na konec (což je mimochodem ve verzi 0.7.8 jediná možnost).
void remove_item(int index);
Odstraní položku zadaného indexu. Pokud odstraní aktuálně vybranou položku, bude vybrána další položka v pořadí a vyšle se signál sig_highlighted().
void change_item(CL_ListItem *item, int index); void change_item(const std::string &text, int index);
Nahradí položku zadaného indexu novou položkou.
void set_current_item(int index);
Nastaví aktuálně vybranou položku.
void set_selected(int index, bool select);
Chová se, jako bychom klikli na položku příslušného indexu myší.
void clear_selection();
Zruší aktuální výběr.
void select_all(bool select = true);
Vybere vše (nebo naopak, zadáme-li false).
void invert_selection();
Po zavolání této metody budou vybrány právě ty položky, které před zavoláním vybrány nebyly.
void set_multi_selection(bool enable = true);
Povolí nebo zakáže výběr několika položek najednou.
void set_top_item(int index);
Posune scrollbarem tak, aby nejvyšší zobrazená položka měla zadaný index.
void sort(bool ascending = true);
Setřídí položky abecedně (anglicky).
void clear().
Odstraní všechny položky.
Třída CL_ListBox vysílá následující signály:
CL_Signal_v1<int> &sig_highlighted();
Signál vyslaný, pokud vybereme novou položku. Jako parametr nese index této nově vybrané položky.
CL_Signal_v1<int> &sig_activated();
Signál vyslaný, pokud dvakrát klikneme na položku (příp. zmáčkneme space nebo return). Parametr je index této položky.
CL_Signal_v0 &sig_selection_changed();
Signál vyslaný, pokud se nějak změní výběr.
CL_Signal_v1<int> &sig_item_added();
Zmíněný signál, který by měl být vyslán po každém přidání položky do listboxu.
CL_Signal_v1<int> &sig_item_removed();
Signál vyslaný při odstranění položky.
CL_Signal_v0 &sig_clear();
Signál vyslaný po použití metody clear().
Závěrem
Náš výčet komponent dosud nezahrnuje například FileDialog, CheckBox, InputBox a další. Důvodem je zejména to, že jsou většinou ještě ve stádiu vývoje, které nezaručuje plnou funkčnost, a také to, že si myslím, že už je na čase podívat se zase na něco alespoň trochu jiného. Ještě nejsem úplně rozhodnut, co to bude, ale asi bych ještě nechtěl úplně opustit téma tvorby GUI.
V době, kdy píši tento článek je již opět možné z www.clanlib.org stahovat, takže kdo ještě nemáte verzi 0.7.8, máte konečně šanci ji získat.
Dostal jsem od vás nějaké náměty na příklady, za což děkuji a doufám, že se mi něco podaří zařadit (tj. že budu mít čas to naprogramovat :-)).