################################################################################
    GUI
################################################################################
Autor  : Adam Sawicki
Wersja : 2.0
Data   : 10 listopada 2006


Szkielet graficznego interfejsu uytkownika prezentujcego rysowane w postaci
grafiki 2D kontrolki, ktre mog reagowa na wejcie z klawiatury i z myszy,
a take przechowywa swj stan, pozostawa we wzajemnych relacjach i reagowa
na zdarzenia.

Cechy:

- Wypasiony
  Posiada wsparcie dla hintw, kursorw, przecigania, wysuwanych menu,
  dziedziczon aktywno i widzialno, ognisko z klawiatury i wiele innych
  cech - wszystko co zdoaem wymyli i napisa.
- Uoglniony
  Jest wprawdzie zaleny od PCH (a wic gwnie Common Lib) oraz Framework,
  ale mona go atwo dostosowa do wsppracy z praktycznie kadym silnikiem
  gry.


Podstawy
================================================================================

Wszystko zgromadzone jest w przestrzeni nazw gui. Podstaw jest singleton klasy
Manager. Naley mu w kadej klatce wywoywa OnFrame w celu przeliczenia
(wtedy te powstaje wikszo zdarze dla kontrolek) oraz OnDraw w celu
odrysowania (wtedy powstaj zdarzenia OnDraw kontrolek).

Manager implementuje interfejs frame::IInputObject. W ten sposb reaguje
na zdarzenia z wejcia od klawiatury i myszy. Niestety w zdarzeniach
od klawiatury zawsze zwraca true, bo nie potrafi stwierdzi natychchmiast czy
dane zdarzenie zostaje obsuone. Dlatego w razie potrzeby obsugiwania tych
zdarze w razie nieobsuenia przez modu GUI, naley wykorzysta delegaty
OnUnhandled* z obiektu klasy Manager.

Modu GUI. nie uywa moduu Err. Nie rzuca wyjktw. Jeli prbuje si zrobi
co niedozwolonego, po prostu jest to ignorowane. Bdy krytyczne s sprawdzane
za pomoc asercji, a wszelkie absolutnie karygodne niedopatrzenia zakocz si -
wiadomo - bdem ochrony.


Kontrolki
================================================================================

Gwn struktur GUI jest las kontrolek. S to obiekty klas dziedziczcych po
klasie Control lub CompositeControl. CompositeControl, sama bdc odziedziczona
z Control, jest zdolna do przechowywania w sobie podkontrolek i z niej naley
wyprowadza kontrolki konkretne, ktre te maj mie tak zdolno.

Manager przechowuje na trzech warstwach listy kontrolek gwnego poziomu.
Te kontrolki mog mie swoje podkontrolki itd.

Kontrolki definiuje si dziedziczc z klasy Control lub CompositeControl i
implementujc wybrane metody wirtualne. Wikszo z nich to zdarzenia wywoywane
w ramach Manager::OnFrame, ktre podlegaj kolejkowaniu. Dziki temu mona
w nich (mam nadziej) bezpiecznie wywoywa rne operacje na strukturze
kontrolek i ich stanie (cznie z usuwaniem), ale za to zachodz z opnieniem,
niekoniecznie od razu w chwili zajcia zdarzenia i z takim stanem jaki maj
natychmiast po tym zajciu.

Kontrolki tworzy si wywoujc konstruktor klasy. Naley je tworzy na stercie
operatorem new. Do konstruktora podaje si kontrolk rodzica /parent/ lub 0
jeli to kontrolka najwyszego poziomu. W konstruktorze klasy potomnej mona
sobie poustawia rne rzeczy, jak SetHint, SetRect, SetLayer itd.

Kontrolki usuwa naley metod Release, nie bezporednio delete. Obecnie to nie
robi rnicy, ale w przyszoci moliwe jest odraczanie usuwania w celu
rozwizania pewnych komplikacji z sekwencj wywoa metod. Kontrolka
CompositeControl sama usuwa swoje podkontrolki podczas zniszczenia. Kontrolki
najwyszego poziomu zobowizany jest usuwa uytkownik - nie robi tego Manager.

ClientRect to prostokt wyznaczajcy w CompositeControl obszar, w ktrym
mieszcz si podkontrolki. Jest wyraony we wsprzdnych lokalnych danej
kontrolki (wzgldem jej normalnego Rect), a wsprzdne Rect podkontrolek s
z kolei wzgldem miejsca pocztku tego prostokta ClientRect. Podkontrolka,
ktra nie mieci si w zakresie ClientRect w caoci / w ogle (zalenie
od staej NO_CLIP) nie jest rysowana ani nie ma szansy na hit-test, ale nie jest
formalnie uznana za niewidzialn ani nieaktywn, moe wic mie ognisko itd.

Warstwy
-------

