C dla każdego (cz. 13.)
Gadżety
Zgodnie z obietnicą, dziś zabieramy się wreszcie
do gadżetów -- najważniejszego po ekranach i
oknach fragmentu graficznego interfejsu
użytkownika.
Gadżety, jak zresztą całe GUI, są zarządzane przez
bibliotekę Intuition. Tak samo jednak, jak w
wypadku górnego menu, biblioteka
"gadtools.library" zawiera kilka funkcji w
niesłychany wręcz sposób upraszczających tworzenie
gadżetów oraz późniejsze zarządzanie nimi. Wypada
wiedzieć, że w OS 1.3 tworzenie bardziej
skomplikowanych gadżetów było udręką programistów!
Trzeba było samodzielnie zdefiniować dla każdego
gadżetu strukturę "Gadget", opisującą rozmiar
gadżetu i jego typ, strukturę "Border", opisującą
rozmiar i kształt ramki wokół gadżetu, strukturë
"IntuiText", opisującą tekst gadżetu i jego
ułożenie, nierzadko równieű dodatkowe struktury,
jak "StringInfo", "PropInfo" itd. Tymczasem,
korzystając z "gadtools.library", wystarczy
zdefiniować jedną prostą strukturę dla każdego
gadżetu, a otrzymujemy rozplanowany gadżet o
przyzwoitym, trójwymiarowym wyglądzie. Będzie to z
pewnością pewnym uproszczeniem, ale nie pomylë się
chyba wiele twierdząc, że właśnie pojawienie się w
OS 2.0 biblioteki "gadtools.library" spowodowało,
że przestały powstawać programy działające pod OS
1.3.
Struktura zawierająca informacje niezbędne do
stworzenia gadżetu znajduje się w pliku
"libraries/gadtools.h" i wygląda tak:
struct NewGadget
{
WORD ng_LeftEdge, ng_TopEdge;
WORD ng_Width, ng_Height;
UBYTE *ng_GadgetText;
struct TextAttr *ng_TextAttr;
UWORD ng_GadgetID;
ULONG ng_Flags;
APTR ng_VisualInfo;
APTR ng_UserData;
};
Znaczenie pól jest następujące:
ng_LeftEdge, ng_TopEdge, ng_Width, ng_Height --
współrzędna lewego górnego rogu gadżetu oraz jego
rozmiar.
ng_GadgetText -- tekst opisujący gadżet. Zaleca
się, aby w wypadku gadżetów-przycisków, których
naciśnięcie powoduje otwarcie jakiegoô okienka,
tekst ten był zakończony trzema kropkami --
ułatwia to użytkownikowi orientację.
ng_TextAttr -- wskaźnik na strukturë "TextAttr",
opisującą czcionkę, jaką mają być wypisywane
teksty w gadżecie.
ng_Flags -- flagi odnoszące się do tekstu,
opisującego gadżet. Flagi PLACETEXT_LEFT,
PLACETEXT_RIGHT, PLACETEXT_ABOVE, PLACETEXT_BELOW
i PLACETEXT_IN ustalają, w którym miejscu ma się
znaleźć ten tekst (odpowiednio: po lewej, po
prawej, powyżej, poniżej, wewnątrz); flaga
NG_HIGHLABEL powoduje, że tekst "ng_GadgetText"
zostanie wypisany jasnym kolorem.
ng_VisualInfo -- wskaźnik na prywatną strukturę
uzyskaną za pomocą funkcji GetVisualInfo(), którą
opisaliśmy przy okazji górnego menu.
ng_GadgetID, ng_UserData - pola pozostawione dla
nas, do wykorzystania w dowolny sposób. W
"ng_GadgetID" umieszcza się zwykle numer gadżetu.
Po tym można później rozpoznać, który gadżet
został wciśnięty. "ng_UserData" może np. wskazywać
na funkcję, która ma zostać wywołana po wciśnięciu
danego gadżetu.
Tworzenie listy gadżetów za pomocą GadTools jest
dwuetapowe:
Najpierw należy wywołać funkcję:
struct Gadget *CreateContext( struct Gadget
**glistptr );
Funkcja ta tworzy "zaczątek" listy gadżetów, w
którym inne funkcje biblioteki GadTools będą
odnotowywały prywatne informacje. Parametr wygląda
może groźnie -- "wskaźnik na wskaźnik na gadżet".
Nie należy się go jednak bać. Jako parametr
funkcji należy podać po prostu adres wyzerowanej
wcześniej zmiennej, będącej wskaźnikiem na gadżet
-- wartość tej zmiennej zostanie zmieniona.
Funkcja zwraca adres "zaczątku" lub, jak to zwykle
bywa, NULL w wypadku niepowodzenia.
Po wykonaniu powyższych operacji należy dla każdej
struktury "NewGadget" z osobna wywołać funkcję:
struct Gadget *CreateGadgetA( unsigned long kind,
struct Gadget *gad, struct NewGadget *ng, struct
TagItem *taglist );
struct Gadget *CreateGadget( unsigned long kind,
struct Gadget *gad, struct NewGadget *ng, Tag
tag1, ... );
"kind" oznacza rodzaj gadżetu, jaki ma zostać
utworzony (o tym za chwilë).
"gad" to wskaźnik na poprzedni gadżet, tzn. na
gadżet utworzony przy poprzednim wywołaniu tej
funkcji lub na "zaczątek" przy jej pierwszym
wywołaniu.
"ng" to wskaźnik na opisaną wcześniej strukturę
"NewGadget", która opisuje podstawowe atrybuty
gadżetu do utworzenia.
Funkcja akceptuje również wiele tagów, niemal
wszystkie jednak zależą od rodzaju gadżetu, który
ma zostać utworzony. Istnieją tylko dwa tagi
"uniwersalne":
GT_Underscore -- daną tego taga jest litera
(char). Ustala ona, jaka litera w tekście pola
"ng_GadgetText" struktury "NewGadget" ma być
uznawana za identyfikator podkreślenia --
właściwie zawsze jako wartość tego taga ustala się
'_'. Użycie w tekście "ng_GadgetText" podkreślnika
spowoduje, że znajdująca się za nim litera
zostanie wyświetlona jako podkreślona, co jest
używane do poinformowania użytkownika o "skrócie z
klawiatury" dla danego gadżetu. Standardowo żadna
litera nie jest identyfikatorem podkreślenia.
GA_Disabled -- dana taga jest typu BOOL. Gdy jest
ona niezerowa, to gadżet zostanie utworzony jako
"wyłączony", tzn. niemożliwy do obsługi za pomocą
myszy -- "za mgiełką". Standardowo gadżet jest
tworzony jako włączony.
Czeka nas teraz to, czego nie lubimy i staramy się
w tym kursie unikać, a mianowicie długa nudna
lista stałych -- niestety, w tym wypadku inaczej
postąpić nie mogliśmy... Oto rodzaje gadżetów
oferowane przez GadTools oraz najważniejsze z
akceptowanych przez nie tagów:
BUTTON_KIND -- klasyczny gadżet-przycisk, jak
np. Save w preferencjach.
CHECKBOX_KIND -- gadżet-fajeczka, do włączenia
bądź wyłączenia jakiejô opcji. Tagi:
GTCB_Checked -- dana typu BOOL ustala, czy gadżet
jest zaznaczony (ma fajeczkę), czy nie.
Standardowo nie jest zaznaczony.
GTCB_Scaled -- dana typu BOOL ustala, czy gadżet
może mieć dowolny rozmiar -- w OS 2.0 gadżety
"fajeczkowe" mogły mieć tylko jeden ustalony
rozmiar 26 x 11, w OS 3.0 usunięto to ograniczenie
-- gadżety te mogą być skalowalne, ale tylko
wtedy, gdy został użyty ten tag z daną niezerową
(najlepiej po prostu podać TRUE). Patrz też
obrazek, na którym pokazałem główne różnice między
gadżetami w OS 2.04 i OS 3.00.
STRING_KIND, INTEGER_KIND -- gadżety do
wprowadzania informacji z klawiatury, odpowiednio
tekstu lub liczby całkowitej dziesiętnej (może być
ujemna). Tagi:
GTST_String, GTIN_Number -- początkowa zawartość
gadżetu (odpowiednio: wskaźnik na ciąg znaków lub
liczba typu long). Standardowo odpowiednio: napis
pusty albo 0.
GTST_MaxChars, GTIN_MaxChars -- maksymalna liczba
znaków, jaką można wpisać w gadżet. Standardowo
odpowiednio: 64 albo 10.
STRINGA_Justification -- ustala, czy wpisywany
tekst ma być dosunięty do lewej krawędzi
(GACT_STRINGLEFT -- standardowe), do prawej
(GACT_STRINGRIGHT), czy teű ma być centrowany
(GACT_STRINGCENTER).
TEXT_KIND, NUMBER_KIND -- gadżety do
wyświetlania napisu lub liczby całkowitej
dziesiętnej. Nie można ich wyłączać (GA_Disabled).
Nie miałoby to zresztą większego sensu -- nie
obsługuje się ich myszką. Z tego samego powodu nie
"słuchają" też taga GT_Underscore. Tagi:
GTTX_Text, GTNM_Number -- to samo, co wcześniej z
GTST_String i GTIN_Number.
GTTX_Border, GTNM_Border -- dana typu BOOL ustala,
czy gadżet ma być otoczony wklęsłą trójwymiarową
ramką (standardowo nie).
LISTVIEW_KIND -- gadżet-lista do wyświetlania
obszernej listy pozycji i ewentualnego wybrania
jednej z nich, wyposażony w gadżety do przesuwania
jego zawartości, np. gadżet do wyboru drivera
drukarki w preferencjach. Gadżety te można
wyłączać od OS 3.0. O ile niemal wszystkie
pozostałe gadżety mogą mieć dowolne rozmiary
(przynajmniej od OS 3.0 -- patrz opis
CHECKBOX_KIND i MX_KIND), to wysokości
gadżetów-list nie można powiększyć czy pomniejszyć
o 1 punkt -- wysokość jest zawsze taka, aby w polu
selekcji zmieściła się całkowita liczba pozycji, w
razie potrzeby podana w polu "ng_Height" wysokość
jest zaokrąglana w dół. Tagi:
GTLV_Labels -- daną jest wskaźnik na strukturę
"List", którą poznaliście w poprzedniej części. W
liście tej powinny być zapamiętane poszczególne
pozycje.
GTLV_ReadOnly -- dana typu BOOL ustala, czy gadżet
ma być przeznaczony tylko do przeglądania, tzn.
czy można klikać na poszczególne pozycje, czy nie.
Standardowo można klikać.
GTLV_ShowSelected -- jeżeli ten tag zostanie
użyty, to wybrana w danym momencie pozycja będzie
wyświetlana. Daną taga jest wskaźnik na strukturę
"Gadget", opisująca stworzony wcześniej gadżet
typu STRING_KIND -- po zaznaczeniu pozycji w
liście jej tekst będzie automatycznie kopiowany do
wskazanego gadżetu, co umożliwia np. edycję listy.
Jeżeli jako daną poda się NULL, to wybrana pozycja
będzie zaznaczona w inny sposób: pod OS 2.0 na
dole listy będzie istniał gadżet typu TEXT_KIND, w
którym będzie wyświetlana wybrana pozycja, a od OS
3.0 wybrana pozycja będzie po prostu namalowana
innym kolorem (patrz obrazek).
GTLV_Selected -- ustala wybraną początkowo pozycję
w liście. Należy podać numer porządkowy pozycji w
liście (zaczynając od 0) bądź -1, aby nie mieć
żadnej pozycji zaznaczonej (standardowo -1).
GTLV_Top, GTLV_MakeVisible -- tych dwóch tagów
używa się w połączeniu z "GTLV_Selected". Otóż
zazwyczaj zależy nam, aby wybrana pozycja była
widoczna w liście -- przynajmniej na początku, po
otwarciu okna. Jeżeli lista pozycji jest na tyle
długa, że nie wszystkie mogą być wyświetlone na
raz, a pozycja wybrana znajduje się gdzieś pod
koniec listy, to nie będzie ona widoczna, co może
wprowadzić użytkownika w błąd. Należy więc
odpowiednio przesunąć listę. W OS 2.0 robi się to
za pomocą taga "GTLV_Top", ustalającego numer
pozycji, która ma być wyświetlana w gadżecie jako
pierwsza -- jako daną podaje się po prostu to
samo, co w tagu "GTLV_Selected". Autorzy systemu
twierdzą, że podana wartość zostanie
"zaokrąglona", jeżeli będzie wykraczała poza
dopuszczalne granice. Mają rację, ale niezupełnie,
bo wykryłem jeden problem -- nie radzę podać
wartości "-1"... Metoda ta jest jednak ogólnie
niezbyt elegancka, o czym można się łatwo
przekonać po prostu patrząc, jak to działa.
Dlatego w OS 3.0 pojawił się tag
"GTLV_MakeVisible", który powoduje przesunięcie
listy tylko o tyle pozycji, aby podana pozycja
stała się widoczna (podaje się to samo, co w tagu
"GTLV_Selected"); nie występuje też problem z
wartością "-1".
CYCLE_KIND -- nazywany przeze mnie
"gadżet-kręciołek", używany do wyboru jednej z
kilku pozycji, przy czym tylko jedna na raz jest
wyświetlana, np. gadżet do wyboru rodzaju papieru
w preferencjach drukarki. Tagi:
GTCY_Labels -- daną jest tablica wskaźników na
napisy, której ostatnim elementem jest NULL.
Napisy te to kolejne pozycje wyświetlane wewnątrz
gadżetu. Użycie tego taga jest OBOWIĄZKOWE.
GTCY_Active -- numer pozycji początkowo aktywnej
(po prostu indeks w tablicy przekazanej przez
"GTCY_Labels"). Standardowo 0.
MX_KIND -- tzw. przyciski radiowe -- o funkcji
takiej samej, jak gadżety poprzedniego rodzaju,
ale zajmują więcej miejsca w oknie, bo wyświetlane
są wszystkie możliwości na raz, np. gadżet do
wyboru parzystości w preferencjach portu
szeregowego. Gadżety te można wyłączać od OS 3.0.
Tagi:
GTMX_Labels, GTMX_Active -- to samo, co wcześniej
z GTCY_Labels i GTCY_Active.
GTMX_Spacing -- odstęp w pionie między pozycjami,
podany w punktach -- standardowo 1.
GTMX_Scaled -- to samo, co wcześniej z
"GTCB_Scaled" (no, może poza tym, że rozmiar
gadżetów w OS 2.0 wynosi 17 x 9). Patrz obrazek.
GTMX_TitlePlace -- ten tag pojawił się w OS 3.0.
Jest tu drobne zamieszanie: w OS 2.0 gadżety te
nie miały tekstu, opisującego gadżet, tzn. pole
"ng_GadgetText" w strukturze "NewGadget" było
ignorowane, a "ng_Flags" ustalało, po której
stronie miały być wyświetlane teksty, opisujące
poszczególne pozycje. W OS 3.0 autorzy systemu
postanowili jednak dodać możliwość wyświetlania
tekstu opisującego gadżet. Dla zachowania
zgodności nie można było zmienię znaczenia pola
"ng_Flags", dodano więc ten tag -- ustala on to,
co dla innych rodzajów gadżetów ustala pole
"ng_Flags"; tekst opisujący gadżet podany w polu
"ng_GadgetText" zostanie wyświetlony tylko wtedy,
gdy tag ten zostanie użyty.
To wszystko na dzisiaj. Za miesiąc dokończymy tę
długą listę. Przedstawimy też pierwszą część
listingu, ukazującego wykorzystanie gadżetów.