CL_Menu
Asi nikoho z těch, kdo si přečetli předchozí díly věnované tvorbě GUI, nepřekvapí, že CL_Menu je potomkem třídy CL_Component.
Při tvorbě menu zatím není podporována žádná spolupráce s RDF resp. GDF soubory. To znamená, že všechny naše představy budeme muset napsat přímo do zdrojových kódů. Podle mého názoru to je trošku škoda, avšak odhaduji, že se jedná pouze o přechodný stav. Myslím, že se podpora GDF třídou CL_Menu objeví v některé z příštích verzí knihovny, a jestli ne, tak to je myslím docela zajímavý námět, na jehož realizaci bychom si mohli někdy v budoucnu demonstrovat tvorbu vlastní komponenty.
V tomto dílu si tedy popíšeme, jak vytvořit menu dosud podporovanými prostředky. Uvidíte, že to půjde i tak docela dobře.
Než se pustíme do dalšího povídání, můžete se podívat na ukázku z programu, který si můžete stáhnout na konci článku, chcete-li získat názornější představu o tom, o čem vlastně bude řeč:
Na menu se můžeme dívat jako na zakořeněný strom. To znamená, že se skládá z jednotlivých uzlů propojených cestami tak, že mezi každými dvěma uzly existuje právě jedna cesta. Jeden z uzlů nazýváme kořen. Uzly, ze kterých vychází pouze jedna cesta, nazýváme listy.
V takovémto případě je pak každý uzel jednoznačně určen cestou z kořene k sobě samému (z výše uvedeného plyne, že taková cesta je právě jedna). Pojmenujeme-li uzly jednoznačnými jmény (tj. žádné dva uzly nebudou mít stejné jméno), budeme pomocí těchto jmen schopni jednoznačně zadat cestu z kořene do daného uzlu. Takovým pojmenováním cesty z kořene do zvoleného uzlu může být řetězec skládající se ze jmen uzlů, které na této cestě postupně navštívíme, oddělených například znakem / (lomítko se pak pochopitelně nesmí vyskytovat ve jménech uzlů). Takovýto řetězec nejenže jednoznačně popisuje cestu, ale také jednoznačně určuje cílový uzel. Dokonce v něm můžeme vynechat i první část, tj. pojmenování kořenového uzle, které by se zbytečně vyskytovalo v každém takovém řetězci.
Podívejme se na následující obrázek, který snad pomůže vytvořit si názornější představu o právě popisovaném:
Obrázek totiž jednu takovou strukturu menu znázorňuje. Obdelníky představují listy, ostatní uzly jsou elipsy, spojovací čáry odpovídají cestám. Dvojice čísel oddělených čárkou v levé části každého uzlu představuje ono jednoznačné pojmenování všech uzlů sloužící k zápisu cest. Naopak písmena v pravé části uzlů jsou nejednoznačným pojmenováním a představují text, který uvidí uživatel jako popis daného uzlu. Vpravo nad několika uzly je také uvedena cesta z kořene do tohoto uzlu.
S ClanLibovským menu je to přesně takto. Každý uzel obsahuje jednak string, který říká, jak se tento uzel bude jmenovat, jednak string, který říká, jaký text uvidí uživatel. Asi nás tedy nepřekvapí, že metoda třídy CL_Menu sloužící k vytvoření uzlu (obvykle listu) vypadá takto:
CL_MenuNode *create_item( const std::string &path, const std::string &labels=std::string());
První parametr path představuje cestu k tomuto uzlu (jména uzlů na cestě ve směru od kořene oddělená lomítkem). Druhý parametr představuje texty zobrazované uživateli (také oddělené lomítkem) v pořadí odpovídajícím uzlům z path. Například je-li Menu objekt třídy CL_Menu, bude mít následující příkaz níže popsaný význam:
Menu.create_item("0/1/2", "Soubor/Nový/Textový dokument");
Vytvoříme uzel 0 (uživatelem viděný jako Soubor), který bude přímým poduzlem uzlu root menu, což je kořen (to pro nás může být jakési neviditelné individuum, o jehož existenci se nemusíme starat). Dále vytvoříme uzel 1 (uživatelem viděný pod názvem Nový) jako poduzel uzlu 0 a podobně uzel 2 (Textový dokument) jako poduzel uzlu 1. V tuto chvíli bude uzel 2 listem (CL_Menu to pozná tak, že tento uzel nemá žádné submenu) a jeho chovaní se bude odpovídajícím způsobem lišit.
Listy jsou vlastně tím podstatným, co uživatel potřebuje v menu nalézt, aby nějakým způsobem ovlivnil chování programu. Ostatní uzly slouží k vytvoření požadované hierarchie. List vytvořený takto metodou create_item() se bude chovat tak, že poté, co na něj klikneme, celé menu zmizí a my budeme schopni tuto událost odchytit.
Vraťme se však ještě k metodě create_item(). Jak vidíte, druhý parametr je nepovinný. Pokud ho nezadáme, bude se příkaz chovat tak, jako kdybychom zadali oba dva parametry shodné, tj. vlastně se za druhý parametr doplní parametr první a my se neupíšeme k smrti. Pouze je třeba dát si pozor na to, abychom se v pojmenování resp. v cestách nedopustili nejednoznačností. Ohlídat si to musíme sami, třída CL_Menu to nechává na nás.
Návratová hodnota create_item() je typu CL_MenuNode* a tentokrát ji ještě nebudeme potřebovat. CL_MenuNode je třída, která reprezentuje jeden konkrétní uzel menu (k jejímu podrobnému popisu se dostaneme někdy příště).
Metoda create_item() má sestřičku:
CL_MenuNode *create_toggle_item( const std::string &path, const std::string &labels=std::string());
Její funkčnost je obdobná až na to, že vytvořený list se bude chovat jako přepínač.
Podívejme se konečně na to, jaké má CL_Menu konstruktory:
CL_Menu( const CL_Point &pos, CL_Component *parent, CL_StyleManager *style = NULL, bool vertical=false); CL_Menu( CL_Component *parent, CL_StyleManager *style = NULL, bool vertical=false); CL_Menu( const CL_Rect &rect, CL_Component *parent, CL_StyleManager *style = NULL, bool vertical=false); CL_Menu( CL_MenuNode *parent_node, CL_Component *parent, CL_StyleManager *style = NULL );
Parametr pos slouží k udání pozice levého horního rohu vytvářeného menu vzhledem ke komponentě (CL_Component), na niž odkazuje parametr parent (obvykle je rodičovskou komponentou menu okno, tj. CL_Window). Obdobný význam jako pos má i parametr rect, který však bere v úvahu nejen polohu, ale i rozměry. Pokud polohu nezadáme, bude to, jako bychom zadali CL_Point(0, 0). Nové menu můžeme dokonce přichytit k již existujícímu uzlu danému parametrem parent_node.
Na závěr se můžete podívat na kód, pomocí něhož jsme schopni vytvořit menu odpovídající výše uvedenému obrázku:
// vytvorime okno, do ktereho posleze umistime menu
OknoSMenu = new CL_Window(CL_Rect(0, 0, SirkaHlOkna, VyskaHlOkna), "Okno s menu", SpravceGUI);
// vytvorime menu
Menu = new CL_Menu(OknoSMenu->get_client_area());
// odpovida leve casti obrazku
Menu->create_toggle_item("0,1/0,2/0,4/0,7", "K/B/C/D");
Menu->create_item("0,1/0,2/0,5", "K/B/E");
Menu->create_item("0,1/0,2/0,6", "K/B/F");
Menu->create_item("0,1/0,3", "K/G");
// odpovida prave casti obrazku
Menu->create_item("1,1/1,2/1,8/1,11", "R/B/H/E");
Menu->create_toggle_item("1,1/1,2/1,8/1,12", "R/B/H/F");
Menu->create_item("1,1/1,3", "R/E");
Menu->create_item("1,1/1,2/1,9", "R/B/C");
Menu->create_item("1,1/1,4", "R/G");
Menu->create_item("1,1/1,5", "R/D");
Menu->create_item("1,1/1,6/1,10", "R/B/C");
Menu->create_item("1,1/1,7", "R/D");
Závěr
K tvorbě menu je toho ještě mnoho co povědět, a proto budeme v tomto tématu pokračovat i příště (a nejspíš nejen příště).
Zde si můžete stáhnout ukázkový příklad obsahující výše uvedený kód.