Pasek przewijania

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
OL046 - kontrolki opcji

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 GUI004.

 

Artykuł w PRZEBUDOWIE


SDL

Pasek przewijania (ang. scroll bar) jest standardową kontrolką, stosowaną do sterowania przewijaniem zawartości innych kontrolek. W naszym wykonaniu będzie to bardzo prosty element, reagujący jedynie na zdarzenia myszki - w przypadku klawiatury musielibyśmy bardzo rozbudować system obsługi zdarzeń, jeśli program korzystałby z kilku takich kontrolek.

Pasek przewijania będzie występował w dwóch odmianach - poziomej i pionowej. Typ paska rozpoznaje automatycznie jego konstruktor na podstawie wymiarów ograniczającego prostokąta. Jeśli pasek jest dłuższy w poziomie, to jest traktowany jako poziomy, a jeśli jest dłuższy w pionie, to będzie paskiem pionowym.

Pasek przewijania będzie zawierał następujące pola graficzne sterujące jego wartością:

  1. Kliknięcie myszką zmniejsza wartość paska o 1.
  2. Kliknięcie myszką przed suwakiem zmniejsza wartość paska o zadany krok. Kliknięcie za suwakiem zwiększa wartość paska o zadany krok.
  3. Przeciąganie myszką powoduje odpowiednie zmiany wartości paska.
  4. Kliknięcie myszką zwiększa wartość paska o 1.

Przy każdej zmianie wartości paska będzie wywoływana odpowiednia funkcja zewnętrzna, która może dokonywać zmian w innym obiekcie na podstawie tej wartości.

Utwórz projekt SDL. Dodaj do niego pliki biblioteki SDL_gfx,(nie zapomnij o ustawieniach linkera). Następnie dołącz pliki interfejsu GUI z podsumowania GUI004.

Na koniec pliku nagłówkowego SDL_gui.h dopisz poniższy fragment tekstu:

class gfxScrollBar : public gfxGUIObject
{
  private:

    Sint32 sblen, klen, kpos, mpos;

    void DrawBoxFace(SDL_Rect * r);
    void DrawButton(Uint32 t);
    void Calc();
    void Modify(bool mode, Sint32 x);

  public:

    Uint32 value, vmax, vprop, vstep;
  
    gfxScrollBar(Uint32 tg, bool en, SDL_Surface * s, SDL_Rect * r,
                 Uint32 v, Uint32 m, Uint32 p, Uint32 st, void (* fn)(gfxGUIObject *));
    bool DoEvents(SDL_Event * e);
    void Refresh();
};

gfxScrollBar

Klasa obsługuje element paska przewijania. Dziedziczy wszystkie pola i metody z gfxGUIObject. Pola i funkcje składowe klasy posiadają następujące znaczenie:

value  -  aktualna wartość paska przewijania. Wartość ta jest zawsze w przedziale od 0 do vmax.
vmax  - maksymalna wartość paska przewijania.
vprop  - jest to wartość określająca względną szerokość suwaka. Jeśli wynosi 0, to suwak posiada kształt kwadratu. Każda inna wartość powoduje, iż suwak przyjmuje względną długość równą szerokość suwni x vprop / vmax. Pozwala to dostosowywać szerokość suwaka do wielkości przesuwanego obszaru.
vstep  - określa krok o ile zostaje zwiększona lub zmniejszona wartość paska przewijania przy kliknięciu myszką na suwni.
gfxScrollBart()  - konstruktor
    gfxScrollBar(tg,en,s,r,v,m,p,st,fn)
        tg - numer paska przewijania
        en - określa, czy pasek jest aktywny - true, czy zablokowany - false
        s - wskaźnik struktury SDL_Surface
        r - prostokąt definiujący położenie położenie paska przewijania oraz jego wymiary.
        v - aktualna wartość paska
        m - maksymalna wartość paska
        p - wartość względnej szerokości suwaka
        st - krok suwaka
        fn - wskaźnik funkcji wywoływanej przy każdej zmianie wartości paska
