Home > CSS, Delphi, iScroll, JavaScript, Object Pascal, OP4JS, Smart Mobile Studio > CSS Builder Class, Smart Syntax

CSS Builder Class, Smart Syntax

In my last post I demonstrated just how flexible CSS can be when you start to automate it. Well, if you want to play around with this NOW as opposed to waiting for the next update, I have written down directions to do so here.

First you need the CSS class. This can be found in the QTX library which is hosted on Google Code (Click here to view the repository). Note: You dont need the whole library, just download the unit in the link and rename it and you’re good. Save the files in the libraries folder of Smart Mobile Studio (see the start button registration for Smart, there is a link to the RTL and Library folder).

Generating cool effects is now super easy

Generating cool effects is now super easy

Right. With the CSS class in your possession you now need the builder class. This is essentially a small class with keywords that, when used, generate CSS code according to the functions and when they are used. So if you do something wrong the CSS will come out wrong.

Here is the generator class so far:

unit darth.stylesheet;

//#############################################################################
//
//  DARTH COMPONENTS
//
//  Author:     Jon Lennart Aasenden
//  Copyright:  Jon Lennart Aasenden, all rights reserved
//
//#############################################################################

interface

uses
  System.Types,
  System.Colors,
  SmartCL.System;

type

  TDarthStyleSheet = Class(TObject)
  private
    FHandle:    THandle;
  protected
    function    getSheet:THandle;
    function    getRules:THandle;
    function    getCount:Integer;
    function    getItem(index:Integer):String;
  public
    Property    Sheet:THandle read getSheet;
    Property    Handle:THandle read FHandle;
    function    Add(aName:String;const aRules:String):String;overload;
    Procedure   Add(const aRuleText:String);overload;

    Property    Count:Integer read getCount;
    Property    Items[index:Integer]:String
                read getItem;

    class procedure addClassToElement(const aElement:THandle;const aName:String);
    class procedure removeClassFromElement(const aElement:THandle;const aName:String);
    class function  findClassInElement(const aElement:THandle;const aName:String):Boolean;

    Constructor Create;virtual;
    Destructor  Destroy;Override;
  End;


  TSuperStyle = static class
  public
    class function  EdgeRound(Size:Integer):String;overload;
    class function  EdgeRound(topleftP,toprightP,bottomleftP,
                    bottomrightP:Integer):String;overload;
    class function  EdgeTopaz:String;
    class function  EdgeAngaro:String;

    class function  AnimGlow(aFrom,aTo:TColor):String;

    class procedure AnimStart(Handle:TControlHandle;animName:String);

  end;


implementation

var
_sheet: TDarthStyleSheet;

function getStyleSheet:TDarthStyleSheet;
begin
  if _sheet=NIL then
  _sheet:=TDarthStyleSheet.Create;
  result:=_sheet;
end;

class procedure TSuperStyle.AnimStart(Handle:TControlHandle;animName:String);
begin
  animName:=animName.trim;
  if  (animname.length>0)
  and (handle) then
  begin
    Handle.style['-webkit-animation-name']:=animName;
    Handle.style['-webkit-animation-duration']:='2s';
    Handle.style['-webkit-animation-iteration-count']:='infinite';
  end;
end;

  type
  TCSS = class
  public
    Property  Text:String;
    function  KeyFrames:TCSS;
    function  From:TCSS;
    function  &To:TCSS;
    function  Enter:TCSS;
    function  Leave:TCSS;
    function  PercentOf(Value:Integer):TCSS;
    function  IntOf(Value:Integer):TCSS;
    function  ColorOf(Value:TColor):TCSS;
    function  &inc(Value:String):TCSS;
    function  CRLF:TCSS;
    function  OpenParam:TCSS;
    function  CloseParam:TCSS;
    function  Background:TCSS;
    function  BoxShadow(Left,Top,Right,Bottom:Integer):TCSS;overload;
    function  BoxShadow(Left,Top,Right,Bottom:Integer;
              Color:TColor):TCSS;overload;

    function  LinearGradientV(aFrom,aTo:TColor):TCSS;
    function  LinearGradientH(aFrom,aTo:TColor):TCSS;
    function  LinearGradientTL(aFrom,aTo:TColor):TCSS;
    function  LinearGradientTR(aFrom,aTo:TColor):TCSS;

    Function  BeginComplexGradient(Angle:Integer):TCSS;
    function  EndComplexGradient:TCSS;

    function  ColorPercent(PercentOf:Integer;Color:TColor):TCSS;

    function  LinearGradientAngle(aFrom,aTo:TColor;
              Const Angle:Float):TCSS;overload;

    function  LinearGradientAngle(Colors:Array of TColor;
              Const Angle:Float):TCSS;overload;

    function  AnimationName(name:String):TCSS;
    function  AnimationDuration(Secs,MSecs:Integer):TCSS;
    function  AnimationInfinite:TCSS;
  end;

function TCSS.KeyFrames:TCSS;
begin
  Text:=Text + '@-webkit-keyframes ';
  result:=self;
end;

function TCSS.CRLF:TCSS;
begin
  Text:=Text + #13;
  result:=self;
end;

function  TCSS.OpenParam:TCSS;
begin
  Text:=Text + ' (';
  result:=self;
end;

function  TCSS.CloseParam:TCSS;
begin
  Text:=Text + ') ';
  result:=self;
end;

function TCSS.&inc(Value:String):TCSS;
begin
  Text:=Text + value;
  result:=self;
end;

function TCSS.PercentOf(Value:Integer):TCSS;
begin
  Text:=Text + TInteger.EnsureRange(Value,0,100).toString + '% ';
  result:=self;
end;

function TCSS.IntOf(Value:Integer):TCSS;
begin
  Text:=Text + Value.toString + ' ';
  result:=self;
end;

function TCSS.ColorOf(Value:TColor):TCSS;
begin
  Text:=Text + ColorToStr(Value) + ' ';
  result:=self;
end;

function  TCSS.Enter:TCSS;
begin
  Text:=Text + '{' + #13#10;
  result:=self;
end;

function  TCSS.Leave:TCSS;
begin
  Text:=Text + '}' + #13#10;
  result:=self;
end;

function TCSS.From:TCSS;
begin
  Text:=Text + 'from ';
  result:=self;
end;

function TCSS.&To:TCSS;
begin
  Text:=Text + 'to ';
  result:=self;
end;

function TCSS.BoxShadow(Left,Top,Right,Bottom:Integer):TCSS;
begin
  Text:=Text + '-webkit-box-shadow: '
    + TInteger.ToPxStr(Left) + ' '
    + TInteger.ToPxStr(Top) + ' '
    + TInteger.ToPxStr(Right) + ' '
    + TInteger.ToPxStr(Bottom) + ';';
  result:=self;
end;

function TCSS.BoxShadow(Left,Top,Right,Bottom:Integer;Color:TColor):TCSS;
begin
  Text:=Text + '-webkit-box-shadow: '
    + TInteger.ToPxStr(Left) + ' '
    + TInteger.ToPxStr(Top) + ' '
    + TInteger.ToPxStr(Right) + ' '
    + TInteger.ToPxStr(Bottom) + ' '
    + ColorToWebStr(Color) + '; ';
  result:=self;
end;

function TCSS.Background:TCSS;
begin
  Text:=Text +'background:';
  result:=Self;
end;

function  TCSS.LinearGradientV(aFrom,aTo:TColor):TCSS;
begin
  Text:=Text + '-webkit-linear-gradient('
  + ColorToWebStr(aFrom) + ',' + ColorToWebStr(aTo) + ');';
  result:=Self;
end;

function  TCSS.LinearGradientH(aFrom,aTo:TColor):TCSS;
begin
  Text:=Text + '-webkit-linear-gradient(left,'
  + ColorToWebStr(aFrom) + ',' + ColorToWebStr(aTo) + ');';
  result:=Self;
end;

function TCSS.LinearGradientTL(aFrom,aTo:TColor):TCSS;
begin
  Text:=Text + '-webkit-linear-gradient(left top,'
  + ColorToWebStr(aFrom) + ',' + ColorToWebStr(aTo) + ');';
  result:=Self;
end;

function TCSS.LinearGradientTR(aFrom,aTo:TColor):TCSS;
begin
  Text:=Text + '-webkit-linear-gradient(right top,'
  + ColorToWebStr(aFrom) + ',' + ColorToWebStr(aTo) + ');';
  result:=Self;
end;

function TCSS.LinearGradientAngle(aFrom,aTo:TColor;Const Angle:Float):TCSS;
begin
  Text:=Text + '-webkit-linear-gradient(' + FloatToStr(Angle)
    + 'deg, ' + ColorToWebStr(aFrom) + ',' + ColorToWebStr(aTo) + ');';
  result:=Self;
end;

function TCSS.LinearGradientAngle(Colors:Array of TColor;Const Angle:Float):TCSS;
var
  x:  Integer;
begin
  Text:=Text + '-webkit-linear-gradient('
  + FloatToStr(Angle) + 'deg, ';
  for x:=0 to Colors.length-1 do
  begin
    Text:=Text + ColorToWebStr(Colors[x]);
    if x<Colors.length-1 then
    Text:=Text + ',';
  end;
  Text:=Text + ');';
  result:=Self;
end;

