Hlavní navigace

Knihovna ClanLib (10)

19. 7. 2004
Doba čtení: 7 minut

Sdílet

Dnes se začneme věnovat tvorbě uživatelského rozhraní (GUI) v ClanLibu.

Co je to GUI?

V dnešní době se už myslím nenajde nikdo, kdo by se dosud nesetkal s nějakým grafickým uživatelským rozhraním (dále jen GUI). Vlastně si dokonce myslím, že těch, co si někdy vyzkoušeli ovládat počítač jinak než s pomocí nějakého GUI, bude menšina (uživatelé Windows obvykle nemají moc na výběr :)).

Obvykle je GUI realizováno nějakým okenním systémem. Každý program pak má nějaké základní okno, které může mít buď klasický vzhled jakéhosi rámu s tlačítky pro minimalizaci/ma­ximalizaci, případně i dalšími, nebo je ve formě fullscreen, kdy je obvykle přes celou obrazovku zobrazen pouze vnitřek rámu okna, tj. bez ovládacích tlačítek.

Okna bývají obvykle nějakým způsobem podporována přímo operačním systémem. Program pak obvykle systému sděluje pouze požadavky na vytvoření okna a jeho parametry. Přesvědčit se o tom můžete třeba tak, že ve vašem okenním prostředí, ať už to je KDE nebo Windows nebo něco jiného, změníte styl vykreslování oken – změny se promítnou na všechny spouštěné programy.

Trochu složitější to už však je s ostatními prvky GUI, kterými jsou například tlačítka nebo nějaká dceřinná okna. Většina z nás se totiž spokojí s jednotným orámováním našeho programu, nikoliv však s jednotným vzhledem jeho součástí, protože obvykle právě vzhled je tím, co programy nejvíce prodává.

GUI v ClanLibu

Ukazuje se, že je u prvků (komponenet) GUI velice vhodné nějakým způsobem oddělit jejich vzhled od funkčnosti. Přesně tak to udělali i tvůrci ClanLibu. Celé GUI je totiž navrženo ve dvou oddělených vrstvách. První vrstva realizuje funkčnost, tj. řeší otázky typu „Co se má provést, když uživatel myší klikne na tlačítko?“. Teprve nad touto vrstvou se buduje vzhled jednotlivých komponent.

ClanLib umožňuje programátorům vytvářet celé vlastní styly GUI, což je řekněme jakýsi soubor vzhledů všech komponenet, a pak je pohodlně používat. Vzhledem k tomu, že je podporované i využívání průhlednosti, dájí se v ClanLibu vytvářet opravdu velmi pěkné stly.

Osobně vytvoření vlastního stylu GUI považuji za poněkud pokročilejší techniku, kterou bych rozhodně nechtěl naše povídání o GUI začít (zmiňuji se o něm už nyní, aby ti, kteří touží po velké flexibilitě při tvorbě GUI, věděli, že je na co se těšit :-)). Naštěstí máme na výběr.

Stříbrný styl

Abychom nemuseli začít popisem tvorby vlastního stylu GUI, potřebovali bychom už nějaký hotový. ClanLib jeden opravdu velmi povedený styl obsahuje přímo v sobě (další už je pravděpodobně možné sehnat někde na Internetu). Tvůrci ho pojmenovali Silver style (stříbrný styl) podle převládající barvy jeho komponent.

Podle mých zkušeností je stříbrný styl pro běžné použití plně dostačující, a to hlavně díky tomu, že i v rámci jeho samotného může programátor velice pohodlně měnit některé části vzhledu. Příkladem může být třeba to, že pokud nechceme klasické vzezření tlačítka, můžeme nastavit pro jeho typické polohy (stisknuto, nestisknuto atd.) libovolný obrázek, u nějž se navíc automaticky použije i jeho maska průhlednosti, jak je to třeba i u spritů.

GUI Resources

Abychom nemuseli psát spoustu nepřehledného kódu, uplatňuje se v ClanLibu při tvorbě GUI princip velmi podobný RDF. Opět se jedná o XML soubory, ve kterých je možné mnohem přehledněji popsat vytvářené GUI, nehledě na obroské výhody toho, že pro změnu polohy tlačítka nebo titulku nějakého okna stačí změnit atribut v těchto tzv. GUI definition files (dále jen GDF) a není nutné znovu překládat program.

Z mého pohledu jsou GDF poměrně pěkným kompromisem mezi tvorbou GUI stylem C++ Builderů, Delph a jim podobných a zapisováním všeho přímo do kódu. Přístup Delph je hezký v tom, že člověk GUI takříkajíc maluje myší a občas někde něco napíše a výsledek je hotov opravdu velmi rychle. Nevýhoda bývá v tom, že pokud požadujete nějakou vlastnost či vzhled, se kterým nebylo dopředu počítáno, docela se zapotíte a možná vůbec nebudete mít šanci uspět.

Nevýhoda přístupu, kdy vše píšeme do kódu, je v časové náročnosti a ve sklonu kódu stávat se nepřehledným. Umožňuje nám však naprogramovat lecjakou vychytávku, a pokud se snažíme, můžeme docílit i výkonnějšího řešení šitého na míru.

GDF nám sice nenabízí okamžitou vizuální odezvou, avšak myslím, že člověk s určitou schopností abstrakce se může na GDF podívat a představit si GUI docela dobře. Můžete si to zkusit třeba na tomto příkladu:

<components>
  <window name="my_window" x="10" y="10" width="600" height="400" title="My Window" >
    <components>
      <button name="my_button" x="25" y="25" width="100" height="20" text="My Button" />
      <label name="my_label" x="25" y="50" text="My label" />
    </components>
  </window>