DoEvents()  - obsługuje zdarzenia dla paska przewijania. Jeśli zdarzenie zostanie obsłużone, zwraca false.
    DoEvents(e)
        e - wskaźnik struktury SDL_Event.
Refresh()  - wyświetla pasek przewijania

Na końcu pliku SDL_gui.cpp dopisz poniższy fragment programu:

// *******************************
// *** Obsługa klasy ScrollBar ***
// *******************************

// Funkcja rysuje powierzchnię obramowaną cieniem
// r - prostokąt obejmujący powierzchnię
//-----------------------------------------------

void gfxScrollBar::DrawBoxFace(SDL_Rect * r)
{
  SDL_FillRect(screen, r, C_FACE);
  gfxHLine(screen, r->x, r->y, C_UPPER, r->w);
  gfxVLine(screen, r->x, r->y, C_UPPER, r->h);
  gfxHLine(screen, r->x + 1, r->y + r->h - 1, C_LOWER, r->w - 1);
  gfxVLine(screen, r->x + r->w - 1, r->y + 1, C_LOWER, r->h - 1);  
}

// Funkcja rysuje przycisk znajdujący się na końcach paska
// t - rodzaj przycisku:
//     0 - góra
//     1 - dół
//     2 - lewo
//     3 - prawo
//--------------------------------------------------------

void gfxScrollBar::DrawButton(Uint32 t)
{
  SDL_Rect r;
  
  switch(t)
  {
    case 0 : r.x = rect.x; r.y = rect.y; r.w = r.h = rect.w; break;
    case 1 : r.x = rect.x; r.y = rect.y + rect.h - rect.w; r.w = r.h = rect.w; break;
    case 2 : r.x = rect.x; r.y = rect.y; r.w = r.h = rect.h; break;
    case 3 : r.x = rect.x + rect.w - rect.h; r.y = rect.y; r.w = r.h = rect.h; break;
  };

  DrawBoxFace(&r);
  
  Uint32 x = r.x + (r.w >> 1);
  Uint32 y = r.y + (r.h >> 1);
  
  Uint32 c = enabled ? C_TEXT : C_DIS;
  
  switch(t)
  {
    case 0 : gfxMoveTo(r.x + 3, r.y + r.h - 4);
             gfxLineTo(screen, x, r.y + 3, c);
             gfxLineTo(screen, r.x + r.w - 4, r.y + r.h - 4, c);
             break;
    case 1 : gfxMoveTo(r.x + 3, r.y + 3);
             gfxLineTo(screen, x, r.y + r.h - 4, c);
             gfxLineTo(screen, r.x + r.w - 4, r.y + 3, c);
             break;
    case 2 : gfxMoveTo(r.x + r.w - 4, r.y + 3);
             gfxLineTo(screen, r.x + 3, y, c);
             gfxLineTo(screen, r.x + r.w - 4, r.y + r.h - 4, c);
             break;
    case 3 : gfxMoveTo(r.x + 3, r.y + 3);
             gfxLineTo(screen, r.x + r.w - 4, y, c);
             gfxLineTo(screen, r.x + 3, r.y + r.h - 4, c);
             break;
  };

}

// Wylicza wielkości obszarów paska przewijania
//---------------------------------------------

void gfxScrollBar::Calc()
{
  if(type)
  {
    sblen = rect.w - (rect.h << 1);
    klen  = rect.h;
  }
  else
  {
    klen  = rect.w;
    sblen = rect.h - (rect.w << 1);
  }

  if(vprop) klen = (sblen * vprop) / (vmax + 1);
  kpos = ((sblen - klen) * value) / vmax;
}

// Modyfikuje wartość paska
// mode : true  - wartość dv jest dodawana do value
//        false - wartość określa współrzędną myszki
//---------------------------------------------------

