C dla każdego (cz. 1.)

Gadżety (2)

Dziś dokończymy rozpoczętą w poprzedniej części listę stałych i przedstawimy pierwszą część listingu. A więc do roboty.

Zostały nam do omówienia jeszcze trzy rodzaje gadżetów oferowanych przez "gadtools.library:

- PALETTE_KIND - gadżet do wyboru jednego koloru z palety. Podobnie jak w przypadku gadżetów-list, rozmiar utworzonego gadżetu może być mniejszy od żądanego, aby wszystkie prostokąty-kolory w palecie miały ten sam rozmiar. Tagi:
GTPA_Depth - liczba bitplanów w palecie (a więc pośrednio liczba kolorów). Standardowo 1, zwykle podaje się tu "głębokość" ekranu, na którym gadżet ma być używany.
GTPA_Color - początkowo zaznaczony kolor. Standardowo 1.
GTPA_IndicatorWidth, GTPA_IndicatorHeight - tu panuje drobne zamieszanie pomiędzy różnymi wersjami systemu. Oryginalnie (OS 2.0) użycie jednego z tych tagów powoduje, że odpowiednio: po lewej bądź na górze gadżetu ukazuje się prostokąt o podanej w tagu odpowiednio: szerokości lub wysokości. W prostokącie tym wyświetlany jest zaznaczony w danym momencie kolor. W OS 3.0 wprowadzono inny sposób oznaczania zaznaczonego w danym momencie koloru - po prostu kolor ten jest obrysowywany w palecie - nie ma żadnego osobnego prostokąta, dane tych tagów są więc ignorowane, a podanie któregoś z tagów powoduje tyle, że zaznaczony w danym momencie kolor jest obrysowywany. Patrz obrazek w poprzedniej części.

- SCROLLER_KIND - gadżet-suwak, używany zwykle do skrolowania obiektów w liście, będący np. częścią gadżetu-listy. Sam gadżet-suwak niczego nie skroluje - służy on do wizualnej reprezentacji tego, ile elementów lista zawiera (na podstawie wielkości suwaka) i która część listy jest w danym momencie wyświetlana (na podstawie położenia suwaka). Tagi:
GTSC_Total - liczba obiektów zawartych w liście.
GTSC_Visible - liczba obiektów widoczna naraz na ekranie - ten tag w połączeniu z powyższym służy do wyliczenia rozmiaru suwaka.
GTSC_Top - element widoczny początkowo na szczycie listy - ten tag służy do wyznaczenia początkowego położenia suwaka.
PGA_Freedom - ustala, czy suwak ma działać w poziomie (LORIEN_HORIZ - standardowo) czy w pionie (LORIEN_VERT).
GTSC_Arrows - jeżeli tag ten zostanie użyty, to do suwaka zostanie dołączony komplet dwóch gadżetów-strzałek, powodujących zmianę położenia suwaka. Dana tego tagu oznacza rozmiar strzałek, w zależności od "PGA_Freedom" szerokość bądź wysokość.

- SLIDER_KIND - to również gadżet-suwak, ale o nieco innym zastosowaniu niż "SCROLLER_KIND". Używa się go zwykle do graficznego przedstawienia jakiejś wartości numerycznej, np. natężenia dźwięku, składowych RGB koloru itp. Gadżet zajmuje się również wyświetleniem obecnej wartości na ekranie. Tagi:
GTSL_Min, GTSL_Max - ustalają minimalną i maksymalną wartość liczby reprezentowanej przez gadżet - na ich podstawie wyznacza się wielkość suwaka. Standardowo 0, 15.
GTSL_Level - początkowa wartość liczby - na jej podstawie wyznacza się początkowe położenie suwaka. Standardowo 0.
PGA_Freedom - to samo co w SCROLLER_KIND.
GTSL_LevelFormat - parametrem jest napis ustalający sposób wyświetlenia bieżącej wartości liczby reprezentowanej przez gadżet na ekranie. Napis podaje się niemal tak samo, jak dla standardowej funkcji języka "C" printf(), jednak zamiast "%d" należy używać "%ld" (tak samo, jak w omówionej w części 3 funkcji EasyRequestArgs()). Standardowo napis nie jest wyświetlany.
GTSL_MaxLevelLen - ustala, jaka jest maksymalna długość napisu opisanego wcześniejszym tagiem - powoduje po prostu przydzielenie bufora odpowiedniej wielkości, nie ma wpływu na miejsce wyświetlania napisu. Podanie tego tagu jest konieczne, jeżeli używa się też GTSL_LevelFormat.
GTSL_LevelPlace - ustala, po której stronie gadżetu ma być wypisywany opisany wcześniejszymi tagami napis - PLACETEXT_LEFT (standardowo) itd. Patrz opis pola "ng_Flags" struktury "NewGadget" w poprzedniej części.

Uff, wreszcie koniec tych stałych...

