Jó lenne, ha lenne valami olyan segítség, ami megkönnyíti számunkra a felhasználói felület megírását. Ilyen lehet pl: A Pascalhoz adott TVision (OOP rovatunk foglalkozik vele) de az sajnos szöveges felület ami mostanság nem annyira korszerű (noha mint azt az OOP későbbi számainkban látni fogjuk, lehet rajta szépíteni). Itt vannak még a Windows különböző verziói melyek a sokéves fejesztésnek köszönhetően mára igen sok lehetőséget bocsájtanak a programozók rendelkezésére is. Néha azonban mégis megesik, hogy a Windows sem igazán járható megoldás, mert nekünk egészen egyedi igényeink vannak. Ilyenkor kerülünk olyan helyzetbe, hogy saját felhasználói felületet kell írnunk. Az elkövetkezendőkben ezzel a problémával szeretnék foglalkozni.
Ha kicsit zavaros az elkövetkezendő néhány bekezdés, akkor bocsánat
érte, később igyekszem mindent megmagyarázni.
A képernyőelemek adatait láncba rendezve érdemes tárolni mert így egy esemény beérkezésekor könnyedén el lehet dönteni, hogy melyik képernyőelemnek kell adni a vezérlést.
A láncot oda-vissza mutatózzuk, hogy visszafelé is könnyű legyen a lépegetés.
Ha a láncot rekordok alkotják, akkor a rekordoknak tartalmazniuk kell procedure vagy function típusú mezőket is (ezekre ugorna a program amikor a képernyőelem eseményt kap), melyeket a rekord első használata előtt be kell állítani. Ez is járható út de igen körülményes.
A Pascalban viszont lehetőségünk van objektumok használatára is melyekkel sokkal egyszerűbben megoldhatjuk a problémát ráadásul a program áttekinthetőbb és rugalmasabb is lesz.
Az objektumok deklarálása
Elsőként deklarálunk egy ősobjektumot, mely azokat a mezőket és metódusokat tartalmazza, melyek az összes képernyőelemre jellemzőek. Ilyen pl: a kirajzolás, inicializálás, lebontás, eseményfeldolgozás.
Először is lássuk az ősobjektumot:
Type POldObject=^TOldObject; TOldObject=Object X, Y, W, H: Integer; Options : Word; EventMask : Word; Next : POldObject; Prev : POldObject; Constructor Init(ax, ay, aw, ah: Integer); Destructor Done; Virtual; Procedure HandleEvent(Event: TEvent); Virtual; Procedure Draw; Virtual; Procedure DrawIt; Virtual; End;
POldObject | az alapobjektumra mutató pointertípus |
Mezők: | X, Y | a képernyőelem bal felső sarkának koordinátái |
W, H | a képernyőelem szélessége és magassága | |
Options | a képernyőelem tulajdonságai | |
EventMask | a képernyőelem által kért eseménytípusok listája | |
Next | a következő képernyőelemre mutató alapobjektum típusú pointer. | |
Prev | az előző képernyőelemre mutató alapobjektum típusú pointer. | |
Metódusok: | Init | Ez az objektum Inicializálását végzi. Ez állítja be az objektum/képernyőelem alapbeállításait |
Done | Ez kiveszi az objektumot a láncból, és törli magát memóriából. | |
HandleEvent | Ez az objektum/képernyőelem eseményértelmezője. A HandleEvent akkor hívódik meg, ha olyan esemény érkezik, ami az objektumnak szól és az EventMaskban szerepel. (bővebben lásd később) A TEvent az eseményt tartalmazza ami miatt meghívódott a metódus. | |
Draw | Ez végzi a képernyőelem kirajzolását. | |
DrawIt | előkészíti a kirajzolást. |
Az esemény-adatszerkezet
Amint fentebb is utaltam rá, az eseményeket egységes szerkezetbe kell rendezni. Ezt a legegyszerűbben egy alternatív rekorddal tehetjük meg:
Const | Az események neveihez konstans számokat rendelünk. |
EvNothing = 1; | Nincs esemény |
EvMouseMove = 2; | Egér mozgatása |
EvMouseDown = 4; | Egér gombnyomás |
EvMouseUp = 8; | Egér gomb felengedés |
EvKeyDown = 16; | Billentyű leütés |
EvMessage = 32; | Üzenet (másik objektumtól) |
EvCommand = 64; | Parancs (másik objektumtól) |
Type | |
TEvent = record | Az eseményeket leíró alternatív rekord |
What: Word; | Az esemény típusát leíró mező |
case Word of | Ha a típus ... |
EvNothing: (); | EvNothing akkor nincs további mező |
EvCommand: ( | EvCommand akkor van egy |
Command: Word); | Command mező, ami leírja, hogy milyen parancs érkezett. |
EvMouseUp, EvMouseDown, EvMouseMove: ( |
EvMouseUp, EvMouseDown, EvMouseMove akkor van |
Buttons: Byte; | Buttons mező - az egér gombjainak állapotát írja le |
WhereX : Integer; | WhereX és WhereY mező ami az |
WhereY : Integer); | egér helyzetét mutatja |
evKeyDown: ( | EvKeyDown akkor |
Shift : Byte; | A Shift mező a Shift gombok állapotát mutatja |
case Integer of | A lenyomott billentyűt ki lehetolvasni |
0: (KeyCode: Word); | KeyCode formában |
1: (CharCode: Char; | vagy Karakterképben és |
ScanCode: Byte)); | Scankódban (csak ha a karakterkép #0 egyébként mindíg 0 értéke van) |
evMessage: ( | EvMessage akkor |
InfoPtr: Pointer); | az InfoPtr a küldő objektum címét tartalmazza. |
end; |
Az eseményfigyelő procedúra
Var | Az egér figyeléséhez |
OldX,OldY: Integer; | szükséges globális |
OldButtons: Byte; | változók |
Procedure GetEvent(Var E: TEvent); | Az eseményfigyelő rutin |
Function Shiftpress:Byte; Assembler; { 1: RShift 2:LShift 3:Both } |
A Shiftpress funkció |
Asm | megadja, hogy a Shift |
XOR AX, AX | gombok közül melyek |
MOV AH, 02h | vannak lenyomva. |
INT 16h | |
AND AX, 3 | |
End; | |
Var | Belső változók |
EX,EY: Word; | |
b1: byte; | |
ch: Char; | |
cb: Byte; | |
Begin | |
E.What:=EvNothing; | Alapesetben nincs esemény |
if Keypressed then | A gombnyomás ellenőrzése |
Begin | |
E.What:=EvKeyDown; | Ha van, az eseménytípus beállítása |
E.Shift:=ShiftPress; | A Shift status beállítása |
ch:=Readkey; | A billentyő lekérdezése |
if ch=#0 then E.KeyCode:=Ord(Readkey)*256 else | Ha 0 a billentyűkód akkor a következő billentyűkódot is lekérdezi. |
Begin | |
E.KeyCode:=ord(ch); | különben a KeyCode az ASCII kód, |
E.CharCode:=ch; | a CharCode ua. csak Char típusú változóban |
End; | |
End; | |
EX:=HiMouseX; | Egérhelyzet lekérdezése |
EY:=HiMouseY; | |
E.Buttons:=Buttons; | Gombok lekérdezése |
b1:=buttons; | |
if ((OldX<>EX) or (OldY<>EY)) then | Ha az egér helyzete változott |
Begin | akkor |
E.What:=EvMouseMove; | Eseménybeállítás |
E.WhereX:=EX; | A helyzet betöltése az |
E.WhereY:=EY; | eseményleíróba |
End; | |
if (((b1 and 1)<>0) and ((OldButtons and 1)=0)) or | Ha lenyomták valamelyik |
(((b1 and 2)<>0) and ((OldButtons and 2)=0)) then | egérgombot |
Begin | akkor |
E.What:=EvMouseDown; | Eseménybeállítás |
E.WhereX:=EX; | A helyzet betöltése az |
E.WhereY:=EY; | eseményleíróba |
End; | |
if (((b1 and 1)=0) and ((OldButtons and 1)<>0)) or | Ha felengedték valamelyik |
(((b1 and 2)=0) and ((OldButtons and 2)<>0)) then | egérgombot |
Begin | akkor |
E.What:=EvMouseUp; | Eseménybeállítás |
E.WhereX:=EX; | A helyzet betöltése az |
E.WhereY:=EY; | eseményleíróba |
End; | |
OldX:=EX; | Az egér helyzetének frissítése |
OldY:=EY; | Az egér helyzetének frissítése |
OldButtons:=b1; | Az egér helyzetének frissítése |
End; |
Procedure System_Run; | |
Var | |
E: TEvent; | |
P1,P2: POldObject; | |
Begin | |
Repeat | |
GetEvent(E); | Az eseményfigyelő meghívása |
Case E.What of | |
EvKeyDown: Begin | Ha megnyomtak egy gombot |
if Focused<>Nil then | és a Focused nem NIL |
Begin | akkor a program a |
if Focused^.EventMask and EvKeyDown<>0 then Focused^.HandleEvent(E); | Focused-nek adja a vezérlést ha jogosult rá. |
End; | |
End; | |
EvMouseDown, | Ha az egérrel történik valami |
EvMouseUp, | akkor |
EvMouseMove: Begin | |
P1:=PFirstObject; | |
While P1<>Nil do | a program elkezdi keresni az |
Begin | objektumokat tartalmazó |
P2:=P1^.Next; | láncban azt az objektumot, |
if IsInArea(E.WhereX, E.WhereY, P1^.X, P1^.Y, P1^.W, P1^.H) and | amelyik felett áll az egérkurzor. |
(P1^.EventMask and E.What<>0) then | |
Begin | Ha megtalálta az objetumot és az jogosult az eseményre |
SetFocus(P1); | akkor azt beállítja Focused-nek és meghívja |
P1^.HandleEvent(E); | a HandleEvent rutinját. |
End; | |
P1:=P2; | A következő képernyőelemre lép. Ez azért nem P1:=P1^.Next mert ha közben a P1-et törölték a memóriából akkor kiakadna. (a P1 pointer nem létez? objektumra muatatna) |
End; | |
End; | |
End; | |
Until SystemExit; | A ciklus addig ismértlődik, amíg a SystemExit globális változó értéke False. |
End; |
A cikk Winword DOC formátumban
Itt van az XVISION1.EXE !
Gerebenics Andor
eMail:PC-XUser@IDG.HU, Subject: "Graphika rovat"