void gfxScrollBar::Modify(bool mode, Sint32 dv)
{
  if(mode)
  {
    value += dv;
    if(value < 0) value = 0;
    if(value > vmax) value = vmax;      
  }
  else
  {
     Sint32 d;
     if(type) d = rect.x + rect.h;
     else     d = rect.y + rect.w;
     kpos += dv - d - kpos - mpos;
     if(kpos < 0) kpos = 0;
     if(kpos > sblen - klen) kpos = sblen - klen;
     value = (kpos * vmax) / (sblen - klen);
     Calc();  // Korygujemy położenie suwaka!!!     
  }
  Refresh();
  if(call)(* call)(this);
}

// Konstruktor paska przewijania
//------------------------------

gfxScrollBar::gfxScrollBar(Uint32 tg, bool en, SDL_Surface * s, SDL_Rect * r,
                           Uint32 v, Uint32 m, Uint32 p, Uint32 st,
                           void (* fn)(gfxGUIObject *))
{
  tag     = tg;
  enabled = en;
  screen  = s;
  rect    = * r;
  value   = v;
  vmax    = m;
  vprop   = p;
  vstep   = st;
  call    = fn;
  type    = (rect.w > rect.h) ? 1 : 0;
  sel     = false;
  Refresh();
}

// Obsługuje zdarzenia w pasku
//----------------------------

bool gfxScrollBar::DoEvents(SDL_Event * e)
{
  if(enabled)
  {
    switch(e->type)
    {
      case SDL_MOUSEMOTION:
        if(sel)
        {
          if(type)
            Modify(false, e->motion.x);
          else
            Modify(false, e->motion.y);
          return false;
        }
        else break;
      case SDL_MOUSEBUTTONDOWN:
        if((e->button.button == SDL_BUTTON_LEFT) && MouseHit(e->button.x, e->button.y))
        {
          if(type)
          {
             if(e->button.x < rect.x + rect.h)
             {
               Modify(true, -1); return false;
             }
             if(e->button.x > rect.x + rect.w - rect.h)
             {
               Modify(true, 1); return false;
             }
             if(e->button.x < rect.x + rect.h + kpos)
             {
               Modify(true, -vstep); return false;
             }
             if(e->button.x > rect.x + rect.h + kpos + klen)
             {
               Modify(true, vstep); return false;
             }
             sel = true;
             mpos = e->button.x - rect.x - rect.h - kpos;
             return false;                           
          }
          else
          {
             if(e->button.y < rect.y + rect.w)
             {
               Modify(true, -1); return false;
             }
             if(e->button.y > rect.y + rect.h - rect.w)
             {
               Modify(true, 1); return false;
             }
             if(e->button.y < rect.y + rect.w + kpos)
             {
               Modify(true, -vstep); return false;
             }
             if(e->button.y > rect.y + rect.w + kpos + klen)
             {
               Modify(true, vstep); return false;
             }
             sel = true;
             mpos = e->button.y - rect.y - rect.w - kpos;
             return false;                           
          }
        }
        else break;
      case SDL_MOUSEBUTTONUP:
        if(sel && (e->button.button == SDL_BUTTON_LEFT))
        {
          sel = false; return false;
        }
        else break;
    }
  }
  return true;
}

// Rysuje pasek przewijania
//-------------------------

void gfxScrollBar::Refresh()
{
  SDL_Rect r;

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

  Calc();
  
  if(type)
  {
    DrawButton(2); DrawButton(3);
    r.x = rect.x + rect.h;
    r.y = rect.y;
    r.w = sblen;
    r.h = rect.h;
  }
  else
  {
    DrawButton(0); DrawButton(1);
    r.x = rect.x;
    r.y = rect.y + rect.w;
    r.w = rect.w;
    r.h = sblen;
  }

  SDL_FillRect(screen, &r, enabled ? C_TEXTBG : C_DIS);
    
  if(type)
  {
    r.x += kpos; r.w = klen;
  }
  else
  {
    r.y += kpos; r.h = klen;
  }
  
  DrawBoxFace(&r);
  
  if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);

  Update();
}