Najwygodniej tworzy się gadżety "wrzucając" omówioną w poprzedniej części funkcję CreateGadgetA() w dużą pętlę. Niezbędne dla niej parametry pobiera się z trzech tablic: tablicy struktur "NewGadget", tablicy typów gadżetów i tablicy tagów. W czwartej tablicy zapamiętuje się adresy utworzonych przez funkcję gadżetów. Rozwiązanie to jest zwykle krótsze od konkurencyjnego, polegającego na wypełnianiu pomocniczej struktury "NewGadget" różnymi danymi i wywoływaniu funkcji CreateGadgetA() osobno dla każdego gadżetu.
Nie rozumiecie? Wypełniamy strukturę "NewGadget", wywołujemy funkcję, potem zmieniamy parę pól struktury "NewGadget" (nazwę, pozycję), wywołujemy znowu funkcję itd. Po prostu praktyka wykazuje, że zdefiniowanie tablicy struktur jest mniej "kosztowne" z punktu widzenia długości uzyskanego programu niż "ręczne" wypełnianie wszystkich pól, a poza tym w tej drugiej metodzie łatwiej o pomyłkę... Nie bez znaczenia jest też to, że metody z tablicami używają programy do projektowania interfejsu użytkownika (Designer, GadToolsBox itd.).

Gdy gadżety nie są nam już dłużej potrzebne, należy użyć funkcji:

void FreeGadgets( struct Gadget *gad );

Jako parametr należy podać wartość zwróconą przez funkcję CreateContext().

Kolejną sprawą jest kwestia skalowalnego interfejsu. Czasy "tryumfalnego pochodu przez świat" czcionki topaz/8 skończyły się wraz z upowszechnieniem się trybów graficznych o wyższych rozdzielczościach niż 640 x 256 dpi. Rozdzielczość w okolicach 800 x 600 nikogo już dzisiaj nie szokuje, a przy takiej rozdzielczości nikt normalny nie używa czcionki o wysokości 8 punktów. Jeżeli chcemy, aby nasz program nie został przez posiadacza dobrego monitora skasowany w 10 sekund po pierwszym uruchomieniu, to musimy stworzyć interfejs dopasowujący się do czcionki ekranowej.

Niezbędne są nam więc informacje o parametrach użytej na ekranie czcionki. Uzyskuje się je przy użyciu funkcji biblioteki "graphics.library":

void AskFont( struct RastPort *rp, struct TextAttr *textAttr );

Funkcja pobiera z RastPortu "rp" informacje o używanej czcionce i zapisuje je w dostarczonej jako drugi parametr strukturze "TextAttr".

Jeżeli nie używamy okna typu GIMMEZEROZERO (patrz część 4.), to potrzebujemy też informacji o wysokości górnej listwy oraz szerokości ramki z lewej strony okna. W części 9. Darek pokazał Wam, jak uzyskać te dane ze struktury "Window". Teraz jednak musimy je znać wcześniej, przed otwarciem okna - korzystamy więc z pól struktury "Screen". Wysokość górnej listwy jest równa sumie wysokości czcionki oraz pola "WBorTop" powiększonego o 1, zaś szerokość lewej ramki jest zapisana w polu "WBorLeft".

Zaleca się, aby interfejs projektować pod kątem czcionki topaz/8 i rozdzielczości 640 x 200 dpi, jako że są to minimalne atrybuty, które ma każda Amiga.

Tak też robimy w naszym przykładzie - współrzędne i rozmiary są podane dla czcionki o rozmiarze 8 x 8 i okna typu GIMMEZEROZERO. Każdą współrzędną skalujemy za pomocą funkcji wyliczx() i wyliczy() - funkcje te korzystają ze zmiennych "fontx" i "fonty", które są niczym innym, jak szerokością i wysokością czcionki. Użyte w tych funkcjach wyrażenie "+4" służy zaokrąglaniu wyniku "do najbliższej całkowitej" - bez niego część ułamkowa wyniku byłaby pomijana (zaokrąglanie "w dół"). Dla czcionek proporcjonalnych "fontx" jest siłą rzeczy uśrednione i niektóre napisy mogą "wychodzić" poza ramki.
Jeżeli to kogoś nie zadowala, to może używać lepszych algorytmów - za pomocą poznanych w części 8. funkcji sprawdzać rzeczywistą szerokość napisów i odpowiednio przesuwać/skalować gadżety. Jest to jednak w wypadku bardziej złożonych układów gadżetów kawał roboty, dlatego my zadowolimy się algorytmem prymitywniejszym ("Lepszy rydz niż nic").

Przed stworzeniem gadżetów sprawdzamy, czy powiększone okno zmieści się na ekranie. Jeżeli nie, to wymuszamy użycie standardowej czcionki "topaz 8".

Na tym musimy już dziś zakończyć. Za miesiąc omówimy dokładnie pokazaną dzisiaj część listingu i przedstawimy kolejny jego fragment.