Manager posiada trzy warstwy, na ktrych zgromadzone mog by kontrolki gwnego
poziomu - NORMAL, STAYONTOP i VOLATILE. Zwyke okna i kontrolki naley tworzy
na warstwie NORMAL. Do niej domylnie trafiaj kontrolki. STAYONTOP to warstwa
dla okien zawsze-na-wierzchu, jak powiadomienia /MessageBox/. VOLATILE to
miejsce dla "wyskakujcych" kontrolek, jak menu, rozwijane listy z ComboBoksa
itp. Wszystko okna na tej warstwie s automatycznie chowane (Visible=false)
kiedy ognisko otrzumuje jaka kontrolka nie naleca choby porednio do okna
z tej warstwy.

Kontrolk mona przenie do wybranej warstwy metod SetLayer.


Aktywno
================================================================================

Kontrolka jest aktywna /Enabled/ lub nieaktywna. Kontrolka jest prawdziwie
aktywna /RealEnabled/ kiedy aktywna jest ona i wszystkie jej nadrzdne a do
gwnego poziomu. Aktywno mona zmienia metod SetEnabled. O zmianie
powiadamiana jest zdarzeniami OnEnable i OnDisable, jednak nie dotycz one
zmiany aktywnoci kontrolek nadrzdnych (TODO: Czy na pewno??? Kod mwi
inaczej!).

Kontrolka nieaktywna traci ognisko i nie moe otrzymywa zdarze od klawiatury
ani od myszy, nie moe mie ogniska. Moe jednak pokazywa hint i mie kursor.
Powinna inaczej wyglda. Nie jest gwarantowane, e adne zdarzenie do kontrolki
ktra staa si nieaktywna nie dotrze - jakie mogo si gdzie zapodzia :)


Widzialno
================================================================================

Widzialno /Visible/ dziaa podobnie do aktywnoci. Te jest dziedziczona. Te
istnieje prawdziwa widzialno /RealVisible/. Kontrolki niewidzialne nie s
rysowane, nie mog mie ogniska, nie mog odbiera wejcia od klawiatury ani od
myszy. Do zmiany widzialnoci suy metoda SetVisible. Do powiadamiania -
zdarzenia OnShow i OnHide.


Wejcie od myszy
================================================================================

OnMouseButton wywouje si dla kontrolki, nad ktr jest kursor. Wyjtkiem jest
sytuacja kiedy trwa przeciganie - wwczas wywouje si dla kontrolki, ktra
jest przecigana i powoduje zawsze zakoczenie przecigania - bd to pozytywne
bd to jego anulowanie.

OnMouseMove wywouje si podobnie - dla kontrolki, nad ktr jest kursor,
z wyjtkiem sytuacji, kiedy trwa przeciganie - wwczas dla kontrolki
przeciganej.

OnMouseWheel wywouje si zawsze dla kontrolki, nad ktr jest kursor
niezalenie od tego czy trwa przeciganie.

OnMouseEnter dostaje kontrolka, nad ktr wchodzi kursor myszy, o ile nie trwa
przeciganie - wwczas uznaje si, e kursor jest tak jakby cay czas nad
kontrolk przecigan. OnMouseLeave analogicznie.

Hit-Testing
-----------

Poprzez wasn implementacj metody OnHitTest kontrolka moe decydowa, czy dany
punkt lecy na obszarze jej prostokta faktycznie do niej naley. Jeli nie,
to miejsce uznane zostanie za przezroczyste i kliknicie czy najechanie mysz
"przejdzie" do kontrolki nadrzdnej, lecej pod spodem, co spowoduje waciwe
zachowanie w kadej sytuacji. Dziki temu kontrolka moe mie nieregularne
ksztaty, o ile wyznaczajcy j prostokt jest na tym ksztacie w caoci
opisany.

Drag&drop
---------

Drag&drop albo przeciganie to najbardziej zaawansowany mechanizm caego moduu
GUI. Polega na moliwoci przecigania obiektw kursorem myszy z jednej
kontrolki do innej (albo tej samej). Procedura jest nastpujca:

1. Rozpoczcie przecigania

Kontrolka wewntrz zdarzenia OnMouseButton w reakcji na akcj MOUSE_DOWN
odpowiedniego klawisza wywouje metod StartDrag, podajc stworzony specjalnie
obiekt klasy dziedziczcej z DragData.

2. Przeciganie trwa

Zamiast kontrolki, nad ktr faktycznie jest mysz, zdarzenia od myszy -
OnMouseButton i OnMouseMove, dostaje kontrolka przecigana. Kontrolka docelowa
przecigania - ta nad ktr jest mysz - dostaje w zamian zdarzenia OnDragEnter,
OnDragLeave, OnDragOver. Jako kursor wykorzystywany jest obiekt klasy DragData.

3. Przeciganie koczy si sukcesem

Jeli uytkownik zwolni klawisz myszy, ktrym rozpocz przeciganie, kontrolka
docelowa (to moe by ta sama co rdowa albo inna) dostaje zdarzenie
OnDragDrop, a rdowa OnDragFinished.

4. Przeciganie koczy si porak

Jeli kliknito lub zwolniono inny klawisz myszy, kontrolka docelowa dostaje
zdarzenie OnDragCancel, a rdowa OnDragFinished.

