Grafikus kinézetű elemek TV számára III
 
Előző számunkban az ablakokon lévő ikonokat írtuk át, kijavítottuk a TButton hibás árnyékát, s egyéb aprócska trükkökről beszélgettünk. Most egy nagyobb lélegzetű dologról a scrollbar (Ř gördítősáv. Inkább maradjunk a scrollbar-nál.) szebbétételéről csevegnék.

Ahogy azt megszokhattuk először is az új control karaktereit kell megrajzolnunk. A szokásos CHAREDIT programmal ezt megtehetjük, s az alábbi nyilakat kell megrajzolni:
 
Felfelé nyíl két karakterből: #212 és #183: 
 
Felfelé nyíl a második karakter invertálása után: 
 
 
Lefelé nyíl két karakterből: #213 és #184: 
 
 
Lefelé nyíl a második karakter invertálása után: 
 
 
Bal nyíl két karakterből: #217 és #21: 
 
 
Bal nyíl a második karakter invertálása után: 
 
 
Jobb nyíl két karakterből: #218 és #185: 
 
 
Jobb nyíl a második karakter invertálása után: 
 
 
Láthatjuk, hogy a nyilak két karakterből állnak a bal oldali részük keretkarakterből, hogy a 9. oszlopban megismételje a 8. oszlop beli pontokat. A nyíl jobb karaktere meg egy invertált karakterből áll, hogy a 9. oszlopban egy teljes csík látszódjék (Így nem lesz üres a 9. oszlopa a nyílnak, s illeszkedni fog a scrollbar többi részéhez. Figyelem nem a 8. Oszlop pontjait fogja a 9.-ben megismételni, hanem a teljes 9.-et fogja kigyújtani.) Megjegyezném, hogy most már a Norton Utilities felhasználói felületén túlnőttünk, hisz ott nincsen horizontális scrollbar. (A NU NCD programja használja egyedül a horizontális scrollbar-t, de az valami fertelmes, sima karakterekből van.)

Nézzük meg, hogyan kell módosítanunk a VIEWS unit forráskódját ahhoz, hogy szép saját scrollbar-jainkat használhassuk.

A Turbo Vision írói ötletes módon egy tömbbe rakták azokat a karaktereket melyekkel a scrollbar-t kiírják. Így a jobb nyíl ASCII kódját, majd a bal nyílét, stb. Persze ezek a sima DOS-os karakterek nem valami szépek, nem csoda, hisz nem erre lettek kitalálva. Épp ezért írjuk át. Mi is az eredetihez hasonlóan egy tömbbe rakjuk a karaktereket amikkel majd kiírjuk a scrollbar-t. A bökkenő az, hogy a TV által definiált ilyen tömb típus (TScrollChars) csak 5 karakteres nekünk meg 6 karakteres tömb kell lévén, hogy a fel és le nyilunk nem egy hanem két karakter széles. Így definiálhatunk egy új, saját típust legyen a változatosság kedvéért: TPCXScrollChars.

Majd ezt a tömböt saját scrollbar objektumunk Init-jében ahogy az eredeti TScrollBar-ban initializáljuk. Persze nem csak grafikus kinézetű karakterekkel töltünk fel egy tömböt, hanem meghagyjuk a sima text karakterekkel is mert lehet, hogy sima nem grafikus kinézetű módban akarjuk elindítani az applikációt. Így elkerülhetjük azt, hogy két külön scrollbar objektumot kelljen használni attól függően, hogy sima text, vagy grafikus látszatú text módban fut –e a programunk. Így a text módtól függően egy objektum fogja lekezelni a scrollbar-t.

Lássuk a saját scrollbar objektumunk init-jét:

constructor TPCXScrollBar.Init(var Bounds: TRect);
const
{ felb felj leb lej }
  GVChars: TPCXScrollChars = (#212, #183, #213, #184, GFrameEdgeL, GFrameEdgeR);
  GHChars: TPCXScrollChars = (#217, #21, #218, #185, #215, #215);
  VChars : TPCXScrollChars = (#30, #31, #177, #254, #178, #32); {Text, it's }
  HChars : TPCXScrollChars = (#17, #16, #177, #254, #178, #32); {the original}
begin
  TView.Init(Bounds);
  if IsPCXGraphCharsOn then TempB:=2 else TempB:=1;
  Value := 0;
  Min := 0;
  Max := 0;
  PgStep := 1;
  ArStep := 1;
  if Size.X = TempB then
  begin
    GrowMode := gfGrowLoX + gfGrowHiX + gfGrowHiY;
    if IsPCXGraphCharsOn then Chars := GVChars
                         else Chars := VChars;
    IsHorizontal:=False;
  end else
  begin
    GrowMode := gfGrowLoY + gfGrowHiX + gfGrowHiY;
    if IsPCXGraphCharsOn then Chars := GHChars
                         else Chars := HChars;
    IsHorizontal:=True;
  end;
end;

Lássuk az Init() megvalósítását:  
Konstans Jelentése ha bekapcsolt:
gfGrowLoX A view bal oldala konstans távolságra marad az owner-ének (tulajdonosának) jobb oldalától.
gfGrowLoY A view tetejének koordinátái nem változnak az owner aljának koordinátáihoz képest. A távolság a view teteje és az owner alja között konstans marad.
gfGrowHiX A view jobb oldala konstans távolságra marad az owner jobb oldalától.
gfGrowHiY A view alja konstans távolságban marad az owner aljától.
gfGrowAll A view az owner átméretezésekor csak mozog, s az owner jobb alsó sarkához képest távolsága nem változik.
gfGrowRel TWindow-nak a spec. méretváltozása 25-sorról 43/50-re való váltáskor. Ekkor az eredeti arányok megmaradnak.
 
Megjedzendő, hogy a: Nézzük meg, akkor hogyan változik a scrollbar mérete pl. a TWindow átméretezésekor: Hál’ Isten eljutottunk az 5. oldalra odáig, hogy initializáltuk a TPCXScrollBar objektumunkbat, lássunk pár nyilvánvaló, s alapvető apróságot, mit át kell írni az eredeti TScrollBar-hoz képest:

Nyílván való, hogy ahol az eredeti TScrollbar megvizsgálja, hogy horizontális, vagy vertikális –e a scrollbar ott módosítani kell, mert a régi feltétel ez volt: Size.X = 1. Ez ugye megváltozott hisz lehet kettő széles scrollbar s még akkor is vertikális, csak grafikus kinézetű. (hisz az eredeti objektumban minden ami nem egy széles volt az horizontális volt.) Így a Size.X = 1 -eket Size.X = TempB-re kell átállítani. (Persze bizonyos helyeken fel lehetne használni az IsHorizontal: Boolean logikai változót, de nem tesszük mert inkább megőrizzük a régivel a látszat szerinti kompatibilitást.) Nos lássuk, mit kell átalakítani. Első sorban a HandleEvent()-öt ami jelentős számban használja ezen vizsgálatot. Így az új objektumunk HandleEvent()-je így néz ki:

procedure TPCXScrollBar.HandleEvent(var Event: TEvent);
{...}
function GetPartCode: Integer;
var
  Mark, Part: Integer;
begin
  Part := -1;
  if Extent.Contains(Mouse) then
  begin
    if Size.X = TempB then Mark := Mouse.Y else Mark := Mouse.X;
    if Mark = P then Part := sbIndicator else
    begin
      if Mark < TempB then Part := sbLeftArrow else
      if Mark < P then Part := sbPageLeft else
      if Mark < S then Part := sbPageRight else
      Part := sbRightArrow;
      if Size.X = TempB then Inc(Part, 4);
    end;
  end;
  GetPartCode := Part;
end;

{ ... }

begin
  TView.HandleEvent(Event);
  case Event.What of
  evMouseDown:
  begin
    { ... }
    if ClickPart <> sbIndicator then
    begin
      { ... }
      end else
      begin
        repeat
          MakeLocal(Event.Where, Mouse);
          Tracking := Extent.Contains(Mouse);
          if Tracking then
          begin
            if Size.X = TempB then I := Mouse.Y else I := Mouse.X;
          end;
          { ... ... ... ... ... ...}
        end;
      end;

Hasonlóan ki kell cserélni a GetSize() függvényben is:

function TPCXScrollBar.GetSize: Integer;
var
  S: Integer;
begin
  if Size.X = TempB then S := Size.Y else S := Size.X;
  if S < 2+TempB then GetSize := 2+TempB else GetSize := S;
end;

Nos jöjjön az objektum lelke a Draw() és DrawPos() metódus, melyet nem véletlenül hagytam a végére. A Draw() metódust ugyanúgy kell az objektumban implementálni ahogy elődjében. (Azért kell deklarálni, mert a DrawPos() rutin privát, s nem virtuális. Így az általunk írt DrawPos() metódus nem tud visszahatni ősének Draw() metódusára.) Lássuk a DrawPos()-t:

procedure TPCXScrollBar.DrawPos(Pos: Integer);
var
  S: Integer;
  B: TDrawBuffer;
  i: Word;
begin
  if IsPCXGraphCharsOn then
  begin
    if Not IsHorizontal then
    begin
      S := GetSize - 1;
      MoveCStr(B[0], Chars[0], GetColor(2));
      MoveCStr(B[1], Chars[1], SwapHighAndLowAreaOfByte(GetColor(2)));
      if Max = Min then
      begin
        i:=0;
        while i<(S*2)-1 do
        begin
          Inc(I,2);
          MoveCStr(B[i], ''+Chars[4]+Chars[5]+'', GetColor(1));
        end;
      end
      else
      begin
          i:=0;
          while i<(S*2)-1 do
          begin
            Inc(i,2);
            MoveCStr(B[i], ''+Chars[4]+Chars[5]+'', GetColor(3));
            MoveCStr(B[Pos*2], ''+Chars[4]+Chars[5]+'', GetColor(4));
          end;
      end;
      MoveCStr(B[S*2], Chars[2], GetColor(2));
      MoveCStr(B[(S*2)+1], Chars[3], SwapHighAndLowAreaOfByte(GetColor(2)));
      WriteBuf(0, 0, Size.X, Size.Y, B);
    end
    else
    begin
      S := GetSize - 1;
      if Pos=1 then Inc(Pos);
      if Pos+1=S then Pos:=S-3;
      MoveCStr(B[0], Chars[0], GetColor(2));
      MoveCStr(B[1], Chars[1], SwapHighAndLowAreaOfByte(GetColor(2)));
      if Max = Min then
        MoveChar(B[2], Chars[4], GetColor(1), S - 2)
      else
      begin
        MoveChar(B[2], Chars[4], GetColor(3), S - 2);
        MoveCStr(B[Pos], Chars[5]+Chars[5], GetColor(4));
      end;
      MoveCStr(B[S-1], Chars[2], GetColor(2));
      MoveCStr(B[S], Chars[3], SwapHighAndLowAreaOfByte(GetColor(2)));
      WriteBuf(0, 0, Size.X, Size.Y, B);
    end;
  end
  else
  begin
    S := GetSize - 1;
    MoveChar(B[0], Chars[0], GetColor(2), 1);
    if Max = Min then
      MoveChar(B[1], Chars[4], GetColor(1), S - 1)
    else
    begin
      MoveChar(B[1], Chars[2], GetColor(1), S - 1);
      MoveChar(B[Pos], Chars[3], GetColor(3), 1);
    end;
    MoveChar(B[S], Chars[1], GetColor(2), 1);
    WriteBuf(0, 0, Size.X, Size.Y, B);
  end;
  Message(Application, evCommand, cmMouseChanged, @Self);
end;
 
Hogyan is működik ?

Hát ezen számunkra ennyi, mert már teljesen kifordultak szemeim két nap folyamatos gammasugárzás hatására, amit ez a 15" moncsi ad (annak ellenére, hogy Low Radiaton) Sok volt így vizsgaidőszakban ez a három nagyobb terjedelm? cikk egyhuzamban.
Ha valakinek kérdése van eMail-ezzen, rögvest válaszolok.

Egy kérdés érdekel valakit a C++ objektum orientáltsága? Mert ha igen akkor arról beszélhetünk a TV-átírás után. Akit érdekel, eresszen el egy egysoros levelecskét a PC-XUser@FREEMAIL.C3.HU-ra.
 

Bérczi László
eMail: PC-XUser@FREEMAIL.C3.HU, Subject: "OOP Rovat"