Function TCSS.BeginComplexGradient(Angle:Integer):TCSS;
begin
  Text:=Text + '-webkit-linear-gradient(' + FloatToStr(Angle) + 'deg,';
  result:=self;
end;

function TCSS.EndComplexGradient:TCSS;
begin
  Text:=Text + ');';
  result:=self;
end;

function TCSS.ColorPercent(PercentOf:Integer;Color:TColor):TCSS;
begin
  if Text[length(text)]='%' then
  Text:=Text + ',';

  Text:=Text + ColorToWebStr(Color) +  ' '
  + TInteger.EnsureRange(PercentOf,0,100).toString + '%';
  result:=self;
end;

function TCSS.AnimationName(name:String):TCSS;
begin
  Text:=Text + '-webkit-animation-name: ' + Name + ';' + #13#10;
  result:=self;
end;

function TCSS.AnimationDuration(Secs,MSecs:Integer):TCSS;
begin
  Text:=text + '-webkit-animation-duration: ' + IntToStr(Secs)
    + '.' + IntToStr(mSecs) + 's;' + #13#10;
  result:=self;
end;

function TCSS.AnimationInfinite:TCSS;
begin
  Text:=Text + '-webkit-animation-iteration-count: infinite;' + #13#10;
  result:=Self;
end;

class function  TSuperStyle.AnimGlow(aFrom,aTo:TColor):String;
var
  mStyles:  TDarthStyleSheet;
  mWriter:  TCSS;
begin
  mStyles:=getStyleSheet;
  if mStyles<>NIL then
  Begin
    result:=w3_GetUniqueObjId;
    mWriter:=TCSS.Create;
    try
      mWriter
        .KeyFrames.inc(result + ' ')
        .Enter
          .from
          .enter.BoxShadow(0,0,0,0).leave.CRLF

          .PercentOf(50)
          .Enter.boxShadow(0,0,12,0,aTo).leave.CRLF

          .to
          .enter.BoxShadow(0,0,0,0).leave.CRLF
        .Leave .CRLF;

        writeln(mWriter.text);
        mStyles.Add(mWriter.Text);
        mWriter.text:='';

        mWriter
          .inc('.' + result + '_player ')
          .enter
            .AnimationName(result)
            .AnimationDuration(2,0)
            .AnimationInfinite
          .leave;
        mStyles.add(mWriter.text);
    finally
      mWriter.free;
    end;
    result:=result + '_player';
  end;
end;

class function TSuperStyle.EdgeRound(topleftP,toprightP,bottomleftP,
      bottomrightP:Integer):String;
var
  mStyles:  TDarthStyleSheet;
begin
  mStyles:=getStyleSheet;
  if mStyles<>NIL then
  Begin
    var mText:='border-radius:';
    if topleftP>0 then
    mText := mText + TInteger.ToPxStr(TInteger.ensureRange(topLeftP,0,100));

    if toprightP>0 then
    mText := mText + ' ' + TInteger.ToPxStr(TInteger.ensureRange(toprightP,0,100));

    if bottomRightP>0 then
    mText := mText + ' ' +TInteger.ToPxStr(TInteger.ensureRange(bottomRightP,0,100));

    if bottomLeftP>0 then
    mText := mText + ' ' +TInteger.ToPxStr(TInteger.ensureRange(bottomLeftP,0,100)) + ';';

    result:=mStyles.Add(result,mText);
  end;
end;

class function TSuperStyle.EdgeRound(Size:Integer):String;
var
  mStyles:  TDarthStyleSheet;
begin
  mStyles:=getStyleSheet;
  if mStyles<>NIL then
  result:=mStyles.Add(result,'border-radius: ' + TInteger.ToPxStr(Size));
end;

class function TSuperStyle.EdgeTopaz:String;
begin
  result:=TSuperStyle.EdgeRound(15,50,0,0);
end;

class function TSuperStyle.EdgeAngaro:String;
begin
  result:=TSuperStyle.EdgeRound(15,50,30,0);
end;

//############################################################################
// TDarthStyleSheet
//############################################################################

Constructor TDarthStyleSheet.Create;
var
  mDocument: THandle;
begin
  inherited Create;
  mDocument:=BrowserAPI.document;
  FHandle:=mDocument.createElement('style');
  FHandle.type := 'text/css';
  mDocument.getElementsByTagName('head')[0].appendChild(FHandle);
end;

Destructor TDarthStyleSheet.Destroy;
Begin
  if (FHandle) then
  FHandle.parentNode.removeChild(FHandle);
  FHandle:=null;
  Inherited;
end;

function TDarthStyleSheet.getCount:Integer;
Begin
  if (FHandle) then
  result:=getRules.length else
  result:=0;
end;

function TDarthStyleSheet.getItem(index:Integer):String;
Begin
  if (FHandle) then
  result:=getRules[index].cssText
end;

(* Takes height for differences between webkit, moz and IE *)
function TDarthStyleSheet.getRules:THandle;
var
  xRef: THandle;
Begin
  if (FHandle) then
  begin
    xRef:=getSheet;
    asm
      @result = (@xRef).cssRules || (@xRef).rules;
    end;
  end;
end;

(* Takes height for differences between webkit, moz and IE *)
function TDarthStyleSheet.getSheet:THandle;
var
  xRef: THandle;
Begin
  if (FHandle) then
  begin
    xRef:=FHandle;
    asm
      @result = (@xRef).styleSheet || (@xRef).sheet;
    end;
  end;
end;

class procedure TDarthStyleSheet.addClassToElement(const aElement:THandle;
      const aName:String);
Begin
 w3_AddClass( aElement,aName);
end;

class procedure TDarthStyleSheet.removeClassFromElement(const aElement:THandle;
      const aName:String);
Begin
  w3_RemoveClass(aElement,aName);
end;

class function  TDarthStyleSheet.findClassInElement(const aElement:THandle;
      const aName:String):Boolean;
Begin
  result:=w3_hasClass(aElement,aName);
end;

Procedure TDarthStyleSheet.Add(const aRuleText:String);
var
  mDocument: THandle;
  mSheet: THandle;
Begin
  mDocument:=BrowserAPI.document;
  if (FHandle) then
  Begin
    mSheet:=getSheet;
    if not (mSheet.insertRule) then
    mSheet.addRule(aRuleText) else
    mSheet.insertRule(aRuleText,0);
  end;
end;

function TDarthStyleSheet.Add(aName:String;const aRules:String):String;
var
  mDocument: THandle;
  mSheet: THandle;
Begin
  aName:=trim(aName);
  if length(aName)=0 then
  aName:=w3_GetUniqueObjId;

  mDocument:=BrowserAPI.document;
  if (FHandle) then
  Begin
    mSheet:=getSheet;
    if not (mSheet.insertRule) then
    mSheet.addRule('.' + aName,aRules) else
    mSheet.insertRule('.' + aName + '{' + aRules + '}',0);
  end;

  result:=aName;
end;

end.

Now inside the AnimGlow function in TSuperClass, you will notice that we have pretty much gone overboard with linear-gradients. Just ignore this for now, we will be dealing with each section one by one.

HTML5 valentines card

HTML5 valentines card, pure animated CSS

[coded by me back in 2012 to my GF]

But first, let’s get to know the commands of TCC. We start with the simple 1:1 type commands and leave the more advanced for later.

TCSS functions

  • Enter = {
  • Leave = }
  • To = to
  • From = from
  • PercentOf(Value) = Value%
  • IntOf(Value) = Value
  • ColorOf(Color) = $hex-color-value
  • inc(string) = add plain text to buffer
  • CRLF = carriage return and linefeed (#13#10, or just #13)
  • OpenParam = (
  • CloseParam = )
  • ColorPercent(Value,Color) = $hex-color-value %value,

The above functions map to simple CSS constructs. For instance, to generate a small JSON object you would write:

 .enter .inc(&amp;quot;value = 12;&amp;quot;) .leave 

And if you check the “text” property you find this:

  {
    value = 12;
  }

These types of functions are not so much time helpers as they are space helpers. It’s the other, high level functions which greatly simplify css generation.

For instance, a complex multi-colored, percentage-divided angular linear-gradient (phew!) can now be reduced to this:

.beginComplexGradient(80)
  .ColorPercent(10,clRed)
  .colorPercent(40,clBlue)
  .colorPercent(50,clWhite)
.endComplexGradient

This results in a variation of this (more compact):

background: -webkit-linear-gradient
   (
     80deg,
     #ff0000 10%,
     #0000ff 40%,
     #ffffff 50%
   );

When you start to build ever more complex shapes (this class is not yet done, not by a longshot) the generated CSS will become more and more complex, meaning that you save more and more time and headache; yet your code remain easy to read and maintain. At least once you know the basics of the class and what is going on.

Odd syntax

If you think the syntax is a bit odd then yes, we are using a fairly modern tecnique – where all functions return a reference to its “self”. This means you can call function after function in the same object without line-break.

At the same time we add data to an internal property (in this case cleverly called “text”). When all is done and ready we collect the output from Text and voila — we have our magic CSS style(s) and animations.

I will keep you all updated on this development. It will not be included in the next update of Smart Mobile Studio, because — well, it’s just a little snippet I’ve been playing with for half an hour. But it may make the next update after that, when it’s more mature and advanced.

Until then, play around with it and add more CSS wrappers (!)

Advertisements
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: