Przyciski poleceń

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

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.

 

Artykuł nie jest już rozwijany


obrazekNa kilku kolejnych zajęciach koła informatycznego zajmiemy się tworzeniem kontrolek (ang. controls) prostego interfejsu graficznego dla użytkownika (ang. GUI - Graphical User's Interface). Interfejs GUI pozwala użytkownikowi na sterowanie aplikacją przy pomocy myszki i klawiatury.

Na początek zajmiemy się przyciskami akcji (ang. command button). Przycisk w GUI jest prostokątnym obszarem na powierzchni graficznej, który przy kliknięciu klawiszem myszki powoduje wykonanie skojarzonej z nim akcji. Do realizacji tego zamierzenia będziemy potrzebowali kilka pojęć z języka C++.

Klasa

W języku C++ klasa (ang. class) jest strukturą danych (ang. data structure) , która oprócz pól (ang. data fields) posiada również skojarzone ze sobą funkcje nazywane metodami (ang. methods) lub funkcjami składowymi (ang. member functions). Funkcje składowe posiadają dostęp do danych przechowywanych w klasie. Ponieważ klasa nie jest typem wbudowanym (takim jak np. int, double, char), musimy ją najpierw zdefiniować, zanim będziemy mogli tworzyć na jej podstawie zmienne. Definicja klasy to jakby plan, wg którego będą tworzone przyszłe obiekty - zmienne danej klasy. Definicja klasy w języku C++ powinna być umieszczona na początku testu programu i powinna posiadać następującą postać:

class nazwa_typu_klasy
{
  private:  // pola i metody prywatne
  ...;
  public:   // pola i metody ogólnie dostępne
  ...;
}; 

Jeśli klasa zawiera metody (a zwykle zawiera), to oprócz definicji samego typu klasy musimy podać definicję jej metod. Krótkie metody można zdefiniować bezpośrednio w typie klasy. Natomiast metody dłuższe definiuje się osobno. Dla przykładu napiszemy prosty program zawierający klasę. W tym celu utwórz nowy projekt konsoli (NIE PROJEKT SDL!!!) i wprowadź do niego poniższy tekst programu:

// I Liceum Ogólnokształcące
// w Tarnowie
// Koło informatyczne
//
// P028 - klasy C++
//---------------------------

#include <iostream>

using namespace std;


main()
{
  system("PAUSE");
}

W programie zdefiniujemy klasę o nazwie abc. Klasa będzie zawierała trzy pola a, b i c oraz trzy funkcje składowe:

  1. Konstruktor abc(a,b,c) - jest specjalną funkcją składową, która wywołana zostaje tuż po utworzeniu obiektu klasy. Zadaniem konstruktora jest zwykle inicjalizacja pól danych klasy, aby utworzony na jej podstawie obiekt zawierał już na początku prawidłowe dane.  Zwróć uwagę, iż konstruktor nie posiada typu.
  2. Funkcja pisz_sume() - wyprowadza na standardowe wyjście sumę pól a, b i c.
  3. Funkcja pisz_iloczyn() - wyprowadza na standardowe wyjście iloczyn pól a, b i c.

We wskazanym miejscu na początku programu wpisz poniższą definicję klasy abc:

using namespace std;

class abc
{
  public:

  int a,b,c; // pola danych

  abc(int xa, int xb, int xc) // konstruktor klasy
  {
    cout << "Konstruktor: a = " << xa << " b = " << xb << " c = " << xc << endl;
    a = xa;
    b = xb;
    c = xc;
  }

  void pisz_sume() // funkcja składowa
  {
    cout << "suma = " << a + b + c << endl;
  }

  void pisz_iloczyn() // funkcja składowa
  {
    cout << "iloczyn = " << a * b * c << endl;
  }
};

main()

Teraz w funkcji main() na podstawie definicji typu klasy utworzymy dwie zmienne k1 i k2. Ponieważ klasa posiada zdefiniowany konstruktor, to przy deklaracji zmiennych zostanie on wywołany dla każdej z nich. My musimy dostarczyć tym konstruktorom danych dla pól a, b i c w tworzonych obiektach k1 i k2.

main()
{

  abc k1(5, 10, 15); // tworzymy zmienną klasy abc o nazwie k1
  abc k2(3, 9, 12);  // tworzymy zmienną klasy abc o nazwie k2

  system("PAUSE");
}

Jeśli uruchomisz program na tym etapie, to otrzymasz na ekranie następujący wydruk:

Konstruktor: a = 5 b = 10 c = 15
Konstruktor: a = 3 b = 9 c = 12
Aby kontynuować, naciśnij dowolny klawisz . . .

Pierwsze dwa wiersze pochodzą od konstruktorów obiektów k1 i k2. Teraz dopiszemy następujący kod:

main()
{

  abc k1(5, 10, 15);  // tworzymy zmienną klasy abc o nazwie k1
  abc k2(3, 9, 12);   // tworzymy zmienną klasy abc o nazwie k2
  
  cout << "\n\nDla obiektu k1:\n"
          "---------------\n";
  k1.pisz_sume();
  k1.pisz_iloczyn();
  
  cout << "\n\nDla obiektu k2:\n"
          "---------------\n";
  k2.pisz_sume();
  k2.pisz_iloczyn();

  cout << endl;

  system("PAUSE");
}
Konstruktor: a = 5 b = 10 c = 15
Konstruktor: a = 3 b = 9 c = 12


Dla obiektu k1:
---------------
suma = 30
iloczyn = 750


Dla obiektu k2:
---------------
suma = 24
iloczyn = 324

Aby kontynuować, naciśnij dowolny klawisz . . .

Funkcje składowe można również definiować poza klasą - w takim przypadku w definicji klasy umieszczamy jedynie prototypy funkcji. Zmień definicję klasy w programie na:

class abc
{
  public:
  
  int a,b,c;                   // pola danych

  abc(int xa, int xb, int xc); // konstruktor
  void pisz_sume();            // funkcja składowa
  void pisz_iloczyn();         // funkcja składowa
};

Teraz klasa nie zawiera już definicji swoich funkcji składowych. Musimy je podać poza ciałem definicji klasy.  Pomiędzy definicją klasy a funkcją main() wpisz poniższy fragment programu:

//***********************************
//*** Definicje funkcji klasy abc ***
//***********************************

// konstruktor klasy abc
//----------------------

abc::abc(int xa, int xb, int xc)
{
  cout << "Konstruktor: a = " << xa << " b = " << xb << " c = " << xc << endl;
  a = xa;
  b = xb;
  c = xc;
}

// funkcja składowa klasy abc
//---------------------------

void abc::pisz_sume()
{
  cout << "suma = " << a + b + c << endl;
}
    
// funkcja składowa klasy abc
//---------------------------

void abc::pisz_iloczyn()
{
  cout << "iloczyn = " << a * b * c << endl;
}

Zwróć uwagę, iż każda definicja funkcji składowej jest  poprzedzona nazwą klasy i operatorem zasięgu ::. Sam program działa identycznie.


Klasy mogą być tworzone dynamicznie przy pomocy operatora new. W tym przypadku zmienna jest wskaźnikiem do obszaru pamięci przechowującego dane. Zmienia się zatem sposób dostępu do składników klasy - zamiast operatora . musimy stosować operator ->. Zmień funkcję main() na poniższy fragment programu:

main()
{

  abc * k1 = new abc(5, 10, 15);  // tworzymy zmienną klasy abc o nazwie k1
  abc * k2 = new abc(3, 9, 12);   // tworzymy zmienną klasy abc o nazwie k2
  
  cout << "\n\nDla obiektu k1:\n"
              "---------------\n";
  k1->pisz_sume();
  k1->pisz_iloczyn();
  
  cout << "\n\nDla obiektu k2:\n"
          "---------------\n";
  k2->pisz_sume();
  k2->pisz_iloczyn();

  cout << endl;

  system("PAUSE");
}

Kolejną cechą klas jest dziedziczenie (ang. class inheritance). Polega ono na tym, iż możemy na podstawie klas już zdefiniowanych tworzyć nowe klasy, które dziedziczą pola danych oraz funkcje składowe (za wyjątkiem konstruktorów i destruktorów). Nowe klasy mogą dodawać nowe pola danych oraz nowe funkcje składowe. Dzięki tej własności klas kod programu staje się bardziej zwarty.

Poniższy przykład tworzy klasę Figura zawierającą trzy pola podstawa, wysokosc i pole. Następnie na jej podstawie tworzone są klasy FiguraP - prostokąt oraz FiguraT - trójkąt. W klasie Figura zdefiniowana jest funkcja składowa Pisz(), która wypisuje zawartość pól danych. Funkcję tę dziedziczą obie klasy FiguraP i FiguraT.

// I Liceum Ogólnokształcące
// w Tarnowie
// Koło informatyczne
//
// P029 - dziedziczenie klas
//--------------------------

#include <iostream>

using namespace std;

class Figura
{
  public:
    int podstawa, wysokosc, pole;
    void Pisz()
    {
      cout << "Podstawa = " << podstawa << endl
           << "Wysokosc = " << wysokosc << endl
           << "Pole     = " << pole << endl << endl;
    }
};

class FiguraP : public Figura
{
  public:
    FiguraP()
    {
      podstawa = 10;                  // odziedziczone pole danych
      wysokosc = 20;                  // odziedziczone pole danych
      pole     = podstawa * wysokosc; // odziedziczone pole danych
      cout << "PROSTOKAT\n";
      Pisz();                         // odziedziczona funkcja składowa
    }
};

class FiguraT : public Figura
{
  public:
    FiguraT()
    {
      podstawa = 10;                      // odziedziczone pole danych
      wysokosc = 20;                      // odziedziczone pole danych
      pole     = podstawa * wysokosc / 2; // odziedziczone pole danych
      cout << "TROJKAT\n";
      Pisz();                             // odziedziczona funkcja składowa
    }
};

main()
{
  FiguraP p;
  FiguraT t;
  system("PAUSE");     
}
PROSTOKAT
Podstawa = 10
Wysokosc = 20
Pole     = 200

TROJKAT
Podstawa = 10
Wysokosc = 20
Pole     = 100

Aby kontynuować, naciśnij dowolny klawisz . . .

Wskaźniki do funkcji

O zwykłych wskaźnikach słyszeliśmy wielokrotnie. Wskaźnik jest zmienną przechowującą adres danych. Funkcje też są danymi - to nic innego, jak obszar pamięci operacyjnej komputera, w którym przechowuje się ciąg rozkazów, poleceń dla mikroprocesora. Ponieważ funkcje zajmują pamięć, to posiadają również adres, który język C++ ukrywa pod nazwą funkcji.

Zmienną typu wskaźnik do funkcji tworzymy następująco:

typ_wyniku_funkcji (* nazwa_wskaźnika) (lista_parametrów);

Cechą charakterystyczną tej definicji jest umieszczenie w nawiasach gwiazdki oraz nazwy zmiennej. Ostatnia para nawiasów informuje, iż jest to wskaźnik funkcji, a nie wskaźnik danej.

Wskaźnikowi przypisujemy funkcję następująco:

nazwa_wskaźnika = nazwa_funkcji;

Przypisaną wskaźnikowi funkcję wywołujemy następująco:

(* nazwa_wskaźnika)(argumenty);

Poniższy program wyjaśnia te zagadnienia:

// I Liceum Ogólnokształcące
// w Tarnowie
// Koło informatyczne
//
// P030 - wskaźniki do funkcji
//----------------------------

#include <iostream>

using namespace std;

int suma(int a, int b)
{
  return a + b;
}

  int iloczyn(int a, int b)
{
  return a * b;
}

main()
{
  int (* f)(int a, int b); // deklarujemy wskaźnik do funkcji
                           // o dwóch argumentach całkowitych
                           // i zwracającej typ całkowity
  
  f = suma;                // wskaźnikowi przypisujemy funkcję suma()

  cout << "5 + 10 = " << (* f)(5, 10) << endl;
  
  f = iloczyn;             // wskaźnikowi przypisujemy funkcję iloczyn()
  
  cout << "5 * 10 = " << (* f)(5, 10) << endl << endl;
  
  system("PAUSE");
}
5 + 10 = 15
5 * 10 = 50

Aby kontynuować, naciśnij dowolny klawisz . . .

Wskaźniki do funkcji wykorzystamy w projekcie przycisków akcji do przechowywania adresu funkcji, która ma zostać wywołana przy kliknięciu przycisku.

Przycisk akcji

Elementy graficznego interfejsu użytkownika zrealizujemy w formie osobnych plików - przecież nie w każdej aplikacji wykorzystującej naszą bibliotekę graficzną musimy stosować GUI. A może zechcesz stworzyć własne elementy sterujące.

Utwórz nowy projekt SDL, dołącz do niego pliki SDL_gfx.cpp i SDL_gfx.h. Bieżące wersje tych plików znajdziesz w podsumowaniu  SDL100. Następnie utwórz w projekcie dwa pliki:

SDL_gui.cpp - plik zawierający definicje funkcji składowych
SDL_gui.h - plik nagłówkowy

Oba pliki zapisz w katalogu c:\Dev-cpp\include\SDL\.

W pliku nagłówkowym SDL_gui.h umieść poniższą deklarację klas gfxGUIObject oraz gfxButton. Klasa gfxGUIOBject jest podstawą wszystkich klas naszego interfejsu. Zawiera ona typowe pola danych oraz funkcje składowe używane w kontrolach GUI.

// I Liceum Ogólnokształcące
// w Tarnowie
// Koło informatyczne 2007
//
// Biblioteka kontrolek dla SDL_gfx
//---------------------------------

#define C_FACE   0xafafaf // kolor powierzchni
#define C_UPPER  0xcfcfcf // kolor jasny
#define C_LOWER  0x7f7f7f // kolor ciemny
#define C_TEXT   0x000000 // kolor tekstu
#define C_TEXTBG 0xffffff // kolor tła tekstu
#define C_CURSOR 0xff0000 // kolor kursora tekstowego
#define C_DIS    0x8f8f8f // kolor zblokowanego elementu

class gfxGUIObject
{
  public:
    Uint32        tag, type;
    bool          enabled,sel,open;
    SDL_Surface * screen;
    gfxFont     * font;
    SDL_Rect      rect;
    char        * text;
    void       (* call)(gfxGUIObject *);
    
    bool          MouseHit(Sint32 x, Sint32 y);
    SDL_Rect      ShrinkRect();
    void          Update();
};
    
class gfxButton : public gfxGUIObject
{
  public:

    gfxButton(Uint32 tg, bool en, SDL_Surface * s, gfxFont * f, SDL_Rect * r, char * t,
              void (* fn)(gfxGUIObject *));
    bool DoEvents(SDL_Event * e);
    void Refresh();
};

Klasa gfxGUIObject posiada następujące składowe:

tag  -  pole bez określonego znaczenia, może służyć do identyfikacji obiektu w wywoływanej przez niego funkcji
type  - umożliwia zdefiniowanie typu elementu, jeśli elementy różnią się w ramach danej klasy
enabled  - jeśli element jest aktywny, to zawiera true. Element zablokowany przechowuje tutaj false.
sel  - jeśli element jest wybrany - posiada skupienie wejścia, to pole zawiera true.
open  - niektóre elementy wymagają otwarcia podlisty kontrolek. W takim przypadku pole zawiera true.
screen  - wskaźnik struktury SDL_Surface definiującej używaną przez element powierzchnię graficzną.
font  - wskaźnik do zainicjowanej struktury gfxFont zawierającej opis używanej czcionki do wydruku tekstu
rect  - prostokąt obejmujący element na powierzchni graficznej
text  - wskaźnik bufora zawierającego tekst elementu
call  - funkcja wywoływana przy wyborze elementu. Parametrem funkcji jest wskaźnik wywołującego ją elementu
ShrinkRect()  - funkcja zwraca prostokąt elementu pomniejszony z każdej strony o jeden piksel - przydaje się przy rysowaniu wnętrza elementu
MouseHit()  - funkcja zwraca true, jeśli punkt x,y przekazany w parametrach wywołania znajduje się wewnątrz prostokąta elementu.
Update()  - funkcja uaktualnia obszar ekranu zajęty przez element

Klasa gfxButton dziedziczy wszystkie pola danych klasy gfxGUIObject oraz jej funkcje składowe, a dodaje następujące funkcje własne:

gfxButton()  -  konstruktor klasy gfxButton:
    gfxButton(tg,en,s,f,r,t,fn)
        tg - identyfikator elementu
        en - określa, czy przycisk ma być aktywny - true, czy zablokowany - false
        s - wskaźnik struktury SDL_Surface, na której będzie rysowany przycisk
        f - wskaźnik struktury gfxFont określającej czcionkę dla tekstu opisującego przycisk
        r - prostokąt definiujący przycisk - wymiary są korygowane do szerokości i wysokości tekstu z marginesem 2 pikseli.
        t - wskaźnik tekstu opisującego przycisk. Tekst powinien kończyć się znakiem o kodzie 0.
        fn - wskaźnik funkcji wywoływanej przy kliknięciu przycisku myszką. Funkcja otrzymuje wskaźnik do przycisku.
DoEvents()  - funkcja obsługuje zdarzenia dla przycisku. Jeśli zdarzenie zostało obsłużone, to zwraca false - jest to informacja dla pętli obsługi zdarzeń, iż danego zdarzenia już dalej nie należy obsługiwać w innych elementach, ale oczywiście wszystko zależy od programisty.
    DoEvents(e)
        e - wskaźnik struktury SDL_Event.
Refresh()  - funkcja rysuje przycisk na powierzchni graficznej

W pliku SDL_gui.cpp umieść poniższy tekst programu:

// I Liceum Ogólnokształcące
// w Tarnowie
// Koło informatyczne 2007
//
// Biblioteka kontrolek dla SDL_gfx
//---------------------------------

#include <SDL/SDL_gfx.h>
#include <SDL/SDL_gui.h>

//**********************************
//*** Obsługa klasy gfxGUIObject ***
//**********************************

// Zwraca prostokąt elementu pomniejszony o 1 piksel z każdej strony
//------------------------------------------------------------------

SDL_Rect gfxGUIObject::ShrinkRect()
{
  SDL_Rect r = rect;
  r.x++; r.y++; r.w -= 2; r.h -= 2;
  return r;
}

// Zwraca true, jeśli punkt x,y leży w obszarze prostokąta elementu
//-----------------------------------------------------------------

bool gfxGUIObject::MouseHit(Sint32 x, Sint32 y)
{
  return (x >= rect.x) && (x < rect.x + rect.w) &&
         (y >= rect.y) && (y < rect.y + rect.h);     
}

// Uaktualnia fragment ekranu zajęty przez kontrolkę
//--------------------------------------------------

void gfxGUIObject::Update()
{
  SDL_UpdateRect(screen, rect.x, rect.y, rect.w, rect.h);
}

//*******************************
//*** Obsługa klasy gfxButton ***
//*******************************

// Konstruktor klasy gfxButton
//----------------------------

gfxButton::gfxButton(Uint32 tg, bool en, SDL_Surface * s, gfxFont * f, SDL_Rect * r, char * t,
                     void (* fn)(gfxGUIObject *))
{
  tag     = tg;
  enabled = en;
  screen  = s;
  font    = f;
  rect    = * r;
  text    = t;
  call    = fn;
  sel     = false;
  if(rect.h < font->h + 4) rect.h = font->h + 4;
  Sint32 len = gfxTextLength(font, text) + 4;
  if(rect.w < len) rect.w = len;
  Refresh();
}

// Obsługuje zdarzenia myszki
//---------------------------

bool gfxButton::DoEvents(SDL_Event * e)
{
   switch(e->type)
   {
     case SDL_MOUSEBUTTONDOWN :
       if(enabled && (e->button.button == 1) && MouseHit(e->button.x, e->button.y))
       {
         sel = true;
         Refresh();
         if(call) (* call)(this);
         return false;
       }
       break;
     case SDL_MOUSEBUTTONUP :
       if((e->button.button == 1) && sel)
       {
         sel = false;
         Refresh();
       }
       break;
   }
   return true;
}

// Na powierzchni graficznej rysuje przycisk
//------------------------------------------

void gfxButton::Refresh()
{
  Uint32 x, y, d, cu, cl, cf, ct;
  cf = C_FACE;    
  ct = enabled ? C_TEXT : C_DIS;
  if(sel)
  {
    d  = 1;
    cu = C_FACE;
    cl = C_UPPER;
  }
  else
  {
    d  = 0;
    cu = C_UPPER; // kolor górnej krawędzi przycisku
    cl = C_LOWER; // kolor dolnej krawędzi przycisku
  }
  
  x = rect.x + ((rect.w - gfxTextLength(font, text)) >> 1) + d;
  y = rect.y + ((rect.h - font->h) >> 1) + d;
  
  SDL_Rect r = ShrinkRect();
  
  if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen);

  SDL_FillRect(screen, &r, cf);
  gfxHLine(screen, r.x, rect.y, cu, r.w);
  gfxVLine(screen, rect.x, r.y, cu, r.h);
  gfxHLine(screen, r.x, rect.y + rect.h - 1, cl, r.w);
  gfxVLine(screen, rect.x + rect.w - 1, r.y, cl, r.h);
  gfxDrawText(screen, font, text, x, y, ct, -1);

  if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);

  Update();
}

