Delphi tools: Align component editor

Erik Stok (aka Baldo)

Delphi’s open tools api (OTA) biedt geweldige mogelijkheden om de IDE uit te breiden met handigheden van jezelf. Er is veel over de OTA te vinden op het web, maar veel artikelen vertellen vooral wat er kan en verzuimen een praktisch toepasbaar voorbeeld te geven van het gebruik van de OTA. NLDelphi is een plaats die draait om Delphi in de praktijk, dus een betere plaats om de praktische kan van de OTA te beschrijven kan ik me niet indenken. Dit tweede artikel beschrijft het bouwen van een component editor.

Het idee

RSI is een niet te onderschatten risico in het vak van de programmeur. Voor mezelf was het gebruik van de muis tijdens het ontwerpen van schermen vaak een net iets te zware belasting. Toen ik zag dat ik vaak dezelfde handelingen aan het verrichten was, ging het instinct van de programmeur alweer kriebelen om dit te automatiseren. De align component editor was geboren.

De schermen die ik bouw bestaan veelal uit een aantal wincontrols met daarvoor een label met wat er in deze wincontrols kan worden ingevuld. Tussen de wincontrols wordt een afstand gehouden van 4 pixels, de labels ervoor worden links uitgelijnd en de wincontrols worden links uitgelijnd op afstand van 4 pixels van het breedste label. Onderstaande afbeelding is hiervan een voorbeeld.

Vaak liggen deze wincontrols in een container zoals een panel, groupbox of tabsheet. De tabvolgorde van de wincontrols loopt van boven naar beneden.

Wat ik wilde doen was ervoor zorgen dat ik voor een aantal containers een functie kon starten die de wincontrols zou uitlijnen volgens de genoemde regels.

Het meest gemakkelijk leek mij een rechtsklik op de container waar de controls in liggen. Een component editor lag voor de hand dus. In dit artikel zal ik de bouw van deze component editor behandelen.

Component editors

In de DesignEditors unit (terug te vinden in de Delphi map onder Source\ToolsAPI) zit een class genaamd TComponentEditor. Door een afgeleide van deze class te maken en bepaalde methods te overriden is het eenvoudig een eigen component editor te maken. Als in de Delphi help gezocht wordt op TComponentEditor dan staat vrij expliciet uitgelegd wat de TComponentEditor allemaal kan. Ik ga hier dan ook niet verder in op de details van TComponentEditor, behalve die details die noodzakelijk zijn om de align component editor te bouwen.

Het zal geen verrassing zijn dat de class definition voor de align component editor er als volgt uitziet:

TAlignEditor = class(TComponentEditor)
function GetVerbCount: Integer; override;
function GetVerb(Index: Integer): string; override;
procedure ExecuteVerb(Index: Integer); override;
end;

Via de GetVerbCount method kunnen we aangeven hoeveel acties er kunnen worden uitgevoerd met de component editor. In dit geval is er één actie voor de component editor geïmplementeerd: align.

Om het pop-up menu te kunnen vullen zal voor het aantal acties opgegeven in GetVerbCount de GetVerb van de component editor aangeroepen worden om de captions van de menu items op te halen. In dit geval is er maar één menu-item dus kan worden volstaan met het resultaat ‘Align’.

De executeverb method zal worden aangeroepen voor de in het pop-up menu geselecteerde menu-item. In dit geval is er maar één mogelijke actie, dus kan altijd de align worden uitgevoerd.

Voorwaarden voor uitlijnen

Het is eenvoudig om de wincontrols uit te lijnen op basis van de tabvolgorde. Het maken van een lijst van wincontrols, het sorteren van deze lijst op tabvolgorde en de contols in de lijst en het links uitlijnen is voldoende om dat gedaan te krijgen. De moeilijkheid zit hem in de labels voor de controls. Hoe komt de component editor te weten welk label bij welk control hoort zodat deze mee uitgelijnd kan worden?

De oplossing ligt in de focuscontrol property van het label. Indien een label wordt aangemaakt om voor (of boven) een control te plaatsen dan is het vestandig om in de focuscontrol property van het label in te vullen voor welk control het label van toepassing is. Indien in de caption van het label namelijk een shortcut verwerkt wordt, dan zorgt de focuscontrol property ervoor dat bij het intoetsen van de shortcut de focus naar het overeenkomstige control gezet wordt. Veel programmeurs gebruiken deze property niet en plaatsen maar wat labels voor controls, maar het is aan te raden gebruik te maken van deze functionaliteit. Sterker nog: om het uitlijnen te laten werken is het zelfs noodzakelijk om hiervan gebruik te maken.

Uitlijnen

Door voor ieder item in de lijst van uit te lijnen wincontrols van de container te kijken welk label er via de focuscontrol property gekoppeld is, is het mogelijk om parallel aan de lijst van wincontrols een lijst van labels samen te stellen. Zodra beide lijsten bekend zijn is het eenvoudig om de positie van de labels te bepalen en de controls achter de labels te plaatsen.

Het package waarin de broncode staat voor de simpele versie van de component editor kan worden geïnstalleerd net als ieder ander designtime package. Het is mogelijk om de component editor te registreren voor ieder container die afgeleidt is van TWinControl.

Selectief uitlijnen

Er zijn natuurlijk gevallen waarin er wincontrols in de container staan waar geen uitlijning op gewenst is. Als deze controls al juist uitgelijnd staan (bijvoorbeeld een set buttons onderaan het scherm) dan moet het mogelijk zijn om deze buiten beschouwing te laten bij het uitlijnen. Om dit te kunnen bewerkstelligen kan er een selectiescherm voor het daadwerkelijke uitlijnen worden getoond waarin de gebruiker kan aangeven welke wincontrols dienen te worden uitgelijnd.

Het package waarin de broncode staat voor deze versie van de component editor kan worden geïnstalleerd net als ieder ander designtime package.

Conclusie

Het is duidelijk dat de Delphi open tools api op eenvoudige wijze toestaat handige IDE extensies te maken die het leven vereenvoudigen. En het mooiste is: alles kan worden geprogrammeerd in de bekende Delphi syntax. De align component editor is een voorbeeld hiervan dat menige ontwikkelaar zal aanspreken vanwege zijn eenvoud en toepasbaarheid.