Ponieważ projekt jest w trakcie rozwoju i jeszcze wiele może się w nim zmienić nie ma pełnej dokumentacji. Poniżej przedstawione zostały tylko podstawowe informacje mające na celu pomoc w zrozumieniu działania przykładów i umożliwiające wykorzystanie pewnych elementów frameworku.
Aby utworzyć klasę należy stworzyć plik NazwaKlasy.h i NazwaKlasy.c. W pierwszym z nich musi się znaleźć deklaracja klasy
proto_class (NazwaKlasy);
Dodatkowo mogą też wystąpić następujące elementy:
proto_method (PrefiksNazwaMetody, zwracany typ lub void, typ pierwszego argumentu, typ drugiego argumentu, itd.);
proto_setter (Prefiks, NazwaWłasności, typ_własności);
proto_getter (Prefiks, NazwaWłasności, typ_własności);
W pliku NazwaKlasy.c muszą być następujące elementy:
class_data (PrefiksNazwaKlasy) { typ1 pole1; typ2 pole2; itd. };
PrefiksNazwaKlasy *PrefiksNazwaKlasyConstructor(class_arg, dalsze argumenty) { PrefiksNazwaKlasy *self = (PrefiksNazwaKlasy*) PrefixNazwaKlasyBazowejConstructor(clazz, dalsze argumenty); using_data; // Treść konstruktora return self; }
typ PrefiksNazwaMetody(object_arg, pozostałe argumenty) { using_data; // Treść metody }
class_begin (PrefiksNazwaKlasy, PrefiksNazwaKlasyBazowej) { getter (Prefix, NazwaWłasności, nazwa funkcji realizującej odczyt własności, opcjonalnie metadane); setter (Prefix, NazwaWłasności, nazwa funkcji realizującej zapis własności, opcjonalnie metadane); method (NazwaMetody, nazwa funkcji realizującej metodę, opcjonalnie metadane); } class_end
Ponieważ język C nie ma przestrzeni nazw, to aby zapobiec konfliktom stosowane są prefiksy. Powinny być pisane dużymi literami i nie być zbyt długie, najlepiej 2 lub 3 znaki. Wszystkie prefiksy zawierające F jako drugą literę są zarezerwowane dla frameworku.
Czy można jeszcze prościej i szybciej utworzyć klasę. Tak! Stworzyliśmy narzędzie, je również udostępnimy w jednym z kolejnych wydań, które z definicji klasy tworzy pliki .h i .c. Po zmianie definicji klasy można je przegenerować, zmienione zostaną tylko elementy generowane, a nie te dodane przez programistę.
Przykładowy plik definicji klasy:
class TR::TimeoutButton : AF::Button { constructor(); object AF::ProgressBar *progressBar; object AF::TextView *textView; object AF::Animation *animation; type TRState state; type float progressBarValue; type float progressBarPreviousValue; object AF::String *actionText var set; object AF::String *onTimeText var set; object AF::String *tooLateText var set; override void AF::draw(object AF::Graphics *graphics); override void AF::mouseUp(object AF::MouseEventArguments *mouseEventArguments); override void AF::setupComponent(object AF::Application *application); void TR::reset(); }
Wewnątrz metody dostępna jest zmienna self
wskazująca na obiekt, na rzecz którego metoda została wywołana.
W całym pliku *.c dostępna jest zmienna clazz
wskazująca na klasę w nim zdefiniowaną.
Aby uzyskać dostęp do pól klasy należy w metodzie użyć polecenia using_data;
wtedy dostępna staje się zmienna data
wskazująca na strukturę zawierającą dane klasy. Struktura ta jest typu
PrefiksNazwaklasyData
.
Wewnątrz metody można wywołać metodę klasy bazowej dodając do nazwy metody sufiks Super np:
AFDrawSuper(graphics);
Aby móc używać utworzonej klasy należy ją najpierw zarejestrować używając następującego polecenia:
register_class (PrefiksNazwaKlasy);
Musi się ono wykonać zanim zostanie utworzona pierwsza instancja klasy, lub klasy która z niej dziedziczy.
Uwaga! W jednym pliku *.c może znajdować się definicja tylko jednej klasy.
Aby utworzyć instancję klasy należy użyć następującej komendy:
var instancjaKlasy = PrefiksCreateNazwaKlasy(argumenty);
Wywołanie metody wygląda następująco:
PrefiksNazwaMetody(obiekt, argumenty);
Jeżeli obiekt nie implementuje danej metody to zostanie zgłoszony wyjątek.
Wyjątki zgłasza się używając polecenia raise (obiektInformującyORodzajuWyjątku);
Aby złapać wyjątek należy użyć następującego polecenia:
on_error { // Kod } resume (typ1) { // Obsługa wyjątku typu1 } resume (typ2) { // Obsługa wyjątku typu2 } itd. end_resume
W klauzuli resume
, w zmiennej exception
jest dostępny wskaźnik na obiekt
będący parametrem polecenia raise
, które wywołało wyjątek.
Kolekcje to obiekty umożliwiające przechowywanie innych obiektów. AFMutableArrayList jest modyfikowalną kolekcją bazującą na tablicy, jej użycie wygląda następująco:
var lista = AFCreateMutableArrayList(); // Utworzenie kolekcji AFAppendObject(lista, obiekt); // Dodanie elementu obiekt za ostatnim elementem var pobranyObiekt = AFGetObjectAtIndex(lista, 0); // Pobranie obiektu z pod indeksu 0 AFRemoveObject(lista, obiekt); // Usunięcie obiektu size_t rozmiar = AFGetSize(lista); // Ilość elementów kolekcji
Aby pobrać po kolei wszystkie elementy kolekcji najlepiej użyć polecenia for_each
for_each (obiekt in lista) { // Zmienna obiekt będzie zawierała kolejne elementy z kolekcji }
lub
for_each (obiekt, indeks in lista) { // Zmienna obiekt będzie zawierała kolejne elementy z kolekcji // Zmienna indeks będzie zawierać index pobranego elementu }
AFMutableDictionary to słownik, czyli obiekt przechowujący pary klucz, wartość. Jego użycie wygląda następująco:
var slownik = AFCreateMutableDictionary(); AFAppendObjectForKey(slownik, klucza, wartość); // Dodaje wartość dla danego klucza var wartość = AFGetObjectForKey(slownik, klucza); // Pobranie obiektu dla danego klucza AFRemoveObjectForKey(slownik, klucza); // Usuwa wartość przypisaną danemu kluczowi
Do tworzenia programów wykorzystuje się różne komponenty. Część z nich to kontrolki UI, o których za chwilę, a część to elementy niegraficzne. Kilka z nich zostało wymienionych poniżej.
Aby zbudować UI należy utworzyć odpowiednie kontrolki i połączyć je ze sobą.
Każda kontrolka może mieć jednego rodzica.
Kontrolki typu AFWindow, AFButton, AFLabel mogą mieć ustawioną treść. W wypadku AFWindow będzie to
przeważnie panel, w wypadku AFButton i AFLabel będzie to tekst. Aby ustawić treść należy utworzyć obiekt, który będzie ją reprezentował
i użyć metody AFSetContent(kontrolka, obiektTreści)
.
Nieco inaczej ma się sytuacja w wypadku paneli.
Do nich można podłączyć dowolną ilość kontrolek używając polecenia AFAppendComponent(panel, kontrolka)
.
W zależności od rodzaju panelu, zostaną one odpowiednio rozmieszczone.
Jak na razie dostępny jest tylko jeden czyli AFFlowGridPanel.
Kontrolki zgłaszają zdarzenia np. mouse down, mouse up itd, aby z nich skorzystać należy użyć metody AFAddEventHandlerFunction lub AFAddEventHandlerMethod. Metoda AFAddEventHandlerFunction pobiera następujące parametry:
void AFAddEventHandlerFunction( AFObject *object, AFString *eventName, void *context, AFEventHandlerFunctionEntryPoint functionEntryPoint);
Funkcja obsługi zdarzenia zostanie wywołana z następującymi parametrami:
void eventHandlerFunction(void* context, uint32_t index, AFObject *sender, AFObject *eventArguments);
AFAddEventHandlerFunctionWithName
,Metoda AFAddEventHandlerMethod pobiera następujące parametry:
void AFAddEventHandlerMethod(AFObject *object, AFString *eventHandlerName, AFObject *object, AFString *methodName);
Metoda obsługi zdarzenia zostanie wywołana z następującymi parametrami:
void eventHandlerMethod(AFObject *sender, AFObject *eventArguments);
Obiekt okna należy podłączyć do utworzonego wcześniej obiektu aplikacji AFApplication
używając metody AFAppendComponent(obiektAplikacji, obiektOkna)
, gdy całe UI zostanie zbudowane metoda
AFShow(obiektOkna)
powoduje pojawienie się okna, a używając AFRunMainLoop(obiektAplikacji)
uruchamia się przetwarzanie zdarzeń.
Każdy obiekt ma w sobie licznik referencji, który pokazuje w ilu miejscach jest przechowywany wskaźnik na niego.
Zaraz po utworzeniu licznik ten ma wartość 1. Metoda AFObtain(obiekt)
zwiększa wartość tego licznika, metod AFRelease(obiekt)
zmniejsza.
Gdy wartość osiągnie 0 obiekt jest kasowany - zostaje wywołany jego destruktor, a pamięć jest zwalniana.
Istnieje też pula obiektów do zwolnienia. Każdy obiekt jest do niej dołączany w chwili gdy zostaje stworzony.
Pula dla każdego z obiektów, którego ma w swojej kolekcji, wywołuje metodę AFRelease i usuwa go z niej.
Jeżeli zatem nigdy nie została wykonana operacja AFObtain, obiekt zostanie skasowany.
Przetwarzanie puli obiektów odbywa się tuż przed zakończeniem działania aplikacji.
Jeżeli aplikacja utworzyła obiekt AFApplication i wywołała metodę AFRunMainLoop, to obsługa puli odbywa się też
po każdym przetworzeniu komunikatów czyli bezpośrednio zanim aplikacja zacznie czekać na kolejne komunikaty.
Jeśli więc każdemu zapamiętaniu wskaźnika na obiekt towarzyszy
AFObtain, a usunięciu tego zapamiętania AFRelease wszystkie, niewykorzystane obiekty, zostaną w odpowiednim momencie zwolnione
i nie dojdzie do wycieków pamięci.
Biblioteka zawierająca funkcjonalność frameworku jest obecnie przygotowana do linkowania statycznego. Konsekwencją tego jest duży rozmiar plików wykonywalnych ją wykorzystujących. To zmieni się gdy biblioteka zostanie przekształcona na dynamiczną. Istnieją też duże możliwości optymalizacji szybkości działania zaimplementowanych metod i ilości zużywanej przez nie pamięci. Nie należy więc wyciągać wniosków co do tych kwestii na podstawie bieżącej wersji.