Poniższy program testuje klasę gfxButton. Tworzy on siedem różnych przycisków i kojarzy z nimi funkcję rysującą prostokąty o różnych kolorach. Do katalogu projektowego przekopiuj czcionkę wektorową vecprop9x12.fnt. Program kończy się po naciśnięciu klawisza ESC.

// I Liceum Ogólnokształcące
// w Tarnowie
// Koło informatyczne
//
// P031 - przycisk akcji
//----------------------

#include <SDL/SDL_gfx.h>
#include <SDL/SDL_gui.h>
#include <time.h>

const int SCRX = 320;      // stałe określające szerokość i wysokość
const int SCRY = 240;      // ekranu w pikselach

SDL_Surface * screen;

// Funkcje obsługujące przyciski
//------------------------------

// Funkcja rysuje przypadkowy prostokąt o podanym kolorze
//-------------------------------------------------------

void DrawFigure(Uint32 color)
{
  SDL_Rect r;
  
  r.x = (screen->w >> 1) + rand() % (screen->w >> 2);
  r.y = rand() % (screen->h >> 1);
  r.w = 1 + rand() % (screen->w - r.x - 1);
  r.h = 1 + rand() % (screen->h - r.y - 1);

  if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen);

  SDL_FillRect(screen, &r, color);

  if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);

  SDL_UpdateRect(screen, r.x, r.y, r.w, r.h);
}

