Formás ablakok |
A
multimédia világát éljük. Furcsa alakú, díszes ablakok, szép vonalú grafikák,
animációk teszik látványossá a programjainkat. Néha elengedhetetlen, hogy
megszabaduljunk a kötött formáktól, hogy valami újat hozzunk be a piacra,
hisz talán ettől (és pont ettől) lesz sikeres a programunk. Emiatt
talán kénytelenek vagyunk más, szokatlan formákhoz nyúlni, mint például,
kitörünk a Windows által nyújtott standard téglalap alakú ablakokból, és
merészen kör alakú ablakra épített felhasználói kezelőfelületet
alakítunk ki, vagy színes képekkel gazdagítjuk programjainkat. Ez
a cikk ebbe a világba fogja elkalauzolni önöket, bemutatja hogyan lehetséges
szakítani a megszokott téglalap alakú világgal, és
miként lehet létrehozni egy teljesen szabad formájú ablakot, mindenféle
megszorításoktól mentesen. A programozási nyelv amit
használni fogunk az a Delphi, mivel tapasztalataim alapján ebben lehet a
legjobb idő/energiabefektetéssel a legjobb minőségű
multimédiás programot előállítani. |
Régiók. |
A
Windows-beli régiókat, a Win32SDK alapos és hosszas
böngészése után a következőképpen lehet meghatározni: egy régió nem más,
mint egy téglalap, egy ellipszis, egy általános sokszög, vagy ezeknek
bizonyos fokú kombinációja, amelyet meg lehet tölteni egy színnel, be lehet
keretezni, bele lehet rajzolni, valamint még sok más tulajdonsággal bír,
amelyeket helyszűke miatt nem sorolok fel. Amint
kitűnik a Win32SDK-ból, négyfajta régiót különböztetünk meg, és
mindegyiknek megvan a saját függvénye, amivel létre lehet őt hozni. ˇ
Ha egy téglalap
alakú régiót akarunk létrehozni, akkor használjuk a következő függvények
valamelyikét: HRGN CreateRectRgn(int Left, int Top, int Right, int Bottom); HRGN CreateRectRgnIndirect(CONST RECT *lpRect); BOOL SetRectRgn(HRGN rgn, int Left, int Top, int Right, int Bottom); A
HRGN típus, amely egyes
függvények visszatérési értéke, nem más, mint a régió handle
változója. Én feltételezem, hogy nem kell elmagyarázni, mi is az a handle, hisz ez a cikk elméletileg nem annak készült, aki
most írja meg az első Windows programját. Az
első függvénnyel létrehozunk egy régiót, amelynek koordinátáit mi adjuk
meg, először a bal felső sarkát, aztán meg a jobb alsót. Az MSDN
figyelmeztet, hogy a legalsó sor illetve a legjobboldalibb oszlop nem fognak
szerepelni az újonnan létrehozott régióban. A második függvény is valami
hasonló célt szolgál, csak a paraméter az egy téglalap koordinátáit fogja
tartalmazni. A
SetRectRgn másképp működik, mert egy létező
régiót átformáz úgy, hogy a paraméterként megadott értékeket fogja
tartalmazni, és természetesen téglalap alakja van. ˇ
A következő
régiótípus, amely nagyon hasonló a téglalap alakúakhoz, az a lekerekített
sarkú téglalap alakú régió. Ilyesmit csak egy függvény kezel: HRGN CreateRoundRectRgn(int Left, int Top, int Height, int Bottom, int WidthEllipse, int HeightEllipse ); Gondolom,
sokat itt sem kell magyaráznom a paraméterek mibenlétét, a megadott téglalap
alakú zónába olyan lekerekített sarkú téglalapot szerkeszt be, amelynek a
sarkaiba berakja azt az ellipszist, amelynek hosszúságát illetve szélességét
az utolsó két paraméter adja meg. ˇ
Ha ellipszis alakú
régiókat szerkesztünk, akkor a következő függvényeket használjuk: HRGN CreateEllipticRgn(int Left, int Top, int Right, int Bottom); HRGN CreateEllipticRgnIndirect(CONST RECT* lpRect); Ezek
úgy működnek, hogy a téglalapba, amelyet megadtunk paraméterként,
berajzol egy ellipszist, melynek tengelyei párhuzamosak a téglalap
tengelyeivel. ˇ
Végül a sokszög
alakú régiók a következő függvényekkel kezelhetők: HRGN CreatePolygonRgn(CONST POINT* lpPoints, int nrPoints, int PolyFillMode); HRGN CreatePolyPolygonRgn(CONST POINT* lpPoints, CONST INT* lpPolyCounts, int nCount, int PolyFillMode); Az
első függvény az lpPoints tömbben található
pontokból felhasznál nrPoints számút, és
létrehozza a kívánt régiót. A PolyFillMode változót a következőképpen értelmezi: ha
PolyFillMode ALTERNATE, akkor csak minden
páratlan oldal után található zónát tekinti a régió belsejének, ha meg WINDING,
akkor mindent berak a régióba. A
következő rajz szemlélteti a két mód közti különbséget:
A
másik függvény viszont már bonyolultabb, mint az összes eddigi egy helyen.
Elsősorban ez több poligont fest ki, nem csak
egyet. Az első paraméter egy vektor, melyben pontokat tartunk. Minden
pont egyszer kell megadni és alapértelmezett módon, egymás után veszi
őket a függvény. Az azt is jelenti, hogy ha szükségünk van arra, hogy
egy pont több poligonnak a csúcsa legyen, akkor
többször kell azt definiálnunk. A második paraméter megadja az egyes poligonokban található pontok számát. És végül a harmadik
paraméter, az nCount megadja, hogy hány
eleme van az lpPolyCounts tömbnek. A PolyFillMode paramétert nem részletezem, lásd fentebb. |
A régiók egyesítése |
Régiókat
lehet kombinálni a int CombineRgn(HRGN hrgnDest, HRGN hrgnSrc1, HRGN hrgnSrc2, int fnCombineMode); függvénnyel.
Ezzel a függvénnyel egyesíthetjük a hrgnSrc1 illetve hrgnSrc2
régiókat a hrgnDest régióba, amelynek
léteznie kell, a fnCombineMode pedig a
következő értékeket veheti fel: RGN_AND - amikor két régiót
metszünk RGN_COPY - amikor csak az
első régiót adja vissza RGN_DIFF - amikor azokat a
részeket veszi, amelyek benne vannak az elsőben, de nincsenek benne a másodikban RGN_OR - amikor egyesíti a két
régiót. RGN_XOR - amikor egyesíti a két
régiót, és kiszedi az így keletkezett alakzatból a kettő metszetét. Magyarázatképpen
egy rajz: (Nézzünk
meg egy érdekes függvényt, mielőtt még továbblépnénk a következő
gondolatra. A PtInRegion függvény
megmondja, hogy a paraméterként átadott pont benne van-e a szintén
paraméterként átadott régióban.) |
Kapcsolat az ablakok és a régiók közt |
int GetWindowRgn( HWND hWnd, HRGN hRgn ); függvénnyel.
A hWnd paraméter az annak az ablaknak a handle-je, amelynek a régióját le akarjuk kérdezni, és a hRgn változóban tárolni. Figyelem! A régió az
ablak bal felső sarkába kezdődik, és nem a ClientArea bal felső sarkába. Egyértelmű,
hogy ha van olyan függvény, amivel le lehet kérdezni egy ablak régióját,
akkor olyannak is kell létezni, amivel be tudjuk állítani, az
pedig a következő: int SetWindowRgn( HWND hWnd, HRGN hRgn, BOOL redraw ); Gondolom,
sok magyarázatra nincs szükség, az egyedüli dolog, amire kell figyelni, hogy
ne töröljük a hRgn változót, mert a
rendszer nem őriz másolatot a régiókról. És valójában ezzel elértünk a
célunkhoz, mert ezt a függvényt kell használnunk, hogy ilyen meg olyan alakú
ablakokat létre tudjunk hozni. Amit
tenni kell, nem más, mint létrehozni egy új projektet, és belerakni a
következő kódot: procedure TForm1.FormCreate(Sender: TObject); var regCircleSmall, regCircleBig:HRGN; regFinal:HRGN; begin regCircleBig := CreateEllipticRgn(10,10,200,200); regCircleSmall := CreateEllipticRgn(100,100,200,200); regFinal := CreateRectRgn(0,0,400,300); if( CombineRgn(regFinal, regCircleBig, regCircleSmall, RGN_DIFF) = ERROR) then begin ShowMessage('Error.'); end; SetWindowRgn(Handle,regFinal,false);
end; És
ez a pár sor létrehoz egy érdekes félhold alakú user
interface-t, ahova be lehet rakni a saját
kezelőszerveket, képeket, mindenféléket. Csak egy a gond. Ezáltal
eltűnik az ablaknak a címsora, és nem tudjuk többet mozgatni. Ennek a
kiküszöbölésére írtam a következő kódot: procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if button in [mbLeft] then begin mdown:=true; mdx:=x; mdy:=y; end; end;
procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin if not mdown then exit; form1.Left:=form1.left-mdx+x; form1.top:=form1.top-mdy+y; end;
procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if button in [mbLeft] then mdown:=false; end; amely
lehetővé teszi, az ablak mozgatását, persze ne feledjék el definiálni az
mdown, mdx,
illetve mdy osztályváltozókat, mert ezek nélkül nem
lehet a kódot működésre bírni. Viszlát a következő számban. |
Deák Ferenc |