Poniższy program testuje kontrolki paków przewijania. Program kończymy klikając na przycisk zamknięcia aplikacji.

// I Liceum Ogólnokształcące
// w Tarnowie
// Koło informatyczne
//
// P040 - Pasek przewijania
//-------------------------

#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

const int SBSIZE = 15;     // grubość paska przewijania

// ************************
// *** Zmienne globalne ***
// ************************

SDL_Surface  * screen;
gfxFont      * font = gfxOpenFont("vecprop9x12.fnt");
gfxScrollBar * sb1, * sb2;

// ****************
// *** Funkcje ****
// ****************

// Funkcja zamienia liczbę na 3 cyfrowy tekst
//-------------------------------------------

void NumberToASCII(Sint32 n, char * t)
{
  Sint32 x = 100, i;
    
  for(i = 0; i < 3; i++)
  {
    t[i] = 48 + (n / x);
    n %= x;
    x /= 10;
  }
  t[i] = 0;
}

// Funkcja suwaka poziomego
//-------------------------

void sb1f(gfxGUIObject * sender)
{
  char t[4];

  NumberToASCII(((gfxScrollBar *)sender)->value, t);

  Uint32 l = gfxTextLength(font,t);
  Uint32 x = sender->rect.x + (sender->rect.w >> 1) - (l >> 1);
  Uint32 y = sender->rect.y - font->h - 10;

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

  gfxDrawText(screen, font, t, x , y, 0xff0000, 0);

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

  SDL_UpdateRect(screen, x, y, l, font->h);
}

// Funkcja suwaka pionowego
//-------------------------

void sb2f(gfxGUIObject * sender)
{
  char t[4];

  NumberToASCII(((gfxScrollBar *)sender)->value, t);

  Uint32 l = gfxTextLength(font,t);
  Uint32 x = sender->rect.x - l - 10;
  Uint32 y = sender->rect.y + (sender->rect.h >> 1) - (font->h >> 1) ;

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

  gfxDrawText(screen, font, t, x , y, 0xff0000, 0);

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

  SDL_UpdateRect(screen, x, y, l, font->h);     
}

//***********************
// *** 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("Test paska przewijania", ""); 
    
// Tworzymy dwa paski przwijania, jeden po prawej stronie okna, a drugi na dole

  SDL_Rect r;
  
  r.x = 0; r.y = screen->h - SBSIZE;
  r.w = screen->w - SBSIZE; r.h = SBSIZE;
  sb1 = new gfxScrollBar(0, true, screen, &r, 200, 200, 25, 10, sb1f);
  r.x = screen->w - SBSIZE; r.y = 0;
  r.w = SBSIZE; r.h = screen->h - SBSIZE;
  sb2 = new gfxScrollBar(0, true, screen, &r, 0, 100, 25, 10, sb2f);
  sb1f(sb1);
  sb2f(sb2);

// Obsługujemy zdarzenia

  SDL_Event event;
  bool running = true;
  
  while(running && SDL_WaitEvent(&event))
  {
    if(sb1->DoEvents(&event) && sb2->DoEvents(&event))
    switch(event.type)
    {
      case SDL_QUIT: running = false;
                     break;
    }
  }
  
// zamykamy czcionkę

  gfxCloseFont(font);

  return 0;
}


Podsumowanie



List do administratora Serwisu Edukacyjnego Nauczycieli I LO

Twój email: (jeśli chcesz otrzymać odpowiedź)
Temat:
Uwaga: ← tutaj wpisz wyraz  ilo , inaczej list zostanie zignorowany

Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).

Liczba znaków do wykorzystania: 2048

 

W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień szeroko opisywanych w podręcznikach.



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

©2017 mgr Jerzy Wałaszek

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