// Funkcja czyści obszar rysowania prostokatów
//--------------------------------------------

void fbclr()
{
 SDL_Rect r;
  
  r.x = screen->w >> 1;
  r.y = 0;
  r.w = screen->w >> 1;
  r.h = screen->h;

  if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen);

  SDL_FillRect(screen, &r, 0);

  if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);

  SDL_UpdateRect(screen, r.x, r.y, r.w, r.h);     
}

// Podstawowa funkcja dla wszystkich przycisków
//---------------------------------------------

void fn(gfxGUIObject * sender)
{
  switch(sender->tag)
  {
    case  0: fbclr(); break;
    case  1: DrawFigure(((rand() % 256) << 16) | ((rand() % 256) << 8) | (rand() % 256)); break;
    default: DrawFigure(sender->tag); break;
  }     
}

//***********************
// *** Program główny ***
//***********************

int main(int argc, char * argv[])
{

  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("Przyciski akcji",""); 
  
// inicjujemy generator liczb pseudolosowych

  srand((unsigned)time(NULL));
  
// odczytujemy z pliku definicje czcionki bitmapowej

  gfxFont * font = gfxOpenFont("vecprop9x12.fnt");

// Tworzymy siedem przycisków akcji

  char * texts[] = {"Czerwony", "Zielony", "Niebieski", "Biały", "Przypadkowy", "Wymaż", "Nieaktywny"};
  Uint32 tags[]  = {0xff0000, 0x00ff00, 0x0000ff, 0xffffff, 1, 0, 0};
  gfxButton * b[7];
  
// obliczamy maksymalną szerokość przycisków
  
  Sint32 maxw = 0;
  for(int i = 0; i < 7; i++)
  {
    Sint32 tmaxw = gfxTextLength(font, texts[i]);
    if(tmaxw > maxw) maxw = tmaxw;
  }
  
  SDL_Rect r;
  
  r.w = maxw + 16;
  r.h = font->h + 8;
  r.x = 8; r.y = 8;
  
  for(int i = 0; i < 7; i++)
  {
    b[i] = new gfxButton(tags[i], i < 6, screen, font, &r, texts[i], fn);
    r.y += 32;        
  }  
  
  SDL_Event event;
  
  bool running = true;
  
  while(running && SDL_WaitEvent(&event))
  {

// najpierw obsługujemy zdarzenia w przyciskach akcji

      for(int i = 0; i < 7; i++) b[i]->DoEvents(&event);

// a później reszta zdarzeń

      switch(event.type)
      {
        case SDL_KEYDOWN: if(event.key.keysym.sym != SDLK_ESCAPE) break;
        case SDL_QUIT: running = false; break;
      }
  }
  
// zamykamy czcionkę

  gfxCloseFont(font);

// zamykamy przyciski

  for(int i = 0; i < 7; i++) delete b[i];
  
  return 0;
}

obrazek

Zaprojektowany przez nas przycisk akcji jest bardzo prosty. Zastanów się, czy można rozszerzyć jego funkcję, tak aby reagował na wybraną kombinację klawiszy klawiatury.


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