Kirjautuminen

Haku

Tehtävät

Koodit: C++: Ikkunasysteemi

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;
}

Kommentit

nomic [03.08.2008 12:11:04]

#

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ä. :)

Pollapoju [04.08.2008 15:49:42]

#

Kätevää.

jimi-kimi [15.08.2008 18:45:00]

#

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!

Gaxx [15.08.2008 23:01:10]

#

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 :)

JussiR [26.03.2010 10:34:10]

#

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

Gaxx [01.04.2010 16:38:21]

#

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.

Kirjoita kommentti

Muista lukea kirjoitusohjeet.
Tietoa sivustosta