Shared Codebase - TColor vs TAlphaColor (VCL vs FMX)

I have some code that is currently used in several VCL projects and I’d like to reuse it in an FMX application. The first issue I’ve found is the difference in naming of colour constants and types.

In the VCL, there is TColor and clBlack, clWhite, clNone, etc.

In FMX, these are TAlphaColor, claBlack, claWhite, claNull, etc.

Is there a way I can keep the code common to VCL and FMX projects?

Would conditional defines that detect the application type be possible?

Is there a way to alias the types and constants with just a single conditional define block?

I am not aware of a way to do this that would feel natural. The best suggestion I can give you is to create your own enumeration, have a base abstract class that translates across enumerations, like so:

Type
  TCrossPlatColour<TDestColour> = abstract class
  public
     function ToColour(Origin: TMyColourEnum) : TDestColour;virtual;abstract;
  end;

Then you will have two units, one per framework, which implements the “correct” enumeration, like so:

Type
    TMyVCLColours = class( TCrossPlatColour<TColor> )
    strict private
       FColourMap: array[TMyColurEnum]: TColor;
    public
      procedure AfterConstruction;override; // Fill the array here!
      function ToColour(Origin: TMyColourEnum): TColor;override;
    end;

Now you have a way to decode the right colour. All you need to do now is to use this class to convert across, because you’ll hardly ever need the other way around and - if you ever do - you can use a dictionary. In order to simplify the usage, you could create this class as soon as possible and use a function to access it. Because the base class is abstract, code users won’t be able to create it and you can create the descendants in the implementation section, therefore removing the ability to instantiate them. You can, if you want, even use a const array if you’d rather, which will give you more compiler checks in case you add some but do not complete the mapping.

Also, because you’re using a custom enumeration, you can use literals such as “colClientDisabled”, for example, which is much more meaningful than “clGrey”.

Cheers!

Also, if you’re using Alexandria 11.1 or 11.2 you can also very easily have a common, outer-facing unit that does something like this:

// Unit declaration cut because of good reasons
uses
  System.Classes, System.Generics.Collections, System.Generics.Defaults,
  {$IF defined(FRAMEWORK_VCL)}
    VCL.Controls, Vcl.Forms
  {$ELSE}
    FMX.Objects, FMX.Forms, FMX.Controls
  {$ENDIF};
// Other stuff cut because of good reasons, I ain't a snitch!
Type

  TXPControl = {$IF defined(FRAMEWORK_VCL)} TWinControl {$ELSE} TControl{$ENDIF};

This is done to allow different control classes, but it works equally well for other class types.
Apparently I cannot codify this but oh well, you get the gist anyway, right?

1 Like

Thanks for your replies Andrea.

Part of it is that I’m still using Delphi XE for one major project, so that makes it even more fun. The uses statement part of it was easily sorted though.

I’ve been working on the unit involved today and have reduced the number of references to colours in it to just a few. Doing a few conditional defines is now looking more reasonable.

Andrea, Thanks for your good ideas on how to make a class work with different platforms.

I will be giving a talk at IT DevCon towards sharing VCL and FMX code.

1 Like

That certainly sounds good.