Ukázky stříbrného stylu GUI
Zde jsou slíbené obrázky toho, jak může vypadat aplikace vytvořená v ClanLibu s využitím stříbrného stylu GUI. Jedná se o screenshoty z ukázkového programu, který si můžete stáhnout na konci článku.
Několik poznámek k příkladu
Přiložený program demonstruje funkčnost a vzhled několika komponent GUI. Jedná se o CL_DisplayWindow (hlavní okno celé aplikace), CL_Window (základní okno vytvořeného GUI), CL_Label, CL_ScrollBar, CL_ListBox, CL_Frame, CL_InputBox, CL_Image, CL_Button (ať již klasického, či vlastního vzhledu), CL_ProgressBar a CL_MessageBox. Tento výčet samozřejmě neobsahuje vše, co ClanLib nabízí, avšak obsahuje myslím to, co nejčastěji potřebujeme, kromě tvorby menu, které se, jak doufám, budeme věnovat v souvislosti s nějakým dalším příkladem.
Součástí příkladu je poměrně rozsáhlý GDF soubor, který by se vám mohl hodit při vlastní tvorbě. Když si nebudete jisti, jak se něco zapíše, možná to tam objevíte. Například zápis obrázeku (image) zatím tvůrci ClanLibu nezdokumentovali, takže pokud nechcete procházet zdrojáky, jako jsem to dělal já, podívejte se do zmíněného GDF souboru (samozřejmě si tento zápis popíšeme i v tomto seriálu).
Program sám o sobě nedělá nic moc duchaplného, spíše samé pouťové efekty. Zkuste tedy klikat na všechno možné, vypozorovat, co se kdy stane, a pak se podívejte do zdrojáků, jak je to naprogramované. Použité obrázky jsou z mé hry, za jejich nakreslení vděčím své přítelkyni.
Při psaní ukázkového programu jsem narazil na několik věcí, o kterých bych se rád zmínil.
Uzavření hlavního okna programu
Možná jste si všimli u některého z předchozích příkladů, že pokud jste klikli na křížek v pravém horním rohu hlavního okna, který obvykle slouží k jeho zavření, nic se nestalo. Otázka zní, jak zařídit, aby se něco stalo, přesněji řečeno, aby se stalo to, co my chceme. Řešení je poměrně jednoduché, pokud víme, že při kliknutí na zmíněné tlačítko vyšle CL_DisplayWindou signál sig_window_close. Stačí pak už jen tento signál připojit k nějaké naší metodě a provést to, co chceme:
// zde Sloty je CL_SlotContainer
// signál připojujeme k metodě on_HlavniOkno_close(),
// jejiz telo nasleduje ...
Sloty.connect(HlavniOkno.sig_window_close(), this, &T_Aplikace::on_HlavniOkno_close);
void T_Aplikace::on_HlavniOkno_close() {
// Zjistime, zda chce uzivatel opravdu ukoncit program
if (CL_MessageBox::info("Ukonceni...", "Chcete opravdu ukoncit program?"
"Ano", "Ne", "Storno", SpravceGUI) == 0) {
// prerusime hlavni smycku, cimz ukoncime program
Konec = true;
}
} // on_HlavniOkno_close() ------------------------
V tomto případě je situace taková, že je vyslán určitý signál, který nás informuje o tom, že nastala nějaká událost, a jeho vyslání nemá automaticky žádný další efekt.
Občas se však může stát i to, že je signál nejen vysílán, ale také automaticky odchytáván a po jeho vyslání je automaticky provedena nějaká akce.
Zavírání CL_Window a virtuální sloty
Příkladem výše popsaného chování je klasické okno v našem GUI a kliknutí na jeho uzavírací tlačítko. Zde je chování jiné než u CL_DisplayWindow, totiž CL_Window se automaticky zavře. Může samozřejmě nastat situace, kdy nám takovéto chování nebude vyhovovat. Potřebovali bychom nějakým způsobem signál odchytit předtím, než se dostane ke slotům, ke kterým již byl připojen.
K tomuto účelu slouží tzv. virtuální sloty. Princip je podobný jako u klasického připojení signálu do slotu, avšak trochu se liší syntaxe:
// signal uzavreni zakladniho okna pretizime, aby nedochazelo
// k nenavratnemu zruseni okna kliknutim na prislusne tlacitko
slot_ZakladniOkno_close = ZakladniOkno->sig_close().connect_virtual(this, &T_Aplikace::on_ZakladniOkno_close);
Zde je slot_ZakladniOkno klasický slot, tj. typ CL_Slot. Změna je v tom, že místo metody connect() signálu použijeme jeho metodu connect_virtual(), která zařídí přesně to, co potřebujeme. Nejen že přetíží původní připojení tohoto signálu do slotů, navíc předá metodě, která bude volána (zde je to metoda on_ZakladniOkno_close() třídy T_Aplikace) jako parametr referenci na CL_SlotParent_v0 (může být i v1 apod. dle typu signálu). Jedná se o jakéhosi předka vzniklého virtuálního slotu, kterého můžeme v těle metody zavolat jako funkci, čímž dosáhneme následného vykonání akcí, které by proběhly, kdybychom slot „nepřetížili“. Následující příklad kódu snad vše ozřejmí:
void T_Aplikace::on_ZakladniOkno_close(CL_SlotParent_v0& Ukonceni) {
using namespace std;
// Zjistime, zda chce uzivatel opravdu ukoncit program zavrenim okna
if (CL_MessageBox::info("Zavreni okna...", "Zavreni tohoto okna je nevratne.\n"
"Chcete ho opravdu zavrit?",
"Ano", "Ne", "Storno", SpravceGUI) == 0) {
// !!! zavreme zakladni okno zavolanim predka slotu
Ukonceni();
OknoZavreno = true;
}
}
Tento kód způsobí, že po kliknutí na uzavírací tlačítko okna se objeví message box s dotazem, zda si opravdu přejeme okno zavřít. Pokud odpovíme kladně, pošleme predka, tj. vyšleme signál, který jsme odchytili dřív, než se mohl někam dostat. V opačném případě se chováme, jako by nikdo na uzavírací tlačítko nekliknul, a tedy okno neuzavřeme.
CL_MessageBox
V ukázkách jste si zajisté všimli použití komponenty, kterou jsme si dosud podrobněji nepopsali, a to CL_MessageBoxu. Ten slouží k zobrazování krátkých zpráv uživateli. Může mít podobu s jedním až se třemi tlačítky. Podobu s jedním tlačítkem používáme, pokud chceme pouze zobrazit zprávu. Podobu se třemi tlačítky používame, chceme-li od uživatele odpověď typu ano/ne/storno.
Pokud je mi známo, není zatím možné message box vytvářet z GDF, což by také v jeho případě nemělo příliš velký smysl. Nejčastěji vytvoříme message box způsobem jako ve výše uvedených ukázkách kódu, tj. voláním CL_MessageBox::info(). Tato metoda se vyskytuje v následujících přetížených verzích:
static int info( const std::string &text, CL_Component *parent, CL_StyleManager *style = NULL); static int info( const std::string &title, const std::string &text, CL_Component *parent, CL_StyleManager *style = NULL); static int info( const std::string &title, const std::string &text, const std::string &button1, const std::string &button2, const std::string &button3, CL_Component *parent, CL_StyleManager *style = NULL);
Poslední dva parametry jsou vždy ukazatel na rodičovskou komponentu a na správce stylu (nepoviný). Rodičovskou komponentou může být v podstatě cokoliv, nejčastěji to asi bude odkaz přímo na správce GUI, tj. CL_GUIManager, což je také potomek CL_Component (GUIManager je kořenovou komponentou každého GUI v ClanLibu).
Dále můžeme zadat buď jenom text zprávy, nebo text s titulkem message boxu, případně text až tří tlačítek (prázdný řetězec by měl způsobit vynechání posledního či posledních dvou tlačítek).
Metoda info vždy vrací index tlačítka, na které jsme v message boxu klikli (číslováno od nuly). Na základě této vrácené hodnoty se pak můžeme rozhodovat.
Message box můžeme vytvořit také přímo pomocí konstruktoru, který má stejné parametry jako nejširší verze metody info. Na jaké tlačítko jsme klikli, můžeme zjistit pomocí metody get_result_button(), která opět vrací index tlačítka stejně jako info().
CL_RadioButton
Přepínač je poslední komponentou, jejíž popis se ještě do dnešního dílu vejde. V GDF ho můžeme popsat například takto:
<radiobutton name="RadioButton" x="315" y="360" />
Prostě udáme jeho souřadnice a jméno. Třída CL_RadioButton pak poskytuje metody:
bool is_checked() const; void set_checked(bool check);
Pomocí nich jsme schopni zjistit, nebo nastavit stav tohoto přepínače.
Závěr
Příště budeme pokračovat v podobném duchu.
Slíbený KDevelopí projekt příkladu můžete stáhnout zde. Verze s jednoduchým makefilem je zde.
Možná jste si všimli, že tvůrci knihovny přecházejí na nový server, tak doufejme, že se jim vše podaří co nejdříve zprovoznit.