Kirjoittaja: Gaxx
Kirjoitettu: 02.08.2008 – 02.08.2008
Tagit: koodi näytille, vinkki
Tässä teille hyvin yksinkertainen ja käyttäjäystävällinen, mutta kätevä ja käyttökelpoinen ikkunoiden hallintajärjestelmä. Lisäksi se on täysin järjestelmäriippumaton ja SDL:ää on käytetty vain esimerkissä. Ikkunoista voi tehdä melkeimpä millaisia vain, sillä järjestelmä ei käytännössä rajoita niitä millään tavalla. Järjestelmä huolehtii ikkunoiden valinnoista, siirtelyistä, sulkemisista sekä käsittely- ja piirtofunktioiden suorittamisesta.
Käytännössä homma etenee siten, että suunnitellaan oma ikkunaluokka, joka periytytetään Window-luokasta. Tämä asettaa tietenkin vaatimukseksi toteuttaa luokalle julkiset draw() ja doEvents() -metodit, mutta siihen ne vaatimukset sitten jäävätkin - muuten luokka voi olla juuri sellainen, kuin itse haluat!
Itse käyttö ei ole sen monimutkaisempaa. Luodaan ikkunat dynaamisesti
Window* ikkuna = new OmaIkkuna;
ja registeröidään ikkuna ikkunasysteemiin
Windows winsys; winsys.pushWindow(ikkuna);
Ikkunoiden käsittely hoituu ohjelmassa yhdellä ainoalla funktiolla, jolle annetaan parametreiksi hiiren koordinaatit ja nappien tila. Huomioi, että mouseButton:n arvolla nolla(0) tulkitaan siten, että mitään hiiren nappia ei paineta.
winsys.doEvents(mouseX, mouseY, mouseButton);
Lopuksi, muistakaa vapauttaa dynaamisesti luodut ikkunat :)
Ensimmäiset kaksi listausta ovat itse systeemi ja jälkimmäiset kaksi esimerkkiä.
Joku kumminkin pyytää binääriä niin tässä on binäärit ja sourset dev-cpp:n projektitiedostolla varustettuna, kaikki samassa paketissa
winsys.h
#ifndef WINDOWS_H #define WINDOWS_H #include <vector> // Ikkunan käsittelystä tullut viesti enum EventMsg {NONE, MOVE, CLOSE}; // Vakiomäärittely päälimmäisen ikkunan indeksille const int TOP_WINDOW = 0; // Systeemin ikkunoiden isäntäluokka, josta // kaikki ikkunaluokat tulee periyttää class Window { public: // Periytyvien luokkien tulee sisältää käsittelyfunktio // Parametreina annetaan hiiren sijainti ja nappien tila virtual EventMsg doEvents(int x, int y, int button) = 0; // Periytyvien luokkien tulee sisältää piirtofunktio virtual void draw() = 0; // Käytetään liikuttamaan ikkunaa halutun verran // Parametreina annetaan x:n ja y:n muutos void move(int dx, int dy); // Käytetään tarkastamaan, onko hiiri ikkunan päällä // Parametreina annetaan hiiren koordinaatit bool mouseOn(const int x, const int y) const; // Nämä funktiot palauttavat ikkunan vasemman yläkulman // sijainnin näytöllä sekä ikkunan leveyden ja korkeuden int getX() const; int getY() const; int getW() const; int getH() const; protected: // Ikkunan sijainti näytöllä ja sen koko int x; int y; int w; int h; }; // Ikkunasysteemi class Windows { public: // Rakentaja Windows(); // Käytetään lisäämään ikkuna systeemiin void pushWindow(Window* window); // Näillä funktioilla voi poistaa ikkunan systeemistä // ikkunan osoitteen tai sen indeksin perusteella bool popWindow(const unsigned int window); bool popWindow(const Window* window); // Tyhjentää systeemin ikkunoista void clear(); // Hoitaa kaikki systeemin toiminnallisuudet // Parametreina annetaan hiiren koordinaatit ja nappien tila void doEvents(int x, int y, int button); // Palauttaa halutun ikkunan osoitteen // Parametrina annetaan ikkunan indeksi systeemissä Window* getWindow(const unsigned int window) const; private: // Vektori, joka säilöö systeemin ikkunat std::vector<Window*> windows; // Vanhat hiiren koordinaatit - käytetään ikkunoiden liikuttelussa int oldX; int oldY; }; #endif
winsys.cpp
#include "winsys.h" // Jäsenfuktio: move // Parametrit: x:n ja y:n muutos // Toiminta: liikuttaa ikkuna annettujen arvojen mukaan void Window::move(int dx, int dy) { // Lisätään muutokset ikkunan koordinaatteihin Window::x += dx; Window::y += dy; } // Jäsenfunktio mouseOn // Parametrit: hiiren x- ja y-koordinaatit // Toiminta: tarkastaa, onko hiiri ikkunan alueella // Paluuarvo: totuusarvo, onko hiiri ikkunan alueella bool Window::mouseOn(const int x, const int y) const { // Jos hiiri on ikkunan päällä if(x > Window::x && x < Window::x + Window::w && y > Window::y && y < Window::y + Window::h) { return true; } return false; } // Jäsenfunktio: getX // Paluuarvo: palauttaa ikkunan x-koordinaatin int Window::getX() const { return Window::x; } // Jäsenfunktio: getY // Paluuarvo: palauttaa ikkunan y-koordinaatin int Window::getY() const { return Window::y; } // Jäsenfunktio: getW // Paluuarvo: palauttaa ikkunan leveyden int Window::getW() const { return Window::w; } // Jäsenfunktio: getH // Paluuarvo: palauttaa ikkunan korkeuden int Window::getH() const { return Window::h; } // Rakentaja // Toiminta: alustaa hiiren vanhat koordinaatit Windows::Windows() { oldX = oldY = -1; } // Jäsenfunktio pushWindow // Parametrit: lisättävän ikkunan osoite // Toiminta: lisää annetun ikkunan ikkunasysteemiin void Windows::pushWindow(Window* window) { // Lisätään ikkuna Windows::windows.push_back(window); } // Jäsenfunktio: popWindow // Parametrit: poistettavan ikkunan indeksi systeemissä // Toiminta: poistaa annettua indeksiä vastaavan ikkunan // Paluuarvo: totuusarvo, onnistuiko ikkunan poistaminen bool Windows::popWindow(const unsigned int window) { // Jos arvo on laillinen if(window < Windows::windows.size()) { // Poistetaan haluttu ikkuna Windows::windows.erase(Windows::windows.begin()+window); return true; } else { return false; } } // Jäsenfunktio: popWindow // Parametrit: poistettavan ikkunan osoite // Toiminta: poistaa parametrina annetun ikkunan // Paluuarvo: totuusarvo, onnistuiko ikkunan poistaminen bool Windows::popWindow(const Window* window) { // Etsitään annettu ikkuna for(unsigned int i = 0; i < Windows::windows.size(); ++i) { if(Windows::windows.at(i) == window) { Windows::windows.erase(Windows::windows.begin()+i); return true; } } // Ikkunaa ei löytynyt return false; } // Jäsenfunktio: clear // Toiminta: poistaa kaikki ikkunat systeemistä void Windows::clear() { // Poistetaan kaikki ikkunat systeemistä Windows::windows.clear(); } // Jäsenfunktio: doEvents // Parametrit: hiiren koordinaatit ja hiiren nappien tila // Toiminta: huolehtii ikkunoiden valitsemisesta, siirtämisestä ja // sulkemisesta sekä suorittaa ikkunoiden piirto ja käsittelyfunktiot void Windows::doEvents(int x, int y, int button) { static EventMsg msg = NONE; // Kohdeikkunan palauttama viesti static int target = Windows::windows.size() - 1; // Kohdeikkunan indeksi // Jos systeemissä ei ole ikkunoita, poistutaan if(Windows::windows.empty()) { return; } // Jos mitään ikkunaa ei siirretä if(msg != MOVE) { // Selvitetään, mikä ikkuna on kohteena. // Vektorin päälimmäisin tarkastetaan luonnollisesti viimeisenä. for(unsigned int i = 0; i < Windows::windows.size(); ++i) { // Jos hiiri on ikkunan alueella if(Windows::windows.at(i)->mouseOn(x, y)) { // Merkataan ikkuna kohdeikkunaksi target = i; } } // Jos hiirtä painetaan if(button) { // Siirretään lopullinen kohdeikkuna päälimmäiseksi Windows::windows.push_back(Windows::windows.at(target)); Windows::windows.erase(Windows::windows.begin() + target); // Kohde on tietenkin hiiren alla päälimmäisenä oleva ikkuna target = Windows::windows.size() - 1; } } // Suoritetaan päälimmäisen ikkunan tapahtumat if(msg != MOVE || (msg == MOVE && !button)) { msg = Windows::windows.at(target)-> doEvents(x - Windows::windows.at(target)->getX(), y - Windows::windows.at(target)->getY(), button); } // Reagoidaan ikkunan viestiin switch(msg) { case MOVE: { // Jos vanhat koordinaatit ikkunalla if(Windows::windows.at(target)-> mouseOn(Windows::oldX, Windows::oldY)) { // Liikutetaan ikkunaa Windows::windows.at(target)->move( x - Windows::oldX, y - Windows::oldY); } // Otetaan uudet koordinaatit talteen Windows::oldX = x; Windows::oldY = y; break; } case CLOSE: { // Jos hiiri on kohdeikkunan päällä if(Windows::windows.at(target)->mouseOn(x, y)) { // Poistetaan ikkuna systeemistä popWindow(target); // Valitaan kohteeksi seuraavaksi päälimmäinen ikkuna target = Windows::windows.size() - 1; } break; } default: { // Merkataan vanhat koordinaatit käyttämättömiksi Windows::oldX = Windows::oldY = -1; } } // Piirretään kaikki ikkunat for(unsigned int i = 0; i < Windows::windows.size(); ++i) { Windows::windows.at(i)->draw(); } } // Jäsenfunktio: getWindow // Parametrit: ikkunan indeksi systeemissä // Paluuarvo: palauttaa halutun ikkunan osoitteen Window* Windows::getWindow(const unsigned int window) const { // Jos ikkunan indeksi on laillinen if(window < Windows::windows.size()) { // Palautetaan ikkunan osoite return Windows::windows.at(window); } // Ikkunaa ei ole return NULL; }
mywin.h
#ifndef MYWIN_H #define MYWIN_H #include <SDL/SDL.h> #include "winsys.h" class MyWin : public Window { public: // Oma konstruktori MyWin(int x, int y, SDL_Surface* bg); // Ikkunan käsittelyfunktio // Suoritetaan sillon, kun ikkunaa kohdistetaan hiirellä EventMsg doEvents(int x, int y, int button); // Ikkunan piirtofunktio // Suoritetaan jokaisella doEvents -kutsulla void draw(); private: SDL_Surface* bg; // Taustakuvan osoite }; #endif *** -------------------------------------------------------- *** mywin.cpp #include "mywin.h" // Rakentaja MyWin::MyWin(int x, int y, SDL_Surface* bg) { // Sijoitetaan annetut alkuarvot MyWin::x = x; MyWin::y = y; MyWin::w = bg->w; MyWin::h = bg->h; MyWin::bg = bg; } // Ikkunan toiminnallisuus EventMsg MyWin::doEvents(int x, int y, int button) { // Jos ikkunaa klikataan ruksin kohdalla if(x > MyWin::w - 15 && y < 15 && button) { // Käsketään sulkea ikkuna return CLOSE; } // Jos hiirtä painetaan siirtopalkin kohdalla if(x < MyWin::w - 15 && y < 15 && button) { // Käsketään liikuttaa ikkunaa hiiren mukaan return MOVE; } return NONE; } // Ikkunan piirtäminen void MyWin::draw() { // Piirretään taustakuva SDL_Rect pos = {MyWin::x, MyWin::y}; SDL_BlitSurface(MyWin::bg, NULL, SDL_GetVideoSurface(), &pos); }
main.cpp
#include <iostream> #include <cstdlib> #include "winsys.h" #include "mywin.h" int main(int argc, char** argv) { // Alustetaan tarvittavat SDL if(SDL_Init(SDL_INIT_VIDEO)) { std::cerr << "SDL:n alustaminen epäonnistui" << std::endl; return EXIT_FAILURE; } // Luodaan näyttöpinta if(SDL_SetVideoMode(800, 600, 32, SDL_DOUBLEBUF) == NULL) { std::cerr << "Ikkunan luonti epäonnistui" << std::endl; return EXIT_FAILURE; } // Ladataan ikkunan taustakuva SDL_Surface* bg = SDL_LoadBMP("pic.bmp"); // Hiiren sijainti ja nappien tila int mx, my, mb; // Itse ikkunasysteemi Windows winsys; // Luodaan kaksi omaa ikkunaa Window* win1 = new MyWin(50, 50, bg); Window* win2 = new MyWin(400, 400, bg); // Lisätään ikkunat systeemiin winsys.pushWindow(win1); winsys.pushWindow(win2); // Kunnes ohjelma lopetetaan while(true) { // Haetaan syötteet SDL_PumpEvents(); mb = SDL_GetMouseState(&mx, &my); // Ohjelman sulkeminen if(mb == SDL_BUTTON(2)) { break; } // Pyyhitään näyttöpinta SDL_FillRect(SDL_GetVideoSurface(), NULL, 0); // Hoidetaan ikkunoiden käsittely ja piirto winsys.doEvents(mx, my, mb); // Päivitetään näyttöpinta SDL_Flip(SDL_GetVideoSurface()); } // Vapautetaan ikkunat winsys.clear(); delete win1; delete win2; SDL_Quit(); return EXIT_SUCCESS; }
Oikein mainio. Itsekin tässä mietin omaa ikkunasysteemiä peliäni varten. Pitää vielä tarkastella lähemmin koodia, mutta näin amatöörinä näyttää hyvältä. :)
Kätevää.
Ihan mielenkiinnosta kyselen, että kuinka kauan väkersit kyseistä vinkkiä?
Vinkin selkeyden, helppokäyttöisyyden ja omiin tarkoituksiin soveltuvaisuuden takia taidan vaihtaa oman väkerrykseni tähän ( verkossa toimiva laivanupotus .)
Kiitoksia mainiosta vinkistä. Enemmän mennyt aikaa tuon CCNA sertifikaatin kanssa viimeaikoina, joten en ole nyt aivan kunnolla ajan kanssa päässyt perehtymään soodaan. Toivottavasti kuitenkin pian!
jimi-kimi kirjoitti:
Ihan mielenkiinnosta kyselen, että kuinka kauan väkersit kyseistä vinkkiä?
Tuohon kysymykseen on äärimmäisen vaikea vastata. Tein joskus aikoinaan ikkunasysteemin c:llä, ja nyt sitten päätin tehdän uuden ja paremman systeemin käyttäen hyödyksi c++:n luokkia. Itse koodin kirjoittamiseen ei montaa tuntia vierähtänyt, mutta kyllähän sitä piti ensin vähän suunnitella päässä ja c-version idea toimi pohjana.
Mukava kuulla, että vinkistä on hyötyä jolle kulle :)
Hö en osaa compilee:
Compiler: Default compiler Executing g++.exe... g++.exe "K:\Data\main.cpp" -o "K:\Data\main.exe" -I"c:\user\rantaju1\Desktop\Dev-Cpp\lib\gcc\mingw32\3.4.2\include" -I"c:\user\rantaju1\Desktop\Dev-Cpp\include\c++\3.4.2\backward" -I"c:\user\rantaju1\Desktop\Dev-Cpp\include\c++\3.4.2\mingw32" -I"c:\user\rantaju1\Desktop\Dev-Cpp\include\c++\3.4.2" -I"c:\user\rantaju1\Desktop\Dev-Cpp\include" -L"c:\user\rantaju1\Desktop\Dev-Cpp\lib" C:\DOCUME~1\rantaju1\LOCALS~1\Temp/ccqOaaaa.o(.text+0x14a):main.cpp: undefined reference to `SDL_Init' C:\DOCUME~1\rantaju1\LOCALS~1\Temp/ccqOaaaa.o(.text+0x1ac):main.cpp: undefined reference to `SDL_SetVideoMode' C:\DOCUME~1\rantaju1\LOCALS~1\Temp/ccqOaaaa.o(.text+0x1fe):main.cpp: undefined reference to `SDL_RWFromFile' C:\DOCUME~1\rantaju1\LOCALS~1\Temp/ccqOaaaa.o(.text+0x20e):main.cpp: undefined reference to `SDL_LoadBMP_RW' C:\DOCUME~1\rantaju1\LOCALS~1\Temp/ccqOaaaa.o(.text+0x21c):main.cpp: undefined reference to `Windows::Windows()' C:\DOCUME~1\rantaju1\LOCALS~1\Temp/ccqOaaaa.o(.text+0x261):main.cpp: undefined reference to `MyWin::MyWin(int, int, SDL_Surface*)' C:\DOCUME~1\rantaju1\LOCALS~1\Temp/ccqOaaaa.o(.text+0x2dc):main.cpp: undefined reference to `MyWin::MyWin(int, int, SDL_Surface*)' C:\DOCUME~1\rantaju1\LOCALS~1\Temp/ccqOaaaa.o(.text+0x35a):main.cpp: undefined reference to `Windows::pushWindow(Window*)' C:\DOCUME~1\rantaju1\LOCALS~1\Temp/ccqOaaaa.o(.text+0x36c):main.cpp: undefined reference to `Windows::pushWindow(Window*)' C:\DOCUME~1\rantaju1\LOCALS~1\Temp/ccqOaaaa.o(.text+0x378):main.cpp: undefined reference to `SDL_PumpEvents' C:\DOCUME~1\rantaju1\LOCALS~1\Temp/ccqOaaaa.o(.text+0x38a):main.cpp: undefined reference to `SDL_GetMouseState' C:\DOCUME~1\rantaju1\LOCALS~1\Temp/ccqOaaaa.o(.text+0x3a4):main.cpp: undefined reference to `SDL_GetVideoSurface' C:\DOCUME~1\rantaju1\LOCALS~1\Temp/ccqOaaaa.o(.text+0x3bc):main.cpp: undefined reference to `SDL_FillRect' C:\DOCUME~1\rantaju1\LOCALS~1\Temp/ccqOaaaa.o(.text+0x3dc):main.cpp: undefined reference to `Windows::doEvents(int, int, int)' C:\DOCUME~1\rantaju1\LOCALS~1\Temp/ccqOaaaa.o(.text+0x3e1):main.cpp: undefined reference to `SDL_GetVideoSurface' C:\DOCUME~1\rantaju1\LOCALS~1\Temp/ccqOaaaa.o(.text+0x3e9):main.cpp: undefined reference to `SDL_Flip' C:\DOCUME~1\rantaju1\LOCALS~1\Temp/ccqOaaaa.o(.text+0x3fd):main.cpp: undefined reference to `Windows::clear()' C:\DOCUME~1\rantaju1\LOCALS~1\Temp/ccqOaaaa.o(.text+0x418):main.cpp: undefined reference to `SDL_Quit' c:\user\rantaju1\Desktop\Dev-Cpp\lib/libmingw32.a(main.o)(.text+0x106):main.c: undefined reference to `WinMain@16' collect2: ld returned 1 exit status Execution terminated
JussiR: Esimerkkiohjelma vaatii toimiakseen SDL:n. Lisäksi täytyy kääntää ja linkittää myös mywin.cpp ja winsys.cpp.
Kyseessä on siis jokapäiväinen toimenpide, joka "c++ -ohjelmoijan" täytyy hallita. Putkan oppaista saattaa olla apua.