Knihovna ClanLib (22)

11. 10. 2004
Doba čtení: 5 minut

Sdílet

V dnešním dílu seriálu budeme pokračovat v povídání o tvorbě menu v ClanLibu. Zaměříme se zejména na vztah mezi třídami CL_Menu, CL_MenuNode a CL_MenuItem. Ukážeme si, že položkou menu může být vlastně libovolná komponenta.

Vztah CL_Menu, CL_MenuNode a CL_MenuItem

Třída CL_Menu reprezentuje menu více méně tím způsobem, že si někde uvnitř uchovává seznam všech uzlů, z nichž se toto menu skládá, tj. seznam ukazatelů na objekty třídy CL_MenuNode.

Tento seznam ukazatelů na uzly je vlastně jediná informace, kterou má objekt třídy CL_Menu o struktuře a vlastnostech menu jím reprezentovaného. To vlastně znamená, že pokud se menu chce o sobě něco dozvědět, musí se na to zeptat svých uzlů. Tedy objekty třídy CL_MenuNode jsou nositeli informací o struktuře menu, tj. informací, ze kterých jsme schopni sestrojit stromové schéma, jaké jste si mohli prohlédnout na obrázcích v minulém dílu seriálu. Tyto objekty však nejsou přímými nositeli informací o vzhledu příslušný uzlů, protože vzhled je něco, čím se v zásadě mohou jednotlivé uzly zcela odlišovat (my o možnost takového odlišení budeme stát zejména u listů).

Řešení potenciální různorodosti vzhledu jednotlivých uzlů je vlastně velice jednoduché. Každý uzel obsahuje ukazatel na CL_Component, tj. na komponentu, která ho bude představovat. Z tohoto úhlu se tedy můžeme na menu v ClanLibu dívat jako na strom komponent GUI. Tento pohled je možná až překvapivě obecný. Překvapivější však je, že opravdu odpovídá realitě, že uzlem menu může být třeba listbox nebo scrollbar nebo jakýkoliv jiný korektní potomek CL_Component (třeba námi napsaný).

Zkuste si nyní vzpomenout na příklad kódu z minula, v němž jsme použili metodu create_item() třídy CL_Menu k vytvoření korektně fungujícího menu. Po přečtení předchozích řádků si možná říkáte, na jakou komponentu to asi uzly odkazovaly. Pokud jste si přečetli nadpis této části a máte dobrý odhad, asi již tušíte odpověď. Pokud jste si minule stáhli ukázkový program a pročetli si jeho zdrojáky, možná jste si všimli následujících řád­ků:

// Nasledujici dva radky jsou ekvivalentem k Menu->create_item("2,1/2,2", "X/Y");
// CL_MenuNode* Uzel = Menu->create_node("2,1/2,2", "X/Y");
// new CL_MenuItem("Y", Uzel); 

Nebudu vás déle napínat, odpověď opravdu zní CL_MenuItem. CL_MenuItem je prostě třída, která slouží jako standardní komponenta odpovídající uzlům ClanLibovského menu. Pokud tedy nespecifikujeme jinou komponentu, použije se tato. Její chování je přesně takové, jaké obvykle požadujeme.

Malý problém

Co se týče výše uvedených zakomentovaných řádků kódu, nelamte si hlavu s tím, že vám možná připadají tak trochu jako magie (zejména ten poslední řádek se tváří celkem podezřele). Trik je prostě v tom, že o uvolnění dynamicky vytvořeného objektu třídy CL_MenuItem se postará jeho rodičovská komponenta, tj. o řádek výše vytvořený Uzel. Tedy on by to měl dělat – myslím, že to ještě dosud není implementováno – takže pokud ve vašem programu opakovaně vytváříte menu a někam se vám ztrácí paměť, máte tu námět, kde ji hledat :-). Řešení takovéto situace se nabízí několik. Počet menu, která ve vašem programu vytvoříte, může být dostatečně malý, aby vám to nestálo za námahu tuto paměť před skončením programu uvolňovat. Můžete také počkat, až autoři tuto chybu odstraní, což jistě nebude trvat moc dlouho. Případně můžete provádět uvolňování ručně (metoda get_data() třídy CL_MenuNode vrací ukazatel, na nějž potřebujete aplikovat operátor delete – doporučuji vám tento ukazatel po ručním uvolnění také vynulovat, abyste se nedostali do problémů, až začne fungovat automatické uvolňování). Doufám, že vás předchozí řádky příliš nevystrašily, protože si po pravdě řečeno příliš neumím představit reálnou situaci, kdy by pro vás popsaná chyba znamenala vážnější problém.

