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;
};
#endifwinsys.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.