</components> 

Okno pojmenované my_window je zde komponentou nejvyšší úrovně a jeho souřadnice platí vůči hlavnímu oknu programu, tj. oknu, které vytváříme úplně na začátku jako CL_DisplayWindow. Součástí my_window pak jsou tlačítko (button) pojmenované my_button a popisek (label) pojmenovaný my_label. Jejich souřadnice již nejsou brány vůči CL_DisplayWindow, ale vůči jejich mateřskému oknu, kterým je my_window.

Na tlačítku se objeví text „My Button“ a popisek bude představovat nápis „My label“. Výška a šířka jsou samozřejmě udány v pixelech.

Různé komponenty rozeznávají různé atributy v GDF, jak se o tom ještě podrobně zmíníme. Některé z nich jsou volitelné, a pokud je nezadáme, mají nějakou defaultní hodnotu. Každá komponenta však vyžaduje zadání atributů x, y popisujících její pozici.

Použití GDF v programu

Podívejme se nyní na to, jak GUI popsané v GDF výše uvedeným způsobem můžeme zprovoznit v programu:

CL_ResourceManager SpravceZdroju("GUIStyleSilver/gui.xml");
CL_StyleManager_Silver SpravceStStylu(&SpravceZdroju);
CL_GUIManager SpravceGUI(&SpravceStStylu);

CL_ComponentManager SpravceKomponent("mygui.xml", &SpravceGUI);

SpravceGUI.run(); 

Jelikož stříbrný styl, který chceme použít, má také nějaké zdroje, musíme je nějakým způsobem zpřístupnit. Pokud si rozbalíte stažený archiv s vaším ClanLibem, najdete tyto zdroje v Resources/GU­IStyleSilver/. Měl by zde být jednak soubor gui.xml a jednak podadresáře jako fonts, menu a další. Asi nejlepší způsob je překopírovat si celý adresář GUIStyleSilver někam k vašemu programu a k ostatním vašim zdrojům.

V našem příkladu je GUIStyleSilver/gu­i.xml cesta k tomuto souboru a zmíněné adresáře fonts, menu a další jsou v adresáři jako gui.xml, tedy v adresáři GUIStyleSilver. Tuto cestu předáváme jako parametr konstruktoru nám již známého CL_ResourceMa­nageru.

Odkaz na něj předáme jako parametr konstruktoru třídy CL_StyleManager_Sil­ver, která se stará o fungování stříbrného stylu.

Máme-li SpravceStStylu, předáme odkaz na něj konstruktoru CL_GUIManager, tj. třídě zajišťující existenci GUI jako celku. Nakonec ještě vytvoříme SpravceKomponent jako instanci třídy CL_Componenet­Manager, která nám vytváří jakési rozhraní pro přístup k jednotlivým komponentám našeho GUI. Jeho konstruktoru předávme jako parametr cestu k našemu GDF a odkaz na patřičný CL_GUIManager, což je v našem příkladuSpravceGUI.

Příkazem

SpravceGUI.run();

spustíme naše GUI. Tento příkaz vytváří vlastní nekonečnou smyčku, kerá je užitečná pouze v případě, že si od chvíle jejího spuštění vystačíme se zasíláním zpráv pomocí signálů, jak jsme si to popsali minule. V případě, že máme jinou smyčku, kterou si chceme sami řídit, využijeme spíše příkaz:

SpravceGui.show();

Tak vykreslíme GUI pouze jednou.

Pokud jsme uvnitř smyčky vyvolané metodou run(), můžeme ji přerušit metodou quit(). Tedy příkazem:

SpravceGUI.quit();

K dalším užitečným metodám třídy CL_GUIManager se ještě vrátíme někdy příště.

Přístup k jednotlivým komponentám

Poslední téma, o němž se chci ještě v tomto dílu zmínit, je přístup k jednotlivým komponentám GUI pomocí CL_ComponentMa­nager.

Ke každé komponentě existuje příslušná třída, která ji reprezentuje. Tak například tlačítko je reprezentováno třídou CL_Button. Instanci tlačítka popsaného v GDF spravovaného naším CL_ComponentMa­nagerem vytvoříme takto:

CL_Button* My_button = (CL_Button *) SpravceKomponenet.get_component("my_button"); 

Metoda get_component() vrací ukazatel na třídu CL_Componenet, od níž jsou všechny komponenty jako CL_Button odvozeny. Jelikož víme, že se jedná o tlačítko, můžeme si dovolit výše uvedené přetypování.

Pokud máte povoleno RTTI, můžete používat i poněkud čistší syntax:

CL_Button* My_button;
SpravceKomponent.get_component("my_button", &My_button); 

Závěrem

Příště bude ve výkladu o tvorbě GUI pokračovat. Postupně se budeme snažit probrat se podrobněji jednotlivými komponentami, řekneme si něco víc o CL_Componenet­Manageru a ostatních správcích, časem se snad dostaneme i k tvorbě vlastního stylu.

Pokud se budete snažit experimentovat s GUI již po tomto úvodním článku, nezapomeňte na includování příslušných hlaviček:

#include <ClanLib/core.h>
#include <ClanLib/gui.h>
#include <ClanLib/guistylesilver.h>

#include <ClanLib/application.h>
#include <ClanLib/display.h>
#include <ClanLib/gl.h>

Nové jsou hlavičky gui.h a guistylesilver.h. Nezapomeňte ani na inicializaci a deinicializaci příslušného subsystému:

CL_SetupGUI::init();
CL_SetupGUI::deinit();

Toť pro dnešek opravdu vše.