Metoda create_node() a její využití

V předchozích odstavcích jsme narazili na metodu třídy CL_Menu,

CL_MenuNode *create_node( const std::string &path, const std::string &labels=std::string()); 

která je velice podobná nám již známé metodě create_item(). Stejný je typ návratové hodnoty i význam jednotlivých parametrů, avšak rozdíl je v tom, že uzel, na nějž bude ukazovat návratová hodnota, nebude odkazovat na platný objekt třídy CL_MenuItem. To znamená, že metoda create_node() nám bude užitečná zejména v případě, kdy budeme chtít, aby vrácený list ukazoval na jinou komponentu, než je CL_MenuItem. Vše snad osvětlí příklad:

CL_MenuNode* UzelSListboxem = Menu.create_node("Komponenty/Listbox/uzel_1");
UzelSListboxem->set_close_on_click(false);
CL_ListBox* ListboxVMenu = new CL_ListBox(CL_Rect(17,0,140,50), UzelSListboxem );

ListboxVMenu->insert_item(std::string("prvni polozka"));
ListboxVMenu->insert_item(std::string("druha polozka")); 

a obrázek znázorňující výsledek:

Zařídili jsme to takto, že jednou položkou menu je nám již známý listbox. Použili jsme přitom jednu z metod (první z následujícího seznamu), které nám nabízí třída CL_MenuNode. Pojďme se na tyto metody podívat blíže.

Metody třídy CL_MenuNode

void set_close_on_click(bool close);

Pomocí této metody nastavujeme, zda se má menu sbalit po kliknutí na tento uzel.

void collapse();
void open_submenu();

Metoda colapse() sbalí submenu (podstrom) tohoto uzlu. Metoda open_submenu() ho naopak rozbalí.

CL_Component *get_data();

Tato metoda vrací ukazatel na komponentu, kterou tento uzel představuje. Obvykle se tedy jedná o ukazatel na objekt třídy CL_MenuItem, ale může jít i o jinou komponentu, jak jsme si před chvílí ukázali.

bool has_mouse_over();
bool has_mouse_in_submenus();

První z metod odpoví, je-li kurzor myši nad tímto uzlem, druhá odpoví, jestli je kurzor myši někde v oblasti submenu tohoto uzlu.

bool has_submenu();

Odpoví, zda má tento uzel submenu. Vrátí-li false, znamená to, že je tento uzel listem.

bool has_open_submenu();

Odpoví, zda má tento uzel rozbalené submenu.

CL_Menu *get_submenu();
void set_submenu(CL_Menu *menu);

První z metod vrátí submenu tohoto uzlu jako ukazatel na CL_Menu, druhá ho nastaví.

CL_Menu *get_parent_menu();
void set_parent_menu(CL_Menu *menu);

První vrátí odkaz na menu, jehož je tento uzel součástí, druhá jej nastaví.

std::string get_name();

Vrátí část řetězce, který je posledním dílkem cesty k tomuto uzlu.

ict ve školství 24

CL_Signal_v0 &sig_clicked();

Vrátí signál, který je vyslán v případě, že uživatel klikne na tento uzel.

Závěr

Na příště nám z tématu tvorby menu zbývá doplnit ještě několik informací o metodách třídy CL_Menu a třídy CL_MenuItem.

Autor článku