Jeliby kontrolka rdowa chciaa przerwa przeciganie - na przykad w wyniku
otrzymania zdarzenia nacinicia klawisza ESC (o ile ma ognisko), moe to zrobi
wywoujc metod CancelDrag.


Wejcie od klawiatury
================================================================================

Kontrolka, ktra jest prawdziwie aktywna, prawdziwie widzialna i moe mie
ognisko /Focusable/, moe otrzyma ognisko /Focus/. Wwczas wanie ona dostaje
zdarzenia z klawiatury - OnKeyDown, OnKeyUp, OnChar. Jeli w danej metodzie
zwrci false, zdarzenie przechodzi do jej kontrolki nadrzdnej itd. a
do gwnego poziomu. Dzieje si tak nawet jeli dana kontrolka nadrzdna sama
nie moe mie ogniska (/Focusable/ nie jest dziedziczone). To okazja
do obsuenia zdarze wyszego poziomu takich jak przeniesienie ogniska
na nastpn/poprzedni kontrolk pod wpywem klawiszy takich jak Tab/Shift+Tab
czy Gra/D.

Nada danej kontrolce ognisko mona metod Focus. O uzyskaniu i utracie ogniska
przez kontrolk informuj zdarzenia OnFocusEnter i OnFocusLeave.

Metoda FocusNext i FocusPrev daje ognisko odpowiednio nastpnej i poprzedniej
kontrolce wzgldem danej, przy czym w poszukiwaniu takiej kontrolki
przeszukiwane jest cae drzewo danej kontrolki gwnej, take na niszych i
wyszych poziomach, a po zakoczeniu od pocztku.


Kursor
================================================================================

Kursor to obiekt dziedziczcy z klasy Cursor. Kada kontrolka ma swj kursor -
domylnie jest to wskanik pusty. Kontrolka moe ustawi swj kursor na wybrany
obiekt metod SetCursor. Sama jest odpowiedzialna za jego tworzenie, niszczenie
oraz za to, eby modu GUI nie mia go ustawionego w chwili kiedy ju nie
istnieje. Jeli co zmieni si wewntrz przypisanego cay czas do kontrolki
obiektu kursora, naley wywoa metod CursorChanged.

To, jakie informacje bdzie przechowywaa konkretna klasa kursora i jak one
zostan wykorzystane do pokazywania kursora w programie, to jest zupenie
dowolne - elastyczno jest jak wida ogromna. Uytkownik moe w kadej chwili
otrzyma od managera pozycj kursora metod GetMousePos i biecy kursor metod
GetCurrentCursor. Moe te zmienia pokazywany kursor tylko kiedy to naprawd
konieczne - w reakcji zdarzenie OnCursorChange managera.

Jako biecy kursor manager uznaje kursor tej kontrolki, nad ktr jest
aktualnie kursor myszy. Wyjtkiem jest sytuacja, kiedy co jest przecigane -
wwczas kursorem jest obiekt klasy DragData dotyczcy biecego przecigania.


Hint
================================================================================

Dziaa podobnie jak kursor, ale tutaj nie liczy si przeciganie - biecy
hint dotyczy zawsze kontrolki, nad ktr jest kursor myszy. Hint to mechanizm
do pokazywania wasnych dymkw czy innych informacji z podpowiedzi na temat
danej kontrolki - czy to w dymku przy tej kontrolce, czy to gdzie w wybranym
przez uytkownika miejscu.

Hint dziedziczy z klasy Hint. Kontrolka ustawia swj hint metod SetHint.
Pocztkowo jest to wskanik pusty. O zmianie w obiekcie swojego hinta powiadamia
wywoaniem HintChanged.

Uytkownik moe w kadej chwili otrzyma od managera obiekt biecego hinta
metod GetCurrentHint lub reagowa tylko na zmiany hinta obsugujc zdarzenie
OnHintChanged.

Hint, podobnie jak kursor, w chwili kiedy pozycja myszy nie jest nad adn
kontrolk ma warto 0.

Hint wyprowadzony z klasy PopupHint ma specjaln wasno. Po odczekaniu
odpowiedniego czasu (stae HINT_DELAY_*) wedug specjalnego algorytmu wywoywana
jest takiemu obiektowi metoda OnDraw - ju po narysowaniu wszystkich kontrolek,
ale jeszcze w ramach OnDraw managera. Metoda ta otrzymuje pozycj, w ktrej by
kursor kiedy dany dymek po raz pierwszy si pokaza. Mona sobie do niej doda
co celem pokazania go gdzie obok. Dziki PopupHint mona atwo robi
wyskakujce po jakim czasie w miejscu kursora dymki z podpowiedziami.

Jak zrobi eby nie tworzy masy obiekcikw klasy Hint czy Cursor tylko uywa
wszdzie kilku alternatywnych wartoci? Utworzy kilka globalnie dostpnych
obiektw klasy byle jakiej pochodnej od Hint/Cursor albo klasy Hint/Cursor,
je przypisywa i z nimi porwnywa.

