Wymagane jest zapoznanie się z następującymi podrozdziałami:
P019 - Pierwszy program dla Windows
OL031 - instalacja biblioteki SDL w środowisku Dev-C++
OL032 - inicjalizacja biblioteki SDL
OL033 - powierzchnie graficzne w SDL
OL034 - przygotowanie plików własnej biblioteki graficznej
OL035 - rysowanie po powierzchni graficznej
OL036 - algorytm Bresenhama rysowania odcinka
OL037 - obcinanie grafiki do prostokątaOL038 - podstawowe algorytmy wypełniania obszarów
OL039 - algorytm Smitha
OL040 - praca w środowisku sterowanym zdarzeniami
OL041 - czcionka bitmapowa
OL042 - czcionka wektorowa
OL043 - przyciski poleceń
OL044 - menu
OL045 - edytor jednowierszowy
UWAGA!!!
Bieżące opracowanie NIE JEST KURSEM programowania biblioteki SDL. Są to materiały przeznaczone do zajęć na kole informatycznym w I LO w Tarnowie.
Przed pracą z tym rozdziałem utwórz w środowisku Dev-C++ nowy projekt SDL i dołącz do niego pliki SDL_gfx.cpp oraz SDL_gfx.h. Jeśli nie przeczytałeś zalecanych rozdziałów, koniecznie zrób to, a wszystko stanie się jasne. W przeciwnym razie odpuść sobie również ten rozdział. Opis funkcji bibliotecznych znajdziesz w podsumowaniu SDL100 i GUI003.
Artykuł nie jest już rozwijany
Kontrolki opcji stanowią nierozłączną część wielu okienek dialogowych. Dzięki nim użytkownik posiada możliwość włączania lub blokowania różnych funkcji programu. Rozróżniamy dwa rodzaje kontrolek opcji:
- kontrolki opcji niezależnych (ang. check boxes) - każda kontrolka steruje jedną opcją i jest zupełnie niezależna od pozostałych kontrolek
- kontrolki opcji zależnych (ang. radio buttons) - kontrolki tworzą grupę, w której wybrana może być tylko jedna z opcji.
Każda kontrolka opcji może znajdować się w jednym z trzech stanów:
- opcja wybrana
- opcja nie wybrana
- kontrolka zablokowana - użytkownik nie może zmienić jej stanu.Kliknięcie kursorem myszki w aktywną kontrolkę zmienia stan jej opcji na przeciwny. W przypadku kontrolki opcji zależnych kliknięcie kontrolki powoduje automatycznie wyłączenie opcji kontrolek z tej samej grupy, tak aby w całej grupie była wybrana tylko jedna opcja.
Utwórz projekt SDL. Dodaj do niego pliki biblioteki SDL_gfx, które znajdziesz w podsumowaniu SDL100 (nie zapomnij o ustawieniach linkera). Następnie dołącz pliki interfejsu GUI z podsumowania GUI003. Do katalogu projektowego przekopiuj czcionkę vecprop9x12.fnt (możesz również poeksperymentować z inną czcionką, my wybraliśmy tę czcionkę z powodu jej czytelności). Jesteś gotowy do tworzenia kontrolek opcji dla interfejsu GUI.
Na koniec pliku nagłówkowego SDL_gui.h dopisz poniższy fragment tekstu:
class gfxOption; class gfxOptElement : public gfxGUIObject { public: Uint32 idgroup; gfxOptElement * next; gfxOption * owner; bool option; gfxOptElement(Uint32 tg, Uint32 tp, Uint32 id, bool opt, bool en, char * t, void (* fn)(gfxGUIObject *), gfxOptElement * nopt); ~gfxOptElement(); bool DoEvents(SDL_Event * e); void Refresh(); void Unsel(); }; class gfxOption : public gfxGUIObject { public: gfxOptElement * item; gfxOption(SDL_Surface * s, gfxFont * f, SDL_Rect * r, gfxOptElement * it); ~gfxOption(); bool DoEvents(SDL_Event * e); void Refresh(); void Unsel(); };gfxOptElement
Klasa obsługuje element opcji. Dziedziczy wszystkie pola i metody z gfxGUIObject. Pola i funkcje składowe klasy posiadają następujące znaczenie:
idgroup - dla elementów radio buttons określa numer grupy. W obrębie danej grupy może być wybrana tylko jedna opcja. next - wskaźnik następnego elementu opcji na liście. owner - wskaźnik zmiennej klasy gfxOption, do której należy dany element. option - określa stan opcji danego elementu gfxOptElement() - konstruktor
gfxOptElement(tg,tp,id,opt,en,t,fn,nopt)
tg - numer elementu
tp - typ, 0 - opcja niezależna, 1 - opcja zależna
id - w przypadku typu 1 określa numer grupy opcji zależnej
opt - stan początkowy opcji
en - true - element aktywny, false - element wyłączony
t - tekst elementu opcji
fn - wskaźnik funkcji wywoływanej przy zmianie opcji
nopt - wskaźnik następnego elementu opcji~gfxOptElement() - destruktor DoEvents() - obsługuje zdarzenia dla elementu opcji. Jeśli zdarzenie zostanie obsłużone, zwraca false.
DoEvents(e)
e - wskaźnik struktury SDL_Event.Refresh() - wyświetla element opcji Unsel() - kasuje wybór elementu - stan opcji się nie zmienia. gfxOption
Klasa obsługuje listę połączonych ze sobą elementów opcji. Elementy są wyświetlane jeden pod drugim na panelu. Szerokość i wysokość panelu jest obliczana automatycznie. Położenie listy opcji określa prostokąt, w którym należy zainicjować pola x i y. Jeśli pole w będzie posiadało wartość większą od wymaganej szerokości kontrolki, to kontrolka przyjmie tę szerokość, w przeciwnym razie pole w zostanie dopasowane do szerokości elementów opcji.
item - wskaźnik pierwszego elementu na liście opcji gfxOption() - konstruktor
gfxOption(s,f,r,it)
s - wskaźnik struktury SDL_Surface, na której będzie rysowana kontrolka edytora
f - wskaźnik struktury gfxFont określającej czcionkę dla tekstu kontrolki
r - prostokąt definiujący położenie listy opcji
it - głowa listy opcjiDoEvents() - obsługuje zdarzenia dla listy opcji. Jeśli zdarzenie zostanie obsłużone, zwraca false.
DoEvents(e)
e - wskaźnik struktury SDL_Event.Refresh() - wyświetla listę opcji Unsel() - kasuje wybór elementu na liście opcji - stan opcji się nie zmienia. Na końcu pliku SDL_gui.cpp dopisz poniższy fragment programu:
// **************************** // *** Obsługa klasy Option *** // **************************** // Konstruktor elementu opcji //--------------------------- gfxOptElement::gfxOptElement(Uint32 tg, Uint32 tp, Uint32 id, bool opt, bool en, char * t, void (* fn)(gfxGUIObject *), gfxOptElement * nopt) { tag = tg; type = tp; idgroup = id; sel = false; option = opt; enabled = en; text = t; call = fn; next = nopt; } // Destruktor elementu opcji //-------------------------- gfxOptElement::~gfxOptElement() { if(next) delete next; } // Obsługa zdarzeń w elemencie opcji //---------------------------------- bool gfxOptElement::DoEvents(SDL_Event * e) { switch(e->type) { case SDL_MOUSEMOTION: if(MouseHit(e->motion.x, e->motion.y)) { if(enabled && !sel) { owner->Unsel(); sel = true; Refresh(); } return false; } else Unsel(); break; case SDL_MOUSEBUTTONDOWN: if((e->button.button == SDL_BUTTON_LEFT) && MouseHit(e->button.x, e->button.y)) { if(enabled) { option = !option; if(type) { option = true; gfxOptElement * p = owner->item; while(p) { if((p != this) && (p->idgroup == idgroup)) p->option = false; p->Refresh(); p = p->next; } } else Refresh(); if(call) (* call)(this); } return false; } break; } return true; } // Wyświetlanie elementu opcji //---------------------------- void gfxOptElement::Refresh() { Uint32 fc = enabled ? C_TEXT : C_DIS; Uint32 bc = sel ? C_UPPER : C_FACE; SDL_Rect r; r.x = rect.x + 2; r.y = rect.y + 2; r.w = r.h = font->h; if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen); SDL_FillRect(screen, &rect, bc); gfxDrawText(screen, font, text, rect.x + 6 + font->h, rect.y + 2, fc, -1); if(type) { Uint32 rr = (r.w >> 1) - 1; Uint32 x = r.x + rr; Uint32 y = r.y + rr; gfxFillCircle(screen, x - 1, y - 1, rr, C_LOWER); gfxFillCircle(screen, x + 1, y + 1, rr, C_UPPER); gfxFillCircle(screen, x, y, rr, C_TEXTBG); if(option) { rr >>= 1; if(rr < 1) rr = 1; gfxFillCircle(screen, x, y, rr, fc); } } else { gfxHLine(screen, r.x, r.y, C_LOWER, r.w); gfxVLine(screen, r.x, r.y + 1, C_LOWER, r.h - 2); gfxHLine(screen, r.x, r.y + r.h - 1, C_UPPER, r.w); gfxVLine(screen, r.x + r.w - 1, r.y + 1, C_UPPER, r.h - 2); r.x++; r.y++; r.w -= 2; r.h -= 2; SDL_FillRect(screen, &r, C_TEXTBG); if(option) { gfxLine(screen, r.x + 1, r.y + 1, r.x + r.w - 2, r.y + r.h - 2, fc); gfxLine(screen, r.x + r.w - 2, r.y + 1, r.x + 1, r.y + r.h - 2, fc); } } if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); Update(); } // Usuwa zaznaczenie elementu //--------------------------- void gfxOptElement::Unsel() { if(enabled) { sel = false; Refresh(); } } // Konstruktor opcji //------------------ gfxOption::gfxOption(SDL_Surface * s, gfxFont * f, SDL_Rect * r, gfxOptElement * it) { screen = s; font = f; rect = * r; gfxOptElement * p = item = it; Sint32 len, maxw = 0, maxh = 0; while(p) { p->screen = screen; p->font = font; p->owner = this; len = gfxTextLength(font, p->text); if(len > maxw) maxw = len; p->rect.x = rect.x + 1; p->rect.y = rect.y + 1 + maxh; p->rect.h = font->h + 4; maxh += p->rect.h; p = p->next; } maxw += 10 + font->h; if(rect.w < maxw) rect.w = maxw; p = item; while(p) { p->rect.w = rect.w - 2; p = p->next; } Refresh(); } // Destruktor opcji //----------------- gfxOption::~gfxOption() { if(item) delete item; } // Obsługa zdarzeń w opcjach //-------------------------- bool gfxOption::DoEvents(SDL_Event * e) { bool result = true; gfxOptElement * p = item; while(p) { result = result && p -> DoEvents(e); p = p->next; } return result; } // Wyświetlanie opcji //------------------- void gfxOption::Refresh() { gfxOptElement * p = item; while(p) { p -> Refresh(); p = p->next; } if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen); gfxHLine(screen, rect.x, rect.y, C_UPPER, rect.w); gfxVLine(screen, rect.x, rect.y + 1,C_UPPER, rect.h - 1); gfxHLine(screen, rect.x + 1, rect.y + rect.h - 1, C_LOWER, rect.w - 1); gfxVLine(screen, rect.x + rect.w - 1, rect.y + 1, C_LOWER, rect.h - 1); if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); SDL_UpdateRect(screen, rect.x, rect.y, rect.w, 1); SDL_UpdateRect(screen, rect.x, rect.y + 1, 1, rect.h - 1); SDL_UpdateRect(screen, rect.x + 1, rect.y + rect.h - 1, rect.w - 1, 1); SDL_UpdateRect(screen, rect.x + rect.w - 1, rect.y + 1, 1, rect.h - 1); } // Usuwa wybór elementów opcji //---------------------------- void gfxOption::Unsel() { gfxOptElement * p = item; while(p) { p -> Unsel(); p = p->next; } }W poniższym programie testowym sterujemy za pomocą opcji rysowaniem różnych figur w różnych kolorach:
// I Liceum Ogólnokształcące // w Tarnowie // Koło informatyczne // // P039 - Kontrolki opcji //------------------------ #include <SDL/SDL_gfx.h> #include <SDL/SDL_gui.h> const int SCRX = 320; // stałe określające szerokość i wysokość const int SCRY = 240; // ekranu w pikselach SDL_Surface * screen; gfxFont * font = gfxOpenFont("vecprop9x12.fnt"); gfxOption * opt; // Funkcja wywoływana przez kontrolkę opcji //----------------------------------------- void fn_o(gfxGUIObject * sender) { gfxOptElement * p = opt->item; Uint32 color = 0; SDL_Rect r; r.x = opt->rect.x + opt->rect.w; r.y = opt->rect.y; r.w = screen->w - r.x; r.h = opt->rect.h; if(r.w > r.h) r.w = r.h; else r.h = r.w; r.x += 8; r.y += 8; r.w -= 16; r.h -= 16; if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen); SDL_FillRect(screen, &r, C_FACE); while(p) { if(p->option) switch(p->tag) { case 1 : color = color | 0xff0000; break; case 2 : color = color | 0x00ff00; break; case 3 : color = color | 0x0000ff; break; case 4 : gfxMoveTo(r.x + (r.w >> 1), r.y); gfxLineTo(screen, r.x + r.w - 1, r.y + r.h - 1, color); gfxLineTo(screen, r.x, r.y + r.h - 1, color); gfxLineTo(screen, r.x + (r.w >> 1), r.y, color); gfxFloodFill(screen, r.x + (r.w >> 1), r.y + (r.h >> 1), color); break; case 5 : SDL_FillRect(screen, &r, color); break; case 6 : gfxMoveTo(r.x + (r.w >> 1), r.y); gfxLineTo(screen, r.x + r.w - 1, r.y + (r.h >> 2), color); gfxLineTo(screen, r.x + r.w - 1, r.y + (r.h >> 2) + (r.h >> 1), color); gfxLineTo(screen, r.x + (r.w >> 1), r.y + r.h - 1, color); gfxLineTo(screen, r.x, r.y + (r.h >> 2) + (r.h >> 1), color); gfxLineTo(screen, r.x, r.y + (r.h >> 2), color); gfxLineTo(screen, r.x + (r.w >> 1), r.y, color); gfxFloodFill(screen, r.x + (r.w >> 1), r.y + (r.h >> 1), color); break; case 7 : gfxMoveTo(r.x + (r.w >> 2), r.y); gfxLineTo(screen, r.x + (r.w >> 1) + (r.w >> 2), r.y, color); gfxLineTo(screen, r.x + r.w - 1, r.y + r.h - 1, color); gfxLineTo(screen, r.x, r.y + r.h - 1, color); gfxLineTo(screen, r.x + (r.w >> 2), r.y, color); gfxFloodFill(screen, r.x + (r.w >> 1), r.y + (r.h >> 1), color); break; } p = p->next; } if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); SDL_UpdateRect(screen, r.x, r.y, r.w, r.h); } // Funkcje wywoływane przez przyciski //----------------------------------- void fn_b1(gfxGUIObject * sender) { static bool opttab[] = {false, false, false, true, false, false, false, true, true}; gfxOptElement * p = opt->item; Sint32 i = 0; while(p) { p->option = opttab[i++]; p->Refresh(); p = p->next; } fn_o(NULL); } void fn_b2(gfxGUIObject * sender) { exit(0); } // Funkcja tworzy kontrolki opcji //------------------------------- gfxOption * Options() { gfxOptElement * o9 = new gfxOptElement(9, 0, 3, true, false, "Opcja zablokowana 2", NULL, NULL); gfxOptElement * o8 = new gfxOptElement(8, 1, 2, true, false, "Opcja zablokowana 1", NULL, o9); gfxOptElement * o7 = new gfxOptElement(7, 1, 1, false, true, "Trapez", fn_o, o8); gfxOptElement * o6 = new gfxOptElement(6, 1, 1, false, true, "Sześciokąt", fn_o, o7); gfxOptElement * o5 = new gfxOptElement(5, 1, 1, false, true, "Kwadrat", fn_o, o6); gfxOptElement * o4 = new gfxOptElement(4, 1, 1, true, true, "Trójkąt", fn_o, o5); gfxOptElement * o3 = new gfxOptElement(3, 0, 0, false, true, "Składowa NIEBIESKA", fn_o, o4); gfxOptElement * o2 = new gfxOptElement(2, 0, 0, false, true, "Składowa ZIELONA", fn_o, o3); gfxOptElement * o1 = new gfxOptElement(1, 0, 0, false, true, "Składowa CZERWONA", fn_o, o2); SDL_Rect r; r.x = 8; r.y = 8; r.w = r.h = 0; return new gfxOption(screen, font, &r, o1); } //*********************** // *** Program główny *** //*********************** int main(int argc, char * argv[]) { char * t = "Test kontrolek opcji"; if(SDL_Init(SDL_INIT_VIDEO)) exit(-1); atexit(SDL_Quit); if(!(screen = SDL_SetVideoMode(SCRX, SCRY, 32, SDL_HWSURFACE))) exit(-1); SDL_WM_SetCaption(t, ""); if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen); SDL_FillRect(screen, NULL, C_FACE); if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); SDL_UpdateRect(screen, 0, 0, 0, 0); opt = Options(); SDL_Rect r; r.x = opt->rect.x; r.y = opt->rect.y + opt->rect.h + 8; r.w = opt->rect.w; r.h = font->h + 8; gfxButton * button1 = new gfxButton(0, true, screen, font, &r, "Resetuj opcje", fn_b1); r.y += 16 + font->h; gfxButton * button2 = new gfxButton(1, true, screen, font, &r, "Zakończ aplikację", fn_b2); fn_b1(NULL); SDL_Event event; bool running = true; while(running && SDL_WaitEvent(&event)) { // najpierw obsługujemy zdarzenia w edytorze if(opt->DoEvents(&event) && button1->DoEvents(&event) && button2->DoEvents(&event)) // a później reszta zdarzeń switch(event.type) { case SDL_QUIT: running = false; break; } } // zamykamy czcionkę gfxCloseFont(font); // zamykamy menu delete opt; delete button1; delete button2; return 0; }
![]() | I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe