Blog

All Blog Posts  |  Next Post  |  Previous Post

Using CSS styles for FNC Components in TMS WEB Core

Tuesday, January 23, 2024

TMS Software Delphi  Components
CSS, or Cascading Style Sheets, is a a de-facto standard in web development, used for defining the presentation of a document written in HTML. Its importance lies in its ability to separate content from design, allowing for greater flexibility and control over the appearance of a website. With CSS, developers and designers can specify styles for text, colors, spacing, layout, and responsive design features, ensuring that web pages look consistent across different browsers and devices. 

TMS FNC (Framework Neutral Components) components from TMS Software are unique due to their cross-framework compatibility, enabling developers to use them across multiple development environments and frameworks including VCL (Visual Component Library for Delphi/C++Builder), FMX (FireMonkey for cross-platform), LCL (Lazarus Component Library), and Web. This versatility means that a single set of components can be used to develop applications for Windows, macOS, iOS, Android, Linux and web platforms, significantly reducing development time and effort. 

Due to this universal character of FNC components, it must be perfectly possible to control the look & feel of the components in VCL, FMX, LCL frameworks where a concept such as CSS does not exist. Typically, this is the classic approach of having color, border, font properties for parts of the control. And with these various properties, it is also possible to control the look & feel of FNC controls in a TMS WEB Core web application.


Bringing CSS in the mix for FNC

Now, as through CSS we can define things as colors, borders, fonts, ... what if CSS information could be used to define the appearance of an FNC control? Well, by interpreting the value of such color, border, font settings from CSS, we can apply it directly to FNC control properties. With some helper functions, this can be easily achieved actually. 


CSS helper classes

We introduce 3 class helpers for 3 essential FNC classes that control appearance: TTMSFNCGraphicsFill, TTMSFNCGraphicsFont & TTMSFNCGraphicsStroke. For these 3 classes, we will write a class helper that can initialize its settings from a CSS class. The classes are defined as:

TTMSFNCGraphicsFillHelper = class helper for TTMSFNCGraphicsFill
  procedure InitFromCSS(cssname: string);
end;

TTMSFNCGraphicsFontHelper = class helper for TTMSFNCGraphicsFont
  procedure InitFromCSS(cssname: string);
end;

TTMSFNCGraphicsStrokeHelper = class helper for TTMSFNCGraphicsStroke
  procedure InitFromCSS(cssname: string);
end;

The implementation of these class helpers is:

{ TTMSFNCGraphicsFillHelper }

procedure TTMSFNCGraphicsFillHelper.InitFromCSS(cssname: string);
var
  atr: TCSSAttributes;
begin
  atr := cssAttributes(cssname);
  Color := atr.bkcolor;
end;

{ TTMSFNCGraphicsFontHelper }

procedure TTMSFNCGraphicsFontHelper.InitFromCSS(cssname: string);
var
  atr: TCSSAttributes;
begin
  atr := cssAttributes(cssname);
  Color := atr.txtcolor;
  Name := atr.fntname;
  Height := -atr.fntsize;
end;

{ TTMSFNCCustomGraphicsStrokeHelper }

procedure TTMSFNCGraphicsStrokeHelper.InitFromCSS(cssname: string);
var
  atr: TCSSAttributes;
begin
  atr := cssAttributes(cssname);
  Color := atr.brdcolor;
  Width := atr.brdwidth;
end;

As you can see, for the fill, we simply get the solid background color from the CSS. For the font we get font color, name and size. And for the borders, we get the border color and the border width.

Getting the CSS values for these parts is done with a helper function cssAttributes() that tries to extract these values from a CSS class and it stores what it can retrieve in a helper record:

  TCSSAttributes = record
    brdcolor: TColor;
    brdwidth: integer;
    bkcolor: TColor;
    txtcolor: TColor;
    fntname: string;
    fntsize: integer;
  end;

The implementation of the function cssAttributes() is:

function cssAttributes(cssclass: string): TCSSAttributes;
var
  dummy: TJSHTMLElement;
  cssstyle: TJSCSSStyleDeclaration;
  bkclr,txtclr,brdclr,fntsz,brdsz: string;
  re: TJSRegexp;
  ja: TStringDynArray;
  r,g,b: integer;
