Már régóta beszélünk róla, hogy kellene készíteni egy olyan felhasználói-felületet szöveges módban, mely garfikus kinézetű. Ezt már sok cég programja meg is valósította. (DOS / MSAV - Defrag, NU, SR) Próbáljunk az eddigi sok program hibáiból okulni, és praktikumait felhasználni.
Az első dolog, hogy felmérjük a feladatunkat. Meg kell határozni mibôl, mit akarunk csinálni.
procedure InitBackGround; virtual;
end;
PPCXApplication = ^TPCXApplication;
TPCXApplication = Object(TApplication)
public
constructor Init;
destructor Done; virtual;
procedure InitDeskTop;
virtual;
function GetPalette:
PPalette; virtual;
end;
Látjuk, hogy új desktop-ot definiáltunk, annak érdekében, hogy a #176-os (kockás) karaktert, egy üresre a space (helyköz - #32)-re cseréljük.
Mivel az applikációnak a GetPalette metódusával már át is definiáltuk (lást OOP03.doc) a palettáját, és a TBackground palettájának egyetlen eleme a tulajdonosának - az-az az applikáció - palettájának elsô elemére mutat, így elég volt csak az applikáció palettájának elsô elemét megváltoztani a más háttérszín elérése végett. (Persze azért megjegyzem, hogy az applikáció palettájának elsô eleme csak a háttér színének tárolására van fenntarva.)
Nos ahogy új desktop-ot adunk az új (PCX) applikációhoz, úgy annak a színe, s a feltöltési karaktere is megváltozik.
Továbbá (sôt továbbá javasolnám Karthago elfoglalását ... huhhh de régen voltam én történelem órán ...), szóval továbbá érdemes lenne az applikációnak elmenteni a képernyôn lévô szöveget, hogy a programból való kilépés után ugyanazt lássuk, mint elôtte. Ezt simán $B800:0 címtôl 4096 byte elmentése a prg. indítása elôtt (constructor Init; elôtt), majd kilépéskor, az-az a destructor Done;-ban. (Lásd a forráskódot.)
Ha már ennyire belelendültünk akkor, alakítsuk még "pofásabbra" az applikációt. Vegyük a drága - jó Windows-t. Nos ott van minden programnak egy fejléce, amire a program neve van írva. Huhh, ok csináljuk ezt meg mi is.
Ezt legegyszerűbben egy TStaticText-tel oldhatjuk meg. Persze valami izléses színben. Legyen mondjuk sötétkék (01 szín) háttér, és fehér szinű betűk (15). Ehhez, mint már lassan megszoktuk új objektumot kell örökítenünk a TStaticText-bôl, amit nevezzünk a könnyebb elérhetôség végett TPCXTitleBar-nak. Lássuk az új objektum felépítését:
type PPCXTitleBar = ^TPCXTitleBar; TPCXTitleBar = Object(TStaticText) constructor Init(AText: String); function GetPalette: PPalette; virtual; procedure SetTitleText(AText: String); end;Nos azt láthatjuk, hogy eltér az eredeti TStaticText-tôl már az init is. Hogy miért, a válasz egyszerű: hol akarná az ember a program fejlécét máshol, mint a 0,0 (bal-felsô sarokban). Így nem kell a koordinátákat megadni.
A GetPalette metódus egyértelmű, hisz ezzel adjuk meg a színt. A kérdés, hogy hogyan ? Mibôl, ... mi fog mire mutatni ? Az applikáció palettájához hozzá kell adni egy új színt a program-cím szinét. Legyen ez esetünkben az utolsó. (175. fô paletta index.) Erre az indexre fog mutatni a TPCXTitleBar palettájának egyetlen eleme.
A SetTitleText meg arra szolgál, hogyha egy program esetleg idô közben morfoizálódna avagy a programozó hirtelen felindulásból topic-ot (címet) kívánna váltani az az OOP elvei szerint könnyen megoldható legyen.
Megvalósítás a forráskódban (PCX_APP.pas).
Fontos megemlíteni hogy a Borland, a Turbo Vision alkotói azt a szokást alakították ki, hogy bizonyos az applikációhoz tartozó elemeket nem az objektumban (tehát nem a TApplication-ban) deklaráltak, hanem az ôket tartalmazó unit-ban az APP.pas-ban. Nyilván ezt azért tették, hogy az összes objektum elérhesse, igaz ezzel megsértették az adatrejtés elvét. De hát mi is így teszünk, s a TPCXTitleBar-t a PCX_APP.pas Interface részében deklaráljuk.
const PCXTitlebar: PPCXTitleBar = nil;Ehhez az inicializált változóhoz (konstanshoz) a TPCXApplication Init-je hozza létre a view-ot.
Eredeti célkitűzésünk a Turbo Vision szinesebbé tétele, ehhez szükséges az az információ ami segít minket abban, hogy ne csak az alacsony intenzitású szineket (0-7) lehessen háttérszínként használni, hanem a világosabbakat (0-15) is. Ez persze azzal jár, hogy nem lesznek villogó szineink de ki bánja !? Fent ezt már a háttérszín kialakításánál fel is használtuk.
procedure SetBackGroundIntensity(On: Boolean); Assembler; asm mov bl, On or bl, bl jne @To0 mov bl, 01h jmp @Folyt @To0: xor bl, bl @Folyt: mov ax, 1003h int 10h end;Láthatjuk, hogy videó (INT 10h) megszakítás valósítja ezt meg. Ha fenti procedure True értéket kap akkor felkapcsolja, ha False akkor lekapcsolja a háttérintenzitást. A háttérintemzitást le is lehet kérdezni a memória $40:$65 byte-ján, s ezen byte 5. bit-én. Ha ez a bit lekapcsolt akkor van háttérintenzitás, ellenkezô esetben nem.
A karakterátírás gyakorlati megvalosítását a Tippek - Trükkök rovat 3. számában közöltem. Mivel nem kívánom újra leírni így teljes mélységeiben mindent megtalálhattok a PC-XUSER\OOP\T&T03.HLP\ könyvtárban t&t03.arj file-ban. Ez a T&T 3. száma tömörítve.
A lényeg a amire hangsúlyt kell helyeznünk - hisz már mindenki tud karaktert átírni - hogy milyen képű karaktereket kell létrehoznunk. A rég említett CHAREDIT vizuális programmal fogjuk megtervezni a karakterek képét. Vegyük sorra gondolatainkat:
Láthatóak az egyes karakterek, az F1-vel lehívjuk ôket, a kék mezôben szerkesztjük ôket a space billentyűvel kapcsoljuk ki ill. be a pontokat. Az F2 gombbal pedig mentjük el a kész karakter bit-térképét.
(Itt láthatók a keret sarkait összekötô karakterek !!!)
Nos térjünk vissza lassan a GUI-k (Graphical User Interface, pl. OS/2, Windows) ablakcímeihez. Nyilván észrevettük, hogy a bal felsô sarokban található egy kis gomb aminek a segítségével egy menüt hívhatunk le. Ez a menü tartalmazza az ablakkal (- vagy az applikáció ablakával) végezhetô műveleteket: kicsinyítés, nagyítás, bezárás. Ezt control-box-nak nevezzük. Rajzoljuk mi is egyet. Ezt csak két karakterbôl tudjuk létrehozni így:
A középen látható vastag vonal a két karakter határa. Fontos: hogy takarékoskodjunk a keret rajzoló karakterekkel, így elég, ha csak a bal oldali karakter a keret-karakter - hogy összeérjen a control-box, és jobb oldalinak nem kell annak lennie.
Gondoljuk végig, milyen objektumokra lesz szükség, és ezeket a program mely részein kívánjuk használni:
Mivel több helyen kívánjuk alkalmazni a controll-box-ot (applikáció - window - dialog) ezért hozzunk létre egy közös ôst mely elvégzi a legalapvetôbb műveleteket. Legyen ô a TPCXControllbox. Metódusai:
PPCXControlBox = ^TPCXControlBox; TPCXControlBox = Object(TStaticText) constructor Init(P: TPCXPoint); procedure HandleEvent(var Event: TEvent); virtual; procedure ExecControlMenuBox; virtual; private ControlMenuBox: PPCXMenuBox; end;Láthatjuk, hogy ez is, mint a titleline is a TStaticText-bôl származik hisz nem más, mint két - vagy három kiirandó karakter, amihez menüt társítunk. A továbbiakban esetleges menüt a ControlMenuBox változóban tároljuk.
constructor TPCXControlBox.Init(P: TPCXPoint); var R : TRect; TempS: String; begin ControlMenuBox:=nil; R.A.X:=P.X; R.A.Y:=P.Y; R.B.Y:=R.A.Y+1; if IsPCXGraphCharsOn then begin R.B.X:=2; TempS:=ControlBox; end else begin R.B.X:=3; TempS:='[X]'; end; Inherited Init(R, TempS); end;Láthatjuk, hogy inicializáljuk a ControlMenuBox vátozót. Elôkészítjük az R: TRect változót a TStatusLine örökölt inicializálásához. Az R.A = P. Az-az a koordináták az R. A mezôjében megegyeznek, 1 sor lesz a szélesség.
Ha grafikus kinézetű az applikációnk (az-az használunk karakterátírást) akkor kettô széles a kiirandó szöveg, és a szöveg - pontosabban a kiirandó két karakter - a fent lerajzolt ábra szerint, ill. ha csak sima textmódban vagyunk akkor ugye 3 karakteres, még pedig ez: [X] .
Majd meghívjuk az örökölt TStatusLine init-et.
Ennek a fô objektumnak nem kell ehhez a metódushoz kódot társítania így simán ennyi lesz:
procedure TPCXControlBox.ExecControlMenuBox; begin Abstract; end;Ahol az absztrakt jelzi, hogy még nem használt metódus, majd az utódok használják. Ha mégis meghívnánk szépen leáll runtime error-ral a programunk, figyelmeztetve, hogy ez csak egy absztrakt metódus.
Lássuk a belôle származtatott utódokat, és egynek részletes felépítését. (a többi hasonló analógia alapján felépíthetô). Három utód lesz, a három különbözô használati helyen:
function TPCXControlBoxApp.GetPalette: PPalette; const P: String[Length(CPCXControlBoxApp)] = CPCXControlBoxApp; begin GetPalette:=@P; end;Nyilván virtuális metódus ez is, s a palettául szolgáló konstans az Interface részben van deklarálva. Ennek egy eleme van, mivel ez az applikáció controlbox-a, és ez a címsorban helyezkedik ez azonos színű lesz vele. Ez az egy elem, mint a címsor szine (CPCXTitleBar) a 175. indexre mutat a tulajdonosa, most az applikáció palettájában. Tehát: CPCXControlBoxApp = #175;
A többi ôs is hasonlóan rendeli a controlbox-hoz a palettát, de ott mivel a dialógus ill. az ablak a paletta-tulajdonosa ezért a #34-dik indexre mutat.
procedure TPCXControlBoxApp.ExecControlMenuBox; var R : TRect; O : TPCXPoint; C : Word; Event: TEvent; begin O.Assign(0,0); MakeGlobal(O, O); R.Assign(O.X,O.Y+1,O.X+18,O.Y+7); New(ControlMenuBox, Init(R, NewMenu( NewItem('~M~ove', 'Crt-F5', kbCtrlF5, cmMove, hcNoContext, NewItem('~Q~uit', 'Alt- X', kbAltQ, cmQuit, hcNoContext, NewLine( NewItem('~R~efresh', '', kbNoKey, cmRefresh, hcNoContext, nil))))), nil)); C:=Application^.ExecView(ControlMenuBox); if C<>cmCancel then begin case C of cmMove: begin KeyStrokeToKeyboardBuffer(0, $62); Message(Application, evCommand, cmMouseChanged, @Self); end; cmRefresh: Application^.ReDraw; cmQuit : Message(Application, evCommand, cmQuit, @Self); end; end; end;Láthatjuk ez már egy fokkal szebb.
KeyStrokeToKeyboardBuffer(0, $62);
Az applikáció init-jében (TPCXApplication.Init) simán létrehozzuk, és láthatóvá teszük:
{...} New(PCXTitlebar, Init(#3+ApplicationName)); {látható a cím-sor létrehozása} Insert(PCXTitlebar); {láthatóvá tétele} P.Assign(0,0); {a controlbox helyének kijelölése} New(ControlBoxApp, Init(P)); {létrehozása} Insert(ControlBoxApp); {megjelenítése} {...}Nos ez mind szép és jó, van már működô controlbox-unk, de mi van a kerettel, ettôl még nem lesz szép menünk, ill. ablakunk.
Nos mivel a menüt akarjuk kirajzoltatni, elôször nézzük meg a Pascal RTL (forráskód) \TV könyvtárában a MENUS.PAS unit-ot mely tartalmazza a TMenuBox objektumot mely, mint vettük egy menüt (egy menü téglalapot) rajzol ki. Tehát nézzük, meg hogy is oldották meg eredetileg, s ezt a forráskódot magunkhoz átmásolva módosítgassuk úgy, hogy szép legyen.
Szerencsénk van minimális átírással elérhetjük azt, hogy szép legyen a menü. Vegyük végig a menü kirajzolásának elvét:
Szerencsénkre el van különítve egy rutin mely a buffer-be írja a keretet (ez a Draw procedure-án belűl egy lokális rutin a FrameLine() mely egy sort ír ki a keretbôl, a buffer-be), a képernyôre a DrawLine úgyszint lokális procedure írja ki.
Tehát nekünk a FrameLine rutint kell tanulmányoznunk, lássuk az eredeti lokális rutint:
procedure FrameLine(N: Integer); const FrameChars: Array[0..19] of Char = ' - '; begin MoveBuf(B[0], FrameChars[N], Byte(CNormal), 2); MoveChar(B[2], FrameChars[N + 2], Byte(Color), Size.X - 4); MoveBuf(B[Size.X - 2], FrameChars[N + 3], Byte(CNormal), 2); end;Hogyan működik ?
procedure FrameLine(N: Integer); const FrameChars: Array[0..19] of Char = = ' - '; PCXFrameChars: Array[0..19] of Char = GFrameCornerLU+GFrameSummitU+GFrameSummitU+GFrameSummitU+GFrameCornerRU+ GFrameCornerLD+GFrameSummitD+GFrameSummitD+GFrameSummitD+GFrameCornerRD+ GFrameEdgeL+#32#32#32+GFrameEdgeR+ GFrameEdgeL+#32#196#32+GFrameEdgeR; begin if IsPCXGraphCharsOn then begin MoveBuf(B[0], PCXFrameChars[N], Byte(CNormal), 2); MoveChar(B[2], PCXFrameChars[N + 2], Byte(Color), Size.X - 4); MoveBuf(B[Size.X - 2], PCXFrameChars[N + 3], Byte(CNormal), 2); end else begin MoveBuf(B[0], FrameChars[N], Byte(CNormal), 2); MoveChar(B[2], FrameChars[N + 2], Byte(Color), Size.X - 4); MoveBuf(B[Size.X - 2], FrameChars[N + 3], Byte(CNormal), 2); end; end;Hogy is van ez ?
Hát ennyi mára, hisz ez már a 9.5-dik oldal. Ha valaki a forráskód részletes átrágása után se értené a dolgok mikéntjét, az eMail-ezzen.
Izelítôül egy szebb kinézetű program: