Dialogen bouwen
Arnout van Kempen schrijft in deze rubriek over pret maken met computers. Hij gaat aan de slag met Pascal.
Een dialoog is een venster dat de gebruiker om informatie vraagt, of informatie toont. In ons geval het tweede: we laten CPU-informatie zien. De gebruiker leest het, klikt OK, klaar. Turbo Vision behandelt dialogen als objecten die je tijdens runtime opbouwt. Je maakt een dialoog, voegt er controls aan toe (tekst, knoppen, invoervelden), toont het en ruimt het daarna weer op. Dat patroon maken, gebruiken, opruimen, zie je overal in code die direct met geheugen werkt.
ShowSysInfo is waar dat gebeurt. We bouwen een dialoog op, vullen hem met informatie, tonen hem aan de gebruiker en gooien hem daarna weg.
De code
Procedure TSysInfoApp.ShowSysInfo;
Var
D: PDialog;
R: TRect;
Control: PView;
C: Word;
CPUText, FPUText: String;
Begin
CPUText := 'CPU: ' + DetectCPU;
FPUText := 'Math Coprocessor: ' + DetectFPU;
R.Assign(20, 5, 60, 13);
D := New(PDialog, Init(R, 'CPU Information'));
R.Assign(2, 2, 38, 3);
Control := New(PStaticText, Init(R, CPUText));
D^.Insert(Control);
R.Assign(2, 3, 38, 4);
Control := New(PStaticText, Init(R, FPUText));
D^.Insert(Control);
R.Assign(15, 5, 25, 7);
Control := New(PButton, Init(R, '~O~K', cmOK, bfDefault));
D^.Insert(Control);
C := ExecView(D);
Dispose(D, Done);
End;
Pointers overal
Je ziet dat alle types beginnen met P: PDialog, PView, PStaticText, PButton. Dat zijn pointers naar objecten, geen objecten zelf. Waarom pointers? Omdat Turbo Vision controls dynamisch beheert. Als je een dialoog maakt, reserveert New geheugen op de heap. De dialoog blijft daar totdat je Dispose aanroept. Datzelfde geldt voor alle controls die je toevoegt: elk krijgt zijn eigen stukje heap-geheugen.
Het alternatief zou zijn om alles op de stack te zetten, maar dan weet je van tevoren precies hoeveel ruimte je nodig hebt. Met pointers kan je tijdens runtime beslissen: deze dialoog heeft drie controls, die andere vijf. Flexibel en geheugen-efficiënt.
De dialoog maken
R.Assign(20, 5, 60, 13);
D := New(PDialog, Init(R, 'CPU Information'));
R.Assign vult de TRect met coördinaten. De dialoog begint op kolom 20, regel 5 en eindigt op kolom 60, regel 13. Dat geeft een venster van veertig kolommen breed en acht regels hoog, ergens midden op het scherm.
New(PDialog, Init(...)) reserveert geheugen voor een TDialog object en roept de Init constructor aan. Die krijgt twee parameters: de rechthoek (waar het venster komt) en de titel (CPU Information). Na deze regel bestaat de dialoog, maar is nog leeg. Tijd om er dingen in te zetten.
Controls toevoegen
Alle controls; tekst, knoppen, invoervelden; zijn afgeleid van TView. Daarom kunnen we ze opslaan in een variabele van type PView.
R.Assign(2, 2, 38, 3);
Control := New(PStaticText, Init(R, CPUText));
D^.Insert(Control);
Let op de coördinaten: (2, 2, 38, 3). Die zijn relatief aan de dialoog, niet aan het scherm. Kolom 2 betekent: twee posities vanaf de linkerrand van de dialoog. Regel 2 betekent: twee regels vanaf de bovenkant van de dialoog (regel 0 is de titelbalk, regel 1 is de eerste lege regel). Het verschil tussen regel 2 en regel 3 is de hoogte: één regel tekst.
PStaticText is een label: niet-bewerkbare tekst die je laat zien. De Init krijgt de positie en de string die getoond moet worden.
D^.Insert(Control) voegt de control toe aan de dialoog. De ^ operator dereferentieert de pointer: D is een pointer naar een dialoog, D^ is de dialoog zelf. Insert is een method van TDialog die de control opslaat in een interne lijst.
De volgorde waarin je Insert aanroept, bepaalt de volgorde waarin controls getekend worden en ook de tab-volgorde als je door een dialoog navigeert met Tab. Hetzelfde patroon herhalen we voor de tweede tekstregel (FPU informatie) en de OK-knop.
De knop
R.Assign(15, 5, 25, 7);
Control := New(PButton, Init(R, '~O~K', cmOK, bfDefault));
D^.Insert(Control);
PButton maakt een klikbare knop. De parameters:
- R: positie en grootte;
- '
~O~K': de tekst, met tilde voor de sneltoets (Alt-O); - cmOK: het commando dat verstuurd wordt als je op de knop klikt;
- bfDefault: een flag die zegt "dit is de default button". Als de gebruiker Enter drukt terwijl de dialoog open is, wordt deze knop geactiveerd.
De coördinaten (15, 5) centreren de knop min of meer horizontaal in de dialoog. De breedte (25 - 15 = 10 kolommen) is breed genoeg voor de tekst plus wat ruimte eromheen. De hoogte (7 - 5 = 2 regels) geeft ruimte voor de rand van de knop.
De dialoog tonen
C := ExecView(D);
Dispose(D, Done);
ExecView(D) is de method die de dialoog modaal toont. "Modaal" betekent: het programma wacht tot de dialoog gesloten wordt. De event loop draait nog steeds, maar alleen voor deze dialoog. Niks anders reageert totdat de gebruiker OK klikt of Escape drukt.
ExecView retourneert een Word: het commando van de knop die de dialoog sloot. In ons geval cmOK als de gebruiker OK klikte, of cmCancel als de gebruiker Escape drukte. We slaan het op in C, maar doen er verder niks mee. Bij een dialoog waar je iets invult (bijvoorbeeld een bestandsnaam), zou je hier checken: als C gelijk is aan cmOK, verwerk dan de input, anders negeer het.
Dispose(D, Done) ruimt op. Dispose geeft het geheugen van de dialoog terug aan de heap. De tweede parameter, Done, is de destructor: een method die TDialog aanroept om alle controls netjes op te ruimen voordat het geheugen vrijgegeven wordt. Zonder Done zou je alleen het dialoog-object zelf verwijderen, maar alle controls (de teksten, de knop) zouden blijven rondzweven in het geheugen. Done loopt door de lijst van controls en roept voor elk hun destructor aan. Pas daarna wordt de dialoog zelf weggegooid.
Dit patroon, Init bij creatie, Done bij destructie, is de basis van resourcebeheer in talen zonder garbage collector. Modern C++ noemt het RAII (Resource Acquisition Is Initialization). Rust doet hetzelfde met Drop traits. Het principe blijft: wie geheugen claimt, moet het ook vrijgeven.
Wat we niet zien
De functies DetectCPU en DetectFPU bestaan nog niet. Volgende keer vullen we die in met assembly-code die daadwerkelijk de hardware detecteert. Voor nu zijn het placeholders. Maar de structuur staat: als die functies straks een string teruggeven, verschijnt die vanzelf in de dialoog.
Dat is het mooie van deze opbouw. ShowSysInfo weet niet hoe CPU-detectie werkt. Het vraagt alleen: geef me de info, ik zet het op het scherm. De detectie-logica kan zo simpel of complex zijn als nodig, ShowSysInfo blijft hetzelfde.
Gerelateerd
Menu's en event handling
Arnout van Kempen schrijft in deze rubriek over pret maken met computers. Hij gaat aan de slag met Pascal.
Events en de stroom van controle
Arnout van Kempen schrijft in deze rubriek over pret maken met computers. Hij gaat aan de slag met Pascal.
Een echt project: SystemInformation
Arnout van Kempen schrijft in deze rubriek over pret maken met computers. Hij gaat aan de slag met Pascal.
Turbo Vision, GUI vóór Windows
Arnout van Kempen schrijft in deze rubriek over pret maken met computers. Hij gaat aan de slag met Pascal.
Een serieus project
Arnout van Kempen schrijft in deze rubriek over pret maken met computers. Hij gaat aan de slag met Pascal.