begin
  // Create a dummy element
  dummy := TJSHTMLElement(document.createElement('div'));
  dummy['class'] := cssclass;

  // Append the element to the body to ensure the style is applied
  document.body.appendChild(dummy);

  // Use getComputedStyle to get the computed style values
  cssstyle := window.getComputedStyle(dummy);
  bkclr := cssstyle.getPropertyValue('background-color');
  txtclr := cssstyle.getPropertyValue('color');
  brdclr := cssstyle.getPropertyValue('border-color');

  Result.fntname := cssstyle.getPropertyValue('font-family');

  fntsz := cssstyle.getPropertyValue('font-size');
  fntsz := copy(fntsz, 1, pos('px', fntsz) - 1);

  Result.fntsize := parseInt(fntsz);

  brdsz := cssstyle.getPropertyValue('border-width');
  brdsz := copy(brdsz, 1, pos('px', brdsz) - 1);

  Result.brdwidth := parseInt(brdsz);

  // Remove the dummy element again
  document.body.removeChild(dummy);

  re := TJSRegexp.New('^rgb\((\d+),\s*(\d+),\s*(\d+)\)$');
  ja := re.exec(bkclr);
  r := parseInt(ja[1]);
  g := parseInt(ja[2]);
  b := parseInt(ja[3]);
  Result.bkcolor := RGB(r,g,b);

  ja := re.exec(txtclr);
  r := parseInt(ja[1]);
  g := parseInt(ja[2]);
  b := parseInt(ja[3]);
  Result.txtcolor := RGB(r,g,b);

  ja := re.exec(brdclr);
  r := parseInt(ja[1]);
  g := parseInt(ja[2]);
  b := parseInt(ja[3]);
  Result.brdcolor := RGB(r,g,b);
end;

Basically, this function inserts a dummy DIV element in the DOM, applies the CSS class and then gets the computed style from this element and then throws this element away again. The computed style values are returned as a record of the type TCSSAttributes. 

These values can finally be used to set properties of the FNC classes TTMSFNCGraphicsFill, TTMSFNCGraphicsFont & TTMSFNCGraphicsStroke that control the appearance.



Putting it all together

For a TMS FNC grid, we'd like to use following CSS classes to set the appearance of cells with banding in the grid. 

The CSS we can add for that is for example:

<style>
  .cellfg { background-color: red; color: white; border-width:2px; border-color: blue;}
  .cellbg { background-color: yellow; color: black; border-width: 1px; border-color: silver;}
</style>

and then we extract the FNC control appearance properties from these CSS classes with:

begin
  TMSFNCGrid1.InitSample;

  TMSFNCGrid1.Appearance.NormalLayout.Fill.InitFromCSS('cellfg');
  TMSFNCGrid1.Appearance.NormalLayout.Font.InitFromCSS('cellfg');
  TMSFNCGrid1.Appearance.NormalLayout.Stroke.InitFromCSS('cellfg');

  TMSFNCGrid1.Appearance.SelectedLayout.Font.InitFromCSS('cellfg');
  TMSFNCGrid1.Appearance.FocusedLayout.Font.InitFromCSS('cellfg');

  TMSFNCGrid1.Appearance.BandLayout.Fill.InitFromCSS('cellbg');
  TMSFNCGrid1.Appearance.BandLayout.Font.InitFromCSS('cellbg');
  TMSFNCGrid1.Appearance.BandLayout.Stroke.InitFromCSS('cellbg');
end;
The good news of this approach is that we loose nothing of the flexibility of FNC styling. We can apply this technique for every detail that is configurable in FNC controls. Downside is that it might mean a bit more work writing the CSS classes and the code to get settings from these classes.

The result before and after applying CSS based settings to the TMS FNC Grid is:
TMS Software Delphi  Components

You can play with this yourself if you have the latest TMS WEB Core full or trial release and TMS FNC UI Pack full or trial installed by downloading this sample project here.  


Further considerations

At this moment, these helper classes get basic colors, border widths and font information from the CSS. These helper classes could be extended to also get gradient fill information, border styles from CSS or things like text alignment. Let us know your thoughts and where you would want to see us take this!



Bruno Fierens




This blog post has not received any comments yet.



Add a new comment

You will receive a confirmation mail with a link to validate your comment, please use a valid email address.
All fields are required.



All Blog Posts  |  Next Post  |  Previous Post