Kontrolki opcji

Powrót do spisu treści

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ąta
OL038 - 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


obrazek

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 opcji
DoEvents()  - 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;
}

obrazek


Podsumowanie


   I Liceum Ogólnokształcące   
im. Kazimierza Brodzińskiego
w Tarnowie

©2023 mgr Jerzy Wałaszek

Dokument ten rozpowszechniany jest zgodnie z zasadami licencji
GNU Free Documentation License.

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