Archive

Archive for June, 2014

Writing a Facebook clone with Smart Mobile Studio – part 2

June 30, 2014 Leave a comment
Facebook profile page

Facebook profile page

In the previous installment of this tutorial we focused on displaying posts and creating a custom HTML5 control to represent the message view.

This time we are going to focus on the “home page”, which is the first page you enter when you fire up Facebook on your mobile device. It contains your personal photo in medium size (much larger than the mini-thumbnail that is used on your posts), your name in huge letters, and below that if you have assigned it – your nickname. If you have not defined an alternate name, then there is a blank line there instead. If you look at the picture on your right, you will notice that just beneath the name – there is a blank line. This is the nickname placeholder.

In order to deal with this in a concurrent database-driven fashion we also have to talk a bit about databases and abstraction. The most important task for any distributed application, even if it’s just a two tire – client/server based model, is to abstract data access from data presentation as much as possible.

The forms in your application should have absolutely no clue “how” the data is read or written. It should have a common API (or set of functions and classes) to conform to. By isolating all your database access code inside a single API, you ensure that your application can easily be updated, maintained and even ported to a different platform.

All you have to do is update the db access-layer, and the changes will spread to your entire application. As opposed to hard-coding everything or dropping DB components on every form – which means that your application is married, nuts and bolts, to whatever database you started with.

A few words about data and Smart Pascal

Delphi as a product is probably one of the most data-centric programming environments in existence. Pascal in general has always been the de-facto language for managing huge amounts of data quickly and easily. As a consequence of this – Delphi supports a wide variety of drivers for every conceivable data engines out there. Smart Mobile Studio however, must conform to the realities of the browser.

Cloud database

Cloud database

Writing a database engine in JavaScript is absolutely possible, but none of the traditional storage mechanisms (memory mapped files, disk based look-up tables etc.) required for serious work exists in the world of browsers. nodeJS is a whole different ballgame since under that runtime environment JavaScript have access to pretty much everything a “real” programming language has.

Thankfully the different browsers out there supports a few “lightweight” database engines. WebSQL is by far the best but for some reason it has been marked as obsolete and will disappear from Webkit sometime in the near future. This leaves WebStorage which is a name/value pair database. It’s a bit like TStringList, but inter-linked so that named items can also have child items. So each item is both a named value and a list at the same time.

The greatest obstacle in our case is database-size. By default the browser allows you to create databases in the range of 8-10 megabytes. Which can only be exceeded if you compile with PhoneGap, which removes the size limitation all together.

But that is not really a problem. If you plan to write serious mobile applications you will compile with Phonegap anyway, so you will never encounter the database limitation to begin with. And if you want a serious, portable, non-compiled HTML5 app – then you will handle database storage on your server via DataSnap or RemObjects — so once again, it’s not a problem.

You may also want to have a peek at www.database.com, which is a 100% cloud based data-storage solution used by many JS developers.

In conclusion: The 8-10 megabyte limitation is more than enough for an in-memory record cash, a bit like TClientDataset in Delphi. But using it is optional as you really want to take advantage of cloud-based services or integrate with your Delphi server software.

Either way, since data access is so diverse under HTML5, we haven’t really established a “standard” set of components for it yet. We have of-course written classes to deal with all the databases offered by the browsers, perfectly wrapped in Object Pascal (which gives you a huge advantage) – but there is no TCustomDataset or anything like that in Smart Pascal.

Keep it simple

When creating mobile apps that deals with data, you really want to keep things simple. Connection to your server can be broken at any time, and the “app” can be instantly unloaded from memory if the user clicks the “close” button. So response time and flexibility is more important than rock-solid connections (believe it or not).  Under iOS your app has to be able to shut-down in a matter of 2 seconds, no matter what, or the application will be terminated by force and logged as malfunctioning. So you can forget about letting the user wait until data is transported from X to Y here. You throw the data onto the web and rely on the operative system and socket-layer to do its part.

To seasoned Delphi programmers this sounds like madness. What? Just throw data out there and rely on the JavaScript? For a Delphi or C++ desktop application this like of thinking would be technical suicide. But the alien callback-model of JavaScript is not there out of sloppiness. It’s there because the reality of the browser and how people use mobile technology is completely different from how you use a desktop program.

It has been said that the average user spends 2-12 seconds on a mobile website, and that most games and informative apps are only executed, checked and closed again in the range of 2 to 5 minutes.

So we really don’t want our main-form to block the user while it’s connecting or doing a database login. Mobile users don’t have time for that. We want our application in general to conform to a single API – with clear-cut procedures and functions for dealing with information. “How” we chose to implement that API, be it towards a local database, a web service or a remote dataset — is up to us, but the consumer of this API couldn’t care less. From the consumer (your code in the main-form) point of view, we just “throw” data out there. All the hard work is done behind the scenes in a different callback chain.

Let’s code

Before we start on building the header-coder and navigating between form (in this case, the “new post” form) there is a small trick I want to share with you. Under iOS and Android – have you noticed that the navigation header-bar remains the same? So whenever a window slides into view (or out of view) the header remain fixed?

Unless you want to have a header on each form, which will look very strange and be problematic in terms of code, we have to create a navigation control outside the form, and before any form is created.

The trick is to create a control using Display as the parent (as opposed to Display.View, which is used by forms). When you create a visual control here, it wont be affected by the form effects or position, since it’s outside the scope of the form-container.

So open up the project source (casebook) and rewrite it as such:

unit casebook;

interface

uses
  W3System, W3Components, W3Forms, W3Application,
  W3Header, mainform;

type
  TApplication = class(TW3CustomApplication)
  private
    FForm1: TForm1;
    FHeader:  TW3HeaderControl;
  protected
    procedure ApplicationStarting; override;
    Procedure ApplicationClosing;override;
  public
    Property  Header:TW3HeaderControl read FHeader;
  end;

implementation

{ TApplication}

uses msgform;

procedure TApplication.ApplicationStarting;
var
  mMsgForm: Tmsgform;
begin
  //Create our nav-bar
  Fheader:=TW3HeaderControl.Create(Display);
  FHeader.height:=40;
  FHeader.BackButton.visible:=False;
  FHeader.Title.caption:='CaseBook';

  FForm1 := TForm1.Create(Display.View);
  FForm1.Name := 'Form1';
  RegisterFormInstance(FForm1, True);

  mMsgForm := Tmsgform.Create(Display.View);
  mMsgForm.Name := 'MsgForm';
  RegisterFormInstance(mMsgForm, false);

  inherited;
end;

Procedure TApplication.ApplicationClosing;
Begin
  FHeader.free;
  inherited;
end;

end.

With this code in place you now have an ordinary iOS header fixed on top of your display. It will also auto-resize to the width of the display, because TW3Display auto-sizes and auto-stacks it’s child controls vertically.
Access to this nav-bar is via the normal TApplication instance. Since TApplication is unique for each Smart application, you have to typecast it. And you can alter the events for each form – so that the “back” button always returns you to the mainform. As such:

procedure Tmsgform.FormActivated;
Begin
  TApplication(Application).Header.Title.caption:='New case';
  TApplication(application).Header.Backbutton.Visible:=True;
  TApplication(application).Header.LayoutChildren;
  self.Resize;
  TApplication(application).Header.Backbutton.OnClick:=Procedure(Sender:TObject)
    Begin
      Application.gotoForm('form1',feToLeft);
    end;
end;

Making the header

In the previous installment of this article, we introduced out own scrolling controls. It is a very simple control with a container that can be sized and scrolled. There is also a scroll-indicator that moves and fades in/out as you move around. So what we do is place our header control on the container, same as our posts, and you can move up and down automatically. First, let’s setup our header class:

unit msgutils;

interface

uses w3mousecapture, W3System, W3Graphics, W3Components, w3image,
    w3label, w3toolbar,
    w3Scroll, w3image;

type

TCaseBookDisplayItem = Class(TW3CustomControl)
End;

TCaseBookHeader = Class(TCaseBookDisplayItem)
private
  FImage:   TW3Image;
  FLabel:   TW3Label;
protected
  procedure InitializeObject; override;
  procedure FinalizeObject; override;
  procedure Resize;Override;
public
  Property  Image:TW3Image read FImage;
  Property  Title:TW3Label read FLabel;
End;

TCaseBookNavBar = Class(TCaseBookDisplayItem)
End;

TCaseBookMsgBox = Class(TCaseBookDisplayItem)
private
  FGlyph:   TW3Image;
  FCaption: TW3Label;
  FDesc:    TW3Label;
  FBar:     TW3Toolbar;
protected
  procedure Resize;Override;
  procedure InitializeObject; override;
  procedure FinalizeObject; override;
public
  Property  Title:TW3Label read FCaption;
  Property  Description:TW3Label read FDesc;
  property  Glyph:TW3Image read FGlyph;
  Property  Toolbar:TW3Toolbar read FBar;
End;

TCaseBookContent = Class(TW3ScrollContent)
public
  Procedure Cancel;
End;

TCaseBookMsgList = Class(TW3ScrollControl)
protected
    function getScrollContentClass: TW3ScrollContentClass; override;
public
  function  calcContentHeight:Integer;
  function  AddItem(aCaption,aDescription:String):TCaseBookMsgBox;
  Procedure ClearChildren;
End;

implementation

uses gfxpics;

(* Some constants to simplify layout of case-items *)
const
  CNT_IMG_SIZE  = 24;
  CNT_BAR_SIZE  = 26;
  CNT_SPACE     = 2;

//#############################################################################
// TCaseBookMsgList
//#############################################################################

procedure TCaseBookHeader.InitializeObject;
Begin
  inherited;
  FImage:=TW3Image.Create(self);
  FImage.Background.FromURL(CNT_IMAGEDATA);
  FImage.StyleClass:='';

  FLabel:=TW3Label.Create(self);
  FLabel.font.name:='Verdana';
  FLabel.font.size:=18;
  FLabel.Caption:='Anonymous von Sparta';
  FLabel.Font.Color:=clWhite;

  w3_setstyle(FImage.handle,'border','solid white 0.2em');
  w3_setstyle(FImage.handle,'border-radius','5px');
  w3_setstyle(FImage.handle,'box-shadow','5px 5px rgba(0,0,0,0.2)');
  w3_setStyle(FImage.Handle,'backgroundSize','100% auto');
  w3_setStyle(FImage.Handle,'backgroundRepeat','round round');
  w3_setStyle(FImage.Handle,'background-position','center center');
  w3_setStyle(FImage.Handle,'background-color','RGB(255,255,255)');
end;

procedure TCaseBookHeader.FinalizeObject;
Begin
  FImage.free;
  FLabel.free;
  inherited;
end;

procedure TCaseBookHeader.Resize;
var
  mRect:  TRect;
  dx: Integer;
begin
  inherited;

  if assigned(FImage)
  and assigned(FLabel) then
  begin

    FImage.SetSize(80, 84);
    FImage.MoveTo(16,clientHeight-(16 + FImage.Height));

    dx:=FImage.BoundsRect.right + 8;
    FLabel.SetBounds(FImage.boundsRect.right + 8,
    FImage.top,ClientWidth-(dx + 16), 22);
  end;
end;

//#############################################################################
// TCaseBookContent
//#############################################################################

Procedure TCaseBookContent.Cancel;
Begin
  self.MoveEnds(0,0);
  self.ReleaseCapture;
end;

//#############################################################################
// TCaseBookMsgList
//#############################################################################

Procedure TCaseBookMsgList.ClearChildren;
var
  x: Integer;
  mChildren: TW3ComponentArray;
begin
  Content.BeginUpdate;
  try
    mChildren:=Content.GetChildrenSortedByYPos;
    if mChildren.Length>0 then
    begin
      for x:=mChildren.low to mChildren.high do
      mChildren[x].free;
    end;
  finally
    Content.height:=0;
    Content.EndUpdate;
  end;
end;

function TCaseBookMsgList.getScrollContentClass: TW3ScrollContentClass;
begin
  result:=TCaseBookContent;
end;

function  TCaseBookMsgList.AddItem
          (aCaption,aDescription:String):TCaseBookMsgBox;
var
  dy: Integer;
Begin
  (* create our "news item" control *)
  result:=TCaseBookMsgBox.Create(self.Content);
  result.Color:=clWhite;

  (* set properties for title and text *)
  result.Title.Caption:=aCaption;
  result.Description.Caption:=aDescription;

  (* calculate the height of all case-items being displayed *)
  dy:=calcContentHeight;

  (* position our "news item" at the bottom of the content page *)
  result.SetBounds(CNT_SPACE,dy,clientwidth-4,100);

  (* re-size the content to include the newly created control *)
  Content.Height:=dy + 100 + CNT_SPACE;
end;

function TCaseBookMsgList.calcContentHeight:Integer;
var
  dy: Integer;
  y:  Integer;
  mChildren:  TW3ComponentArray;
begin
  (* Look through all child controls of our content page
     and sum up the size of each + spacing *)
  dy:=0;
  mChildren:=Content.GetChildrenSortedByYPos;
  if mChildren.Length>0 then
  begin
    for y:=mChildren.Low to mChildren.High do
    Begin
      if (mChildren[y] is TCaseBookDisplayItem) then
      begin
        inc(dy,TW3CustomControl(mChildren[y]).Height);
        inc(dy,CNT_SPACE);
      end;
    end;
  end;
  result:=dy;
end;

//#############################################################################
// TCaseBookMsgBox
//#############################################################################

procedure TCaseBookMsgBox.InitializeObject;
Begin
  inherited;
  (* create our glyph image *)
  FGlyph:=TW3Image.create(self);
  FGlyph.setSize(CNT_IMG_SIZE,CNT_IMG_SIZE);

  FCaption:=TW3Label.Create(self);
  //FCaption.Background.FromColor(clRed);
  FCaption.Font.Name:='Helvetica';
  FCaption.Font.Size:=16;
  FCaption.StyleClass:='';

  FDesc:=TW3Label.Create(self);
  FDesc.Font.Name:='Helvetica';
  FDesc.Font.Size:=12;
  //FDesc.Background.FromColor(clCyan);
  FDesc.StyleClass:='';

  FBar:=TW3Toolbar.Create(self);
  FBar.Background.FromColor(rgbToColor(230,230,230));
  FBar.StyleClass:='';
end;

procedure TCaseBookMsgBox.FinalizeObject;
Begin
  FGlyph.free;
  FCaption.free;
  FDesc.free;
  FBar.free;
  inherited;
end;

procedure TCaseBookMsgBox.Resize;
var
  dx,dy: Integer;
Begin
  inherited;
  (* Resize can be called by the browser before the
     JS prototype is complete. So we always check
     all control references before we use them *)
  if assigned(FGlyph)
  and assigned(FCaption)
  and assigned(FBar)
  and assigned(FDesc) then
  Begin
    FGlyph.SetBounds(CNT_SPACE,
      CNT_SPACE,
      CNT_IMG_SIZE + CNT_SPACE,
      CNT_IMG_SIZE + CNT_SPACE);

    dx:=FGlyph.BoundsRect.right + CNT_SPACE;
    FCaption.SetBounds(dx,
      CNT_SPACE,
      clientwidth-(dx + CNT_SPACE) ,
      CNT_IMG_SIZE + CNT_SPACE );

    dy:=clientheight - FCaption.BoundsRect.Bottom;
    dec(dy,CNT_BAR_SIZE);
    dec(dy,CNT_SPACE * 3);

    FDesc.SetBounds(CNT_SPACE,FCaption.boundsrect.bottom + CNT_SPACE,
      clientWidth-(CNT_SPACE * 2),
      dy);

    dy:=FDesc.boundsrect.bottom + CNT_SPACE;
    FBar.setBounds(CNT_SPACE,dy,clientwidth- (2 * CNT_SPACE),
      CNT_BAR_SIZE);

  end;
end;

end.

Voila, now let’s have a look at the result:

Header control in place

Header control in place

Notice the “Post” button? When we click this button we want the form to slide out to the left, and the “new post” form to scroll into view from the right. When this happens, the nav-bar “back button” appears – allowing the user to abort the new post and go back to the main form again.

And naturally — we will style the whole thing in Facebook colors when it’s done – which means altering the ordinary stylesheet that ships with the application 🙂 Simple, effective and very easy to maintain and update.

There is one unit missing from the above, and that is a unit used to store the images in source-form. We want to remove that one in the end, and use “live” images that we ship with the product as resources. But for now we simply use the image to code converter (under the tools menu) to embed the graphics into the application. It looks like this:

unit gfxpics;

interface

uses 
  W3System;

const CNT_BACKGROUND ='data:image/jpeg;base64,'+
'/9j/4AAQSkZJRgABAQAAAQABAAD//gA7Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg' +
'SlBFRyB2NjIpLCBxdWFsaXR5ID0gODAK/9sAQwAGBAUGBQQGBgUGBwcGCAoQCgoJCQoUDg8MEBcU' +
'GBgXFBYWGh0lHxobIxwWFiAsICMmJykqKRkfLTAtKDAlKCko/9sAQwEHBwcKCAoTCgoTKBoWGigo' +
'KCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgo/8AAEQgB4AFA' +
'AwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMF' +
'BQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkq' +
'NDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqi' +
'o6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/E' +
'AB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMR' +
'BAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVG' +
'R0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKz' +
'tLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A' +
'maJTL5uxQ+wISO4H+c1b0mW30+WaU2MVxJIyvl3IAI9hwQe/0pm32o28iu654HKZ+pXkNiivJGS0' +
'j7I4YV5djztUdh+gqlHeWeqGbTb21mhkkQhre5THmL32kcH8DWk1j52rx3LDKxQFUB/vM3J/IUvi' +
'TTWNtYfu3817hJLZwpA+U/MwPoBkfjRcOWxP4Kt7fRbqWLVHnvtLCbbeAjMkR9C2RuX0707xJbab' +
'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' +
'Jupvzeh/Kk+bPRvyoA//2Q=='
;

const CNT_IMAGEDATA ='data:image/png;base64,'+
'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' +
'AOOXnwtR0U65AAAAAElFTkSuQmCC'
;
implementation


end.

Until the next time, when we start adding some DB code for our posts 🙂

Discount of the year! Get Smart Mobile Studio PRO for $99

June 26, 2014 1 comment

Yes you read right ! By ordering Smart Mobile Studio with my codeword – I can give you a massive summer discount! Why? Because I can, that’s why. There are some perks to being the mad scientist that started it all, one of them is that I can give discounts around christmas and summertime. And since the sun is shining and women are running around in a fantastic lack of clothing (I so love living in Norway) – I am offering a nice discount for anyone interested in the professional and enterprise edition of the #1 Object Pascal HTML5 compiler on the marked.

Smart Mobile Studio

Smart Mobile Studio

Ordering is very simple. You do it from the standard Smart Mobile Studio Website, but mark you order with “JLA”.

This discount starts Monday 30’th of June (next monday).

In short you can now obtain the pro edition for $99 (original price $199) and the enterprise edition for $199 (original price $399) — that is a whopping discount for all my fellow spartans out there!

I cut $200 off the enterprise version since, let’s face it — thats the group of coders getting access to the juicy stuff first (just like everywhere else). And that extra $100 is then given back to the PRO buyers via the upgrade plan. So all in all it pans out fair for everyone involved. You either grab the offer or you dont, it’s as easy as that.

We got the next version of the compiler / IDE lined up for release quite soon (you get all updates of-course). Which adds even more features and support than you can throw a stick at.

So if you thought we were going away, think again. 4 successful years of Object Pascal HTML5 development, a globally recognized Pascal dialect – and a rock solid class library which makes HTML5 Builder look like a joke.

Dont expect us to leave any time soon 🙂

Sixpack + Pizza, or an Object Pascal HTML5 compiler suite?

A ridicules but true comparison. Go out for pizza in New york or Oslo, add six pints of beer to that equation and you better be prepared to pay a hell of a lot more than $199.

What would be a better investment in terms of intellectual exploration? Blowing $200 on food and a few hours out, or getting the latest compiler suite and take a step outside your comfort zone? It’s up to you — my discount will only be available for a couple of weeks.

It buys you access to all updates, classes, code and support for one whole year. In the great scheme of things I’m the proverbial santa here, considering that you get a full HTML5/JS development studio and an object pascal compiler for the price of a six-pack and a pizza. I know, it sounds lame but it’s the truth. Take your pick what you think will bring you satisfaction longer.

Awkward parallels aside you also get to support important development, which includes financial support for DWScript and SynEdit. Both fantastic Delphi packages (almost standard) which has benefitted greatly since we started our project. Their evolution is important as more and more developers jump onto the FireMonkey bandwagon. SynEdit is a fantastic free editor component, and we really want to get as much technology onto the Apple platform as possible.

Do a Mike Tyson on the latte crew

Violence perfected, you dont mess with that

Knockout HTML5 with SMS

And secondly, are you tired of latte drinking JavaScript teenagers laughing when they hear “Pascal” or “C++” mentioned? Like they could possibly know what it really takes to build the low-level stuff we do on a daily basis? Well now you can laugh back and write code in 1 hour that would take them 2 months to achieve — in their own language (rubbing it in with an assorted selection of salt from the dead sea). And you get the benefit of OOP, inheritance, polymorphism and everything Object Pascal.

Our Smart Pascal compiler brings the onslaught and full might of our language to the world of JavaScript. The power of 20+ years of carefully crafted programming terminology and skill ready to be unleashed in the browser.

Let them enjoy their latte while they can. In the words of Plato “some are content with Ipse Dixit”, but grown up developers require food meant for adults.

As Delphi developers we don’t get confused when we see cool graphical effects (because we know how easy that is to make). There is more to a well-defined language than being able to throw graphics around. What about financial calculations? Support for leading communication protocols? What about inheritance, interfaces and property expressions? Does these JavaScript “toys” provide this? Or is it just latte instead of real work when it comes down to getting the job done?

JQuery? ExtJS? Well, children have their toys..

And you will be in good company. We got support for RemObjects services out of the box, Embarcadero DataSnap, Synopse is implementing their morMot framework as I type, Steema supplies TeeChart – and (drumroll) quite a few high profiled component vendors have ordered SDK kits to write components for SMS.

You wont believe some of the cool stuff I get in my mailbox coded in SMS. Everything from old Amiga retro games re-made for HTML5 to serious, number crunching services written in SMS and nodeJS. I was proud when some of my old Amiga code ended up at Nasa in the 90’s (MUI plugin), but I have never been more proud than when SMS was used to make Flash look like a raving imbecile. Our HTML5 CSS3 GPU powered sprites ran rings around Adobe’s bloated and hyped technology – which just goes to show: Object Pascal is a living, vibrant and exciting language that is worth learning — for the future.

As long as someone makes a bridge to the future..

JavaScript is the future

JS hardware, an abomination -- but it's what we gotta work with

JS hardware, an abomination — but it’s what we gotta work with

JavaScript is the winning contender for the future. That might sound like hogwash, but check the growth compared to native languages before you object. This is why I created SMS to begin with – so that we could use our Delphi and FPC skills to create kick-ass HTML5 JavaScript applications while we adapt and evolve as programmers. Or just use it as a flash replacement for that matter. It really makes sense in a Delphi programmers toolkit.

You can also enter into the embedded marked, since more and more micro-controllers run JavaScript (believe it or not). Long gone are the days of expensive x86 embedded boards and having to hire a team of 4 coders to build an oil control unit. Cheap ARM based JavaScript hardware is the reality. And Smart Pascal makes it fun and easy to work with — and you only need one good Delphi coder as opposed to 10 Javascript latte drinking teenager to get the job done — and get it done right!

And that is what we Spartans bring to the table. Carefully crafted, rock solid, object oriented and expandable systems. We dont just write code that can be used as a quick fix. We write object oriented code that can be maintained and expanded. Our code is written to last decades, not six months or a phase. And once you get that message through to the board of whatever JS company is involved, they will immediately see the economic reasoning behind hiring a senior Delphi programmer as a opposed to a latte slurping, pimple pushing teenager who wouldn’t know an assembly section if he fell over it.

That gives Delphi developers a huge advantage– if they absorb and adapt to the marked change presently taking place.

Smart Mobile Studio is a bridge from the reality of native programming (which we all know and love), and the emerging reality of cloud oriented, javascript coded, touch contract based nodeJS services. You might not like it any more than me, but that is the reality we have to deal with.

But at least you have a crew of die-hard spartans at the front line paving the way for your arrival. Laying the foundation stone of things to come.

Smart Pascal RTTI Persistence

June 20, 2014 Leave a comment

Updated: For some odd reason, the RTTI option in the last two Smart Mobile Builds have been ignored. Meaning that the compiler doesnt generate the RTTI metadata (except for custom attributes). We are looking into this. It seems to be a very, very small issue where the properties set in the project options is not transfered to the code-generator. The issue will be fixed promptly and in the upcoming 2.2 release.

Quite a few people have asked about RTTI and Smart Mobile Studio, and while the Wikipedia article about Smart Pascal is a great read – it is mistaken regarding lack of RTTI. Smart Pascal has full support for RTTI, allowing you to enumerate properties, methods and extract type information.

In this short article I will introduce a new RTL class, one that I have proposed for inclusion (with some modifications) in the official Smart Pascal RTL. It should be introduced  as the ancestor of TW3TagObj – from which all non visual and visual components derive. This will ensure that all Smart Pascal components can be saved and loaded as you expect.

Another way is to introduce this as helper classes to avoid unwanted dependency injection for apps that doesn’t need RTTI.

As always with RTTI based persistence, only published properties are automatically serialized. I will no doubt add support for more complex storage, much like Delphi’s TFiler architecture, but as a demonstration of RTTI under Smart Pascal this is more than enough.

unit w3persistent;

interface

uses
  W3System;

  type

  EPersistent = Class(EW3Exception);

  IPersistent = Interface
    function  objToString:String;
    procedure objFromString(const aData:String);
    procedure objReset;
  end;

  TPersistent = Class(TObject,IPersistent)
  private
    (* Implements:: IPersistent *)
    function  objToString:String;
    Procedure objFromString(const aData:String);
    procedure objReset;
  protected
    Procedure AssignTo(const aTarget:TPersistent);virtual;
  public
    Procedure Assign(const aSource:TPersistent);virtual;
  End;

  TNamedValuePair = Record
    nvName:   String;
    nvValue:  Variant;
  End;
  TNamedValuePairArray = Array of TNamedValuePair;

  TPersistentHelper = Class helper for TPersistent
  public
    class function  getRTTIProperties(var aPairs:TNamedValuePairArray):Integer;
    class procedure setRTTIProperties(const aPairs:TNamedValuePairArray);
  end;

implementation

resourcestring
CNT_ERR_TPERSISTENT_READ  = 'Persistent read error [%s]';
CNT_ERR_TPERSISTENT_WRITE = 'Persistent write error [%s]';

//#############################################################################
// TPersistentHelper
//#############################################################################

class procedure TPersistentHelper.setRTTIProperties
      (const aPairs:TNamedValuePairArray);
var
  mRTTI:  Array of TRTTIRawAttribute;
  mAttrib:  TRTTIRawAttribute;
  mTypeId:  TRTTITypeInfo;
  x,y:  Integer;
Begin
  if aPairs.length>0 then
  begin
    for y:=aPairs.low to aPairs.high do
    Begin
      mTypeId:=TypeOf(self.classtype);
      mRTTI:=RTTIRawAttributes;
      if mRtti.length>0 then
      Begin
        for x:=mRtti.low to mRtti.high do
        begin
          mAttrib:=mRtti[x];
          if  (mAttrib.T = mTypeId)
          and (mAttrib.A is RTTIPropertyAttribute) then
          begin
            var prop := RTTIPropertyAttribute(mAttrib.A);
            if prop.name = aPairs[y].nvName then
            prop.setter(variant(self),aPairs[y].nvValue);
          end;
        end;
      end;
    end;
  end;
end;

class function TPersistentHelper.getRTTIProperties
         (var aPairs:TNamedValuePairArray):Integer;
var
  mRTTI:  Array of TRTTIRawAttribute;
  mAttrib:  TRTTIRawAttribute;
  mTypeId:  TRTTITypeInfo;
  x:  Integer;
  mPair: TNamedValuePair;
Begin
  aPairs.clear;
  result:=-1;

  mTypeId:=TypeOf(self.classtype);

  mRTTI:=RTTIRawAttributes;
  if mRtti.Length>0 then
  begin
    for x:=mRtti.Low to mRtti.High do
    begin
      mAttrib:=mRtti[x];
      if  (mAttrib.T = mTypeId)
      and (mAttrib.A is RTTIPropertyAttribute) then
      begin
        var prop := RTTIPropertyAttribute(mAttrib.A);
        mPair.nvName:=prop.name;
        mPair.nvValue:=Prop.Getter(Variant(self));
        aPairs.add(mPair);
      end;
    end;
    result:=aPairs.length;
  end;
end;

//#############################################################################
// TPersistent
//#############################################################################

procedure TPersistent.objReset;
var
  mData:  TNamedValuePairArray;
  x:  Integer;
Begin
  if getRTTIProperties(mData)>0 then
  begin
    for x:=mData.low to mData.high do
    mData[x].nvValue:=undefined;
    setRTTIProperties(mData);
  end;
end;

function TPersistent.objToString:String;
var
  mData:  TNamedValuePairArray;
  mCount: Integer;
  x:  Integer;
Begin
  mCount:=getRTTIProperties(mData);
  if mCount>0 then
  begin
    try
      asm
        @Result = JSON.stringify(@mData);
      end;
    finally
      mData.clear;
    end;
  end
end;

Procedure TPersistent.objFromString(const aData:String);
var
  mData:  TNamedValuePairArray;
Begin
  if length(aData)>0 then
  Begin
    asm
      @mData = JSON.parse(@aData);
    end;

    if mData.length>0 then
    Begin
      setRTTIProperties(mData);
      mData.clear;
    end;
  end else
  objReset;
end;

Procedure TPersistent.Assign(const aSource:TPersistent);
Begin
  if aSource<>NIL then
  Begin
    try
      objFromString(aSource.objToString);
    except
      on e: exception do
      Raise EPersistent.CreateFmt(CNT_ERR_TPERSISTENT_READ,[e.message]);
    end;
  end;
end;

procedure TPersistent.AssignTo(const aTarget: TPersistent);
begin
  if aTarget<>NIL then
  begin
    try
      aTarget.objFromString(objToString);
    except
      on e: exception do
      Raise EPersistent.CreateFmt(CNT_ERR_TPERSISTENT_WRITE,[e.message]);
    end;
  end;
end;

end.

Using the system

Using the class is more or less identical to Delphi. Simply derive your class from TPersistent, and all published properties can be transfered via the Assign() and AssignTo() methods. You can also access the serialization methods directly via the IPersistent interface.

Notes

The above is only a rough scetch of the final version. It does not check datatypes before trying to serialize so only use it with ordinal types (string, boolean, word, integer etc). Neither does it check for arrays.

But yes, Smart Pascal supports RTTI

Writing a Facebook clone with Smart Mobile Studio – Part 1

June 18, 2014 2 comments

In this tutorial we are going to explore how to use Smart Mobile Studio to write a small but flattering Facebook clone – cleverly nicknamed CaseBook (Sort of works if you think about it, for a while *smile* ). It will demonstrate just how easy it is to make such applications when you have the right tools.

If this is the first time you check out out Smart Mobile Studio, you will probably need to brush up a bit on what exactly our technology does and what your options are.

A few words about Smart Mobile Studio

Smart Mobile Studio

Smart Mobile Studio

In short, Smart Mobile Studio is an Object Pascal Compiler. Instead of producing machine-code it generates high-quality JavaScript, meaning that you can execute your pascal apps in any HTML5 compliant browser. The system was architected to target mobile devices. It represents a short-cut to delivering “apps”, since using technology like Cordova (Phonegap) allows you to compile HTML5 into real executable code for iOS, Android and blackberry — all in one go.

It comes with a huge collection of ready-made classes and controls, ready to be augmented – extended and used. The RTL (run-time library) was especially architected for mobile devices, but is perfectly suited for full-screen browser experiences as well.

Smart Pascal is also a great Adobe Flash replacement. You can write complex, object-oriented apps, commercials, games or anything you want – and simply embed your program inside  a DIV tag on your website. No dependencies, no plugins or browser extension – and you don’t even need a server to run your apps. They are fully self-sustained JavaScript modules.

If you know Delphi, then Smart Pascal will be like second nature to you. It bridges the rich world of HTML5 and agile JavaScript with the technically superior Object Pascal. The best of both worlds.

If you want to know “how” this is possible (you nerd) then visit wikipedia for the low-down. If you dont care and just want to code – continue reading.

Visit the Smart Mobile Studio website or check out the Wikipedia article for more information.

Setting a goal

Right. Before we start to write code it’s good practice to mentally go through what we want to achieve, and even spend a little time writing it out. Let’s start with a superficial “pow-wow-whatever” of what we are going to build:

  • Write a mobile application inspired by Facebook
  • The application deals with cases rather than news-posts
  • Each case can be commented on
  • Each case is represented as a small post, containing ingress (header image + text)
  • Each case item has a body of text, but is truncated if it covers more than 50% of the display
  • Each case item has a footer with like, comment and other buttons

Now let’s break this into tasks so we can get started:

  • Case items should be touch-scrollable, case display must inherit from TW3ScrollControl
  • Case items are composite, containing sub controls, must inherit from TW3CustomControl
  • The application will need at least 3 different forms:
    • Main form (user profile)
    • Case display form
    • Comment form

If this was a complete, fully functional application we would of-course need more forms, like editing a case, assigning users to a case, security and access rights – and much more. We would also need a web-service and proper database binding. But for our little tutorial the above is enough to get us started and it will demonstrate just how effective Smart Mobile Studio is.

Using Smart Pascal

Ok, start up Smart Mobile Studio and create a new visual project. Feel free to add 2 extra forms to the application (commentform and msgform), then save the project in it’s own folder called “casebook” on your hard disk. Next, add a new unit to the project and call it msgutils.pas and save that as well.

If you start up facebook on your iPhone or Android device, the application starts on your profile. Displaying posts that you yourself have posted earlier, as well as your picture, a header and whatever secondary name you have added (or a sub-title). You also have a slim toolbar with icons for posting a new message, checking personal messages and so on. This form is simply “the main form”.

If you click the “home” button on Facebook, you are brought to a second form which display a scrolling list of messages posted by everyone in your circle. Friends, groups you are a member of, pictures people have posted — depending on the security settings people apply to what they post, these things show up here.

What we need first of all is to be able to scroll up and down a list of messages. For this we will derive our control from the scrolling window class in unit w3scroll.pas. This base-class includes touch sensitivity with a scroll-indicator that fades in and out – so it’s almost perfect.

The class TW3ScrollControl has a property called Content, which is a TW3CustomControl based visual control. You populate this control with your own components, re-size it and you can scroll around it using touch. In our case we want a vertical list of “news” items (case items) so we need a function to measure the total height of whatever content is there already – so we know what position to inject new case-items. To simplify adding case-items we also isolate the construction of these in its function.

Ok, let’s write some code.

unit msgutils;

interface

uses W3System, W3Graphics, W3Components, w3image, w3label, w3toolbar,w3Scroll;

type

TCaseBookMsgBox = Class(TW3CustomControl)
private
  FGlyph:   TW3Image;
  FCaption: TW3Label;
  FDesc:    TW3Label;
  FBar:     TW3Toolbar;
protected
  procedure Resize;Override;
  procedure InitializeObject; override;
  procedure FinalizeObject; override;
public
  Property  Title:TW3Label read FCaption;
  Property  Description:TW3Label read FDesc;
  property  Glyph:TW3Image read FGlyph;
  Property  Toolbar:TW3Toolbar read FBar;
End;

TCaseBookMsgList = Class(TW3ScrollControl)
public
  function  calcContentHeight:Integer;
  function  AddItem(aCaption,aDescription:String):TCaseBookMsgBox;
End;

implementation

(* Some constants to simplify layout of case-items *)
const
  CNT_IMG_SIZE  = 24;
  CNT_BAR_SIZE  = 26;
  CNT_SPACE     = 2;

//#############################################################################
// TCaseBookMsgList
//#############################################################################

function  TCaseBookMsgList.AddItem
          (aCaption,aDescription:String):TCaseBookMsgBox;
var
  dy: Integer;
Begin
  (* create our "news item" control *)
  result:=TCaseBookMsgBox.Create(self.Content);
  result.Color:=clWhite;

  (* set properties for title and text *)
  result.Title.Caption:=aCaption;
  result.Description.Caption:=aDescription;

  (* calculate the height of all case-items being displayed *)
  dy:=calcContentHeight;

  (* position our "news item" at the bottom of the content page *)
  result.SetBounds(CNT_SPACE,dy,clientwidth-4,100);

  (* re-size the content to include the newly created control *)
  Content.Height:=dy + 100 + CNT_SPACE;
end;

function TCaseBookMsgList.calcContentHeight:Integer;
var
  dy: Integer;
  y:  Integer;
  mChildren:  TW3ComponentArray;
begin
  (* Look through all child controls of our content page
     and sum up the size of each + spacing *)
  dy:=0;
  mChildren:=Content.GetChildrenSortedByYPos;
  if mChildren.Length>0 then
  begin
    for y:=mChildren.Low to mChildren.High do
    Begin
      if (mChildren[y] is TCaseBookMsgBox) then
      begin
        inc(dy,TW3CustomControl(mChildren[y]).Height);
        if dy>0 then
        inc(dy,8) else
        inc(dy,CNT_SPACE);
      end;
    end;
  end;
  result:=dy;
end;

//#############################################################################
// TCaseBookMsgBox
//#############################################################################

procedure TCaseBookMsgBox.InitializeObject;
Begin
  inherited;
  (* create our glyph image *)
  FGlyph:=TW3Image.create(self);
  FGlyph.setSize(CNT_IMG_SIZE,CNT_IMG_SIZE);

  FCaption:=TW3Label.Create(self);
  //FCaption.Background.FromColor(clRed);
  FCaption.Font.Name:='Helvetica';
  FCaption.Font.Size:=16;
  FCaption.StyleClass:='';

  FDesc:=TW3Label.Create(self);
  FDesc.Font.Name:='Helvetica';
  //FDesc.Background.FromColor(clCyan);
  FDesc.StyleClass:='';

  FBar:=TW3Toolbar.Create(self);
  FBar.Background.FromColor(rgbToColor(230,230,230));
  FBar.StyleClass:='';
end;

procedure TCaseBookMsgBox.FinalizeObject;
Begin
  FGlyph.free;
  FCaption.free;
  FDesc.free;
  FBar.free;
  inherited;
end;

procedure TCaseBookMsgBox.Resize;
var
  dx,dy: Integer;
Begin
  inherited;
  (* Resize can be called by the browser before the
     JS prototype is complete. So we always check
     all control references before we use them *)
  if assigned(FGlyph)
  and assigned(FCaption)
  and assigned(FBar)
  and assigned(FDesc) then
  Begin
    FGlyph.SetBounds(CNT_SPACE,
      CNT_SPACE,
      CNT_IMG_SIZE + CNT_SPACE,
      CNT_IMG_SIZE + CNT_SPACE);

    dx:=FGlyph.BoundsRect.right + CNT_SPACE;
    FCaption.SetBounds(dx,
      CNT_SPACE,
      clientwidth-(dx + CNT_SPACE) ,
      CNT_IMG_SIZE + CNT_SPACE );

    dy:=clientheight - FCaption.BoundsRect.Bottom;
    dec(dy,CNT_BAR_SIZE);
    dec(dy,CNT_SPACE * 3);

    FDesc.SetBounds(CNT_SPACE,FCaption.boundsrect.bottom + CNT_SPACE,
      clientWidth-(CNT_SPACE * 2),
      dy);

    dy:=FDesc.boundsrect.bottom + CNT_SPACE;
    FBar.setBounds(CNT_SPACE,dy,clientwidth- (2 * CNT_SPACE),
      CNT_BAR_SIZE);

  end;
end;

end.

The class TCaseBookMsgBox is the visual representation of a “news item” (read: case item). Just like on FaceBook when you post something, the news-item has a thumbnail of your profile picture, a short title, your message (either in full or truncated) and a footer. The footer has the “like”, “share” and other buttons on it.

If you look at the code I just wrote you will notice that our item contains child controls which match this.

Having a look at it

Doesnt look like much does it? Yet what we have here is a fully touch-enabled scrolling list which can be CSS3 styled and made to look like the bomb. But before we style this puppy with bling, let’s start with the basics and simply make it visible. So while we work we add this little gem to our main-form until it’s ready to be used by both the main-form and our “news” form.

Casebook first look

Casebook first look

In our main-form I went into the designer and dropped a TW3HeaderControl, then I just adopted the resize method of the form to position the scrolling list. Here is the code so far:

procedure TForm1.InitializeForm;
begin
  inherited;
  FMsgList:=TCaseBookMsgList.Create(self);
  FMsgList.Background.FromColor(RGBToColor(190,190,190));
  FMsgList.Content.Height:=200;
  FMsgList.Content.Background.FromColor(RGBToColor(200,200,200));

  w3_callback( procedure ()
    begin
      Resize;
      FMsgList.AddItem('Anonymous','This is the first<br>line of stuff');
      FMsgList.additem('Anonymous','This is the second<br>case post, simple and easy to work with');
    end,
    100);
end;

procedure TForm1.Resize;
var
  mRect:  TRect;
begin
  inherited;
  if assigned(FMsgList)
  and assigned(w3Headercontrol1) then
  Begin
    mRect:=clientRect;
    mRect.top:=w3HeaderControl1.Height + 2;
    FMsgList.SetBounds(mRect);
  end;
end;

Adding some bling

The next step is to be a bit more detailed. A facebook post actually have 3 labels in the header. First is your name, secondly the title of your post, and on the line below it is the timestamp (e.g: “anonymous posted on thursday 5’th”). So the next step is to fill in the blanks and also populate our toolbar at the bottom. We want a “like” button, a comment button and a share button. We also want word-wrapping inside the labels – and the navigation from mainform to “news” form and “comment” form should work as expected.

For this example we will continue to use the TW3HeaderControl and just style that when all the logic is in place. Facebook has a cooler looking header, but ultimately it’s more or less the same as the classical iOS header.

Next time

Object Pascal has a bright future — but we need to educate more developers about what is out there. I’m by definition an older programmer, so if you have 15+ years of coding behind you – you are in good company. I can deliver a Facebook prototype in under 6 days – server included. That either means that I am a superman coder, or that the way I work and the tools I use gives me an edge. Follow the next tutorial and find out just how awesome Smart Pascal is and what it can do for HTML5!

[to be continued]

 

Smart ExplodeStr() implementation

June 17, 2014 Leave a comment

In PHP there is a neat function called Explode() which takes a string and breaks it down into words, returning an array of strings. This is handy when displaying text and working out word-wrap functionality, so I wrote a Smart Pascal variation of the Explode() function. Simple but handy:

function  ExplodeStr(const aText:String;
          const aRetain:Boolean;
          var aWords:Array of String):Integer;
const
  CNT_INVALID=' ;.,-:&/()*^!' + #13 + #10;
var
  x:      Integer;
  mCache: String;
  mLen: Integer;
begin
  aWords.Clear;
  result:=0;
  mLen:=Length(aText);
  if mLen>0 then
  begin
    x:=0;
    repeat
      inc(x);
      if pos(aText[x],CNT_INVALID)<1 then
      mCache:=mCache + aText[x] else
      begin
        if aRetain then
        mCache:=trim( mCache + aText[x] );
        if length(mCache)>0 then
        begin
          aWords.add(mCache);
          setLength(mCache,0);
        end;
      end;
    until x>mLen;
    result:=aWords.count;
  end;
end;

The retain parameter simply decides if the break character should be included or not. If you want “clean” words then set this to false.

In short, if you do this:

ExplodeStr('this.is.a.test',false,mData);

The mData array contains the following:

mData[0] = this
mData[1] = is
mData[2] = a
mData[3] = test

But if you retain the characters (which you really have to when measuring width/height of a word) then you get this:

mData[0] = this.
mData[1] = is.
mData[2] = a.
mData[3] = test

Smart Pascal, now an official dialect

June 16, 2014 4 comments

This is so cool! Smart Pascal is now an official (valid, recognized, financed, supported and used) dialect of object pascal.

Visit the Wikipedia article here: https://en.wikipedia.org/wiki/The_Smart_Pascal_programming_language

Or download the full Wiki description of Smart Pascal as a PDF document here: Click to download.

Submission.. now that's gonna be a problem

Submission.. now that’s gonna be a problem

It’s quite addictive as well. I find myself missing Smart Pascal features in Delphi on a daily basis.

Creating an edit-box in Smart Pascal

June 14, 2014 Leave a comment

Under HTML5 there is a special property that you can add to any suitable tag, such as the DIV tag, to make the content editable. In other words you are no longer bound to the restrictions of text-fields and memo-fields as was the case earlier.

Since the Smart Pascal RTL (VJL, visual javascript library) is architected to create, maintain and keep track of controls in the DOM (document object model) – with a 1:1 mapping of functionality, adding support for editable elements is fairly straight forward. But you need to know you way around HTML5 and JavaScript.

Setting up the target

All components and controls in Smart Pascal has a method called StyleTagObject(), this method is called at the beginning of the constructor (after the element has been created and a handle-reference is obtained) so the user, which is you and me, can set properties and values before the html element is finalized and made visible.

Note: You can set properties and attributes at any time, but it makes sense to alter the different values before the component becomes visible. This is why I added StyleTagObject() to the construction sequence.

Turning an element editable is very easy. Simply override the StyleTagObject() method as such:

procedure TMyControl.StyleTagObject;
Begin
  inherited;
  w3system.w3_setAttrib(handle,'contenteditable',True);
end;

In this case we alter the html-element property “contenteditable” to true, which depending on the element, turns the object into a text-editor. Since TW3CustomControl is set to create a DIV element by default (this can be altered easily as well) the result is something that looks like a normal memo editor in Delphi. But without any scrollbars of course.

You may also want to add a call to setFocus() in the mouseDown events if you are building a composite control (e.g an editor, scrollbars and other stuff inside a custom control).

And that’s how easy it is to play with the DOM and create an editable custom HTML5 control!

CSS styling your Smart Pascal app

June 12, 2014 Leave a comment

When you know something and work with it for a long time, human beings tend to make the mistake in believing that everyone else automatically knows the same thing – and instantly get what you are on about.

Having read some of the feedback email and comments, it had begun to dawn on me that — perhaps a lot of those interested in Smart Mobile Studio havent really understood how CSS works in our little RTL. And why should they, like most indie companies our documentation is half-blog half-wiki like (with the exception of the Smart book and what is someone tagged us with on wikipedia).

I thought it would be worth 5 minutes of your time to show you some really awesome features of Smart Mobile pertaining to CSS and creating fantastic looking applications.

The Delphi side of things

In Delphi everything is either owner-drawn or system-drawn. Most of the components you can buy, like the massive packages delivered by Developer Express and TMS – are essentially controls written from scratch, being drawn line by line and pixel by pixel in the Paint() method of TCustomControl.

The system drawn components are naturally those that ship with Windows, like TTextbox, TButton, TToolbar and the likes. Where the VCL implementation is simply a wrapper over an already existing codebase in Windows.

Firemonkey breaks somewhat with this, in that it’s initially completely owner-drawn. Taking full use of hardware accelerated GPU rendering (OpenGL or DirectX).

The Smart Pascal side

Under HTML5 there are two ways to go. We could re-draw the display just like Delphi does by overriding the Paint() method of TW3GraphicControl – and believe it or not the performance is pretty much the same. Actually faster in a lot of situations since Delphi is (and I hate to say this) graphically outdated. It havent changed at all since the days of Windows 95.

The second way to go is via CSS3. Which incidentally is also hardware accelerated.

When you create a visual control in Smart Mobile Studio, be it based on the built-in controls of the browser (a text box for instance or a button) or our own TW3CustomControl base which “mimics” the VCL framework — they all adopt a CSS style.

This is where things get’s interesting, because in the code for TW3CustomControl there is a setting which says “automatically use a CSS style name identical to the Smart Pascal control name“. This is actually a brilliant solution to what could otherwise be a potential problem.

In short, if you create custom-control called TMyGrid, then this control will automatically try to use a style with that name.

So in order to skin this control you simply add a style to the CSS file which is created for each project, add a new style to it:

.TMyGrid {
  background-color: RGBA(255,255,255,1.0);
}

And simply define the CSS code for the components. You can also inject the CSS by code – which can be somewhat helpful, since the concept of styling means that you have to write custom CSS for each theme.

You can also, at any time, switch the CSS class for any control (even those you drag & drop on the form layout) by altering the “StyleClass” string property.

It’s that easy to create fantastic looking CSS3 based styles for any Smart Pascal control (!)

Play around with this under Smart Mobile Studio. What happens if you set the StyleClass property of a button to “TW3Panel” for instance?

And what about CSS animations?

There is a wealth of online CSS examples and ready-made CSS files for awesome looking controls – and you can use them all in your Smart Pascal projects (!)

Defining your own classes

Another handy trick when working with Smart Pascal controls, is to create your own local types for already existing components. For instance if you use a TW3VerticalScrollbar in your project and want a different style for it, you simply declare a new custom scrollbar like this:

type
TMyScrollbar = Class(TW3VerticalScrollbar);

Now you can simply copy and paste the TW3VerticalScrollbar CSS at the bottom of the CSS file, and completely re-model it (and rename it to TW3VerticalScrollbar).

Also remember that CSS is short for “cascading style sheets” with emphasis on “cascading”, meaning that styles follow the same ancestor chain as classes in Delphi does. You can inherit a new style from an older style – and the graphical results are mixed. So if the parent style has a gradient, and you inherit from this and add borders — then the final style will have both a gradient and borders.

That’s very cool!

 

CSS3 effects in Smart Mobile Studio

June 9, 2014 Leave a comment

First, a primer. CSS3 is hardware accelerated, meaning that it’s the GPU that takes care of all the hard work. This results in smooth animations and effects on both desktop and mobile devices.

To show just how easy it is to use CSS3 special effects, here is an example that fades and zooms the forms into view on application startup.

Add the following code to the TForm.InitializeObject() method (automatically created for each form). Remember to add the unit “w3effects” to the uses clause.

 

  FEffect:=TW3WarpInTransition.Create;
  FEffect.OnAnimationBegins:=Procedure (sender:TObject)
    Begin
      writeln('Effect starts'); //log to console
    end;
  FEffect.OnAnimationEnds:=Procedure (sender:TObject)
    Begin
      w3_callback(Procedure ()
        Begin
          FEffect.free;
          FEffect:=NIL;
          writeln('Effect object released'); //log to console
        end,
        1000);
      writeln('Effect finished'); //log to console
    end;
  FEffect.Duration:=1.0; //seconds the effect should last
  FEffect.Execute(self);

And voila you have a cool effect when you app starts!

As you can see from the example, I have populated both the start event and finished event. You don’t really need to do anything in the OnAnimationBegins event. I just log to the console that the effect is starting.

The OnAnimationEnds() event is a bit special. Here I use the “w3_callback” function to invoke an anonymous procedure 1000 ms later, and at that point the effect object is released. W3_Callback is very handy because it allows you to think in terms of “Do this later, but right now continue”. Which can be quite new to all of us, since we are used to Delphi – which is completely linear (except for threads and timers).

Also, a lot of people ask if we support jQuery – the answer is yes, but we also have to ask “why do you want that?“. There is nothing jQuery does that we don’t do better, and with the normal parent/child component model our RTL uses (same as VCL and FMX) — there is no point querying the DOM for sub-elements (which is what jQuery does). Just use a list. jQuery is not a magic effects library – but (as the name implies) a query language to locate and alter groups of tags in a document or sub-element.

The only possible use I can imagine for using jQuery with Smart Pascal, is if you call a JSON-RPC service which returns an unknown number of items back. But it would be a very poor service which doesn’t include the result count in the header.

Take a look at the w3effects unit, it’s a snap to make your own effects as well 🙂

Smart Mobile Studio Compiler Is Now Free

June 4, 2014 2 comments
Free as in beer, not as in freedom

Giving back to the community

Updated: This news caused so much traffic that the main Smart Mobile Studio server went down. We are working on getting it back up again as quickly as possible.

Yes you read right. The Smart Mobile Studio command-line compiler is about to be released as free to use by everyone. For a quick presentation of Smart Mobile Studio (other than the main website linked above) you can click here.

One extremely important distinction: Only the command-line compiler is released as free to use, the RTL (source libraries), toolkit and IDE remains a commercial product.

After all, Smart Mobile Studio is a massive undertaking and it costs money to deliver such a system. The development of Smart Mobile Studio have helped finance the further development of DWScript and several critical fixes to SynEdit – so supporting this product is well worth every penny!

So why is this cool?

It means that you can use the most advanced object pascal to JavaScript compiler in the world freely in your own projects.

It means that you can automate object pascal to JavaScript compilation on your web server (which is really cool) to create awesome adaptive projects (code that changes according to user input, perfect for heavy-duty security or multi-player games). You can in fact remote-compile and run projects in the browser. What about a “delphi for the web” where people can actually work online and compile directly in the browser? Then use phonegap/cordova to compile directly to native ? Nothing is impossible.

And it means that the Delphi community finally have a proper HTML5 development tool. After all, HTML5 builder from Embarcadero was just a re-hashing of php-builder which was before that called “Delphi for PHP”. A product which sadly provide absolutely no source to source compilation at all.

Well, now the community can enjoy exactly that.

Is it any good?

Yes. It is the best object pascal source-level compiler on the marked, and most notably the only compiler that processes around 90% of the Delphi syntax and correctly reproduces it in JavaScript. It actually sculpts a VMT (virtual method table) in JavaScript in order to deliver this. Which is no small achievement.

  • Classes
  • Polymorphism
  • Inheritance
  • Interfaces
  • Virtual, abstract and static members
  • Class functions and procedures
  • Class variables
  • Anonymous procedures and functions
  • Private, protected, public and published members
  • Var parameters
  • Class, record and type helpers

Smart Pascal (our flavor of object pascal) also has features not yet supported by Embarcadero Delphi:

  • Partial classes
  • Lambdas
  • Property expressions
  • In place number operators
  • In place array operators

Without the RTL, what’s the point?

The possibilities are endless. For instance, you could easily write a game that only uses a minimalistic RTL that you write yourself (we must remember that this is HTML5 and you have full access to the DOM, which is the operative system in this case). In fact, this is what the “other” JS compiler-vendors out there do. Most of them come with 40+ commands for graphics and general key/mouse/touch handling.

Compare that to the several hundred classes and thousands of functions you get when you buy Smart Mobile Studio (!)

But you can easily use the command-line compiler to create awesome products:

  • Teach Pascal by running the compiler via a web server.
    You can now compile and execute object pascal in the browser quite easily!
  • Write a simple webGL wrapper or canvas wrapper and create your own games (!)
  • Automate server-side code, make nodeJS work for you – not the other way around!
  • Use Delphi to write a nice utility for website effects (flash replacement), then use the compiler to generate the JS
  • Teach kids to program, you now have a compiler and kids needs simple programs (so the RTL is overkill)
  • Use your imagination, the possibilities are endless ..

But the most important time-saving feature (and time being money) is: you can write object pascal and target a language that has absolutely no concept of classes or objects at all. What you can achieve with our compiler in 3 days — equals 3 weeks in native JavaScript. And what you do in 3 months with Smart Pascal – is like 3 years if hand-writing java script. And you still would be nowhere near the quality of code that our compiler produces.

And I might add, you get more power in this single executable, than all of Embarcadero HTML5 Builder combined. This is what Embarcadero should have delivered but failed to do. So there.

I will be blogging a course in how to use it properly over the next weeks.

Also be sure to get the smart pascal book, it will save you a lot of time!

Hello world

No language is complete without a “hello world” example, so here is the classical introduction:

program HelloWorld;

procedure writeln(const aMessage:String);
begin
  asm
    console.log(@aMessage);
  end;
end;

Begin
  writeln('Hello world!);
end.

As you can see from the code we access the DOM (document object model) and JavaScript engine directly via an ASM section. In this case we simply call the stdout.

Having compiled this, you can run it either in the browser with the javascript console visible, or using nodeJS from the command-line like this:

node helloworld.js

And the output is as expected 🙂

A bit more advanced

Let’s do something a bit more fun. Note: you would never have to do this in Smart Mobile Studio, there everything is taken care of by distinctly organized classes and all the low-level stuff is dealt with by the VJL (visual javascript library). Just like in Delphi you have great depth in how you can program. You can chose to write low-level code or stick to the high level classes.

But since an RTL is not available here, we have to do some extra ground-work to get started. Let’s see how to isolate some core functions:

program myFirstHTML5Program;

type
THandle = variant; 

//Creates a HTML element by code
function makeHTMLElement(const aTypeId:String):THandle;
begin
  asm
    @Result = document.createElement(@aTypeId);
  end;
end;

//Destroys a HTML element by code
Procedure killHtmlElement(const aHandle:THandle);
begin
  asm
    window.document.body.removeChild(@aHandle);
  end;
end;

//Display a message
procedure showmessage(const aMessage:String);
begin
  asm
    alert(@aMessage);
  end;
end;

Procedure setupMyApp;
var
  mButton: THandle;
Begin
 mButton:=makeHTMLElement('button');
 mButton.style.width  := "80px";
 mbutton.style.height := "28px";
 mButton.text := "Click me";
 mButton.onClick:=procedure ()
  begin
    showmessage('You clicked me!');
    killHtmlElement(mButton);
  end;
end;

begin
  setupMyApp;
end.

Which neatly compiles to:

function setupMyApp() {
   var mButton=undefined;
   mButton=makeHTMLElement("button");
   mButton.style.width="80px";
   mButton.style.height="28px";
   mButton.text="Click me";
   mButton.onClick=function () {
      showmessage("You clicked me!");
      killHtmlElement(mButton);
   };
};
function showmessage(aMessage) {
    alert(aMessage);
  };
function killHtmlElement(aHandle$1) {
    window.document.body.removeChild(aHandle$1);
  };
function makeHTMLElement(aTypeId) {
   var Result=undefined;
   Result = document.createElement(aTypeId);
  return Result
};

I’m sure you can guess what the outcome looks like:

Fairly straight forward

Fairly straight forward

What does a class look like?

Now let’s see how a class looks like when it’s compiled. Here is the TW3Button class (ordinary button) from the Smart Mobile Studio RTL. Notice the VMT and inheritance chain for each compiled method. This is no simple function mapper – but a real compiler with quality output. Unlike our competitors we produce code that is extremely fast – with no “copy and paste” mechanisms or pre-defined set of RTL functions. Each pascal line is tokenized, parsed and used to build an AST (abstract symbolic tree) which in turn is compiled into JavaScript. And the results speak for themselves.

/// TW3Button = class (TW3CustomControl)
///  [line: 9, column: 3, file: w3button]
var TW3Button= {
   $ClassName:"TW3Button",
   $Parent:TW3CustomControl,
   $Init:function ($) {
      TW3CustomControl.$Init($);
   }
   /// function TW3Button.getCaption() : String
   ///  [line: 39, column: 20, file: w3button]
   ,getCaption:function(Self) {
      var Result="";
      if (Self.FHandle$4) {
         Result=Self.FHandle$4.innerHTML.toString();
      }
      return Result
   }
   /// procedure TW3Button.setCaption(Value: String)
   ///  [line: 45, column: 21, file: w3button]
   ,setCaption$2:function(Self, Value$6) {
      if (Self.FHandle$4) {
         Self.FHandle$4.innerHTML=Value$6;
      }
   }
   /// function TW3Button.makeElementTagObj() : Variant
   ///  [line: 34, column: 20, file: w3button]
   ,makeElementTagObj:function(Self) {
      return w3_createHtmlElement("button");
   }
   /// procedure TW3Button.InitializeObject()
   ///  [line: 27, column: 21, file: w3button]
   ,InitializeObject:function(Self) {
      TW3CustomControl.InitializeObject(Self);
      TW3MovableControl.SetWidth$(Self,100);
      TW3MovableControl.setHeight$(Self,32);
   }
   ,Destroy:TW3TagObj.Destroy
   ,AfterUpdate:TW3CustomControl.AfterUpdate
   ,FinalizeObject:TW3CustomControl.FinalizeObject
   ,InitializeObject$:function($){return $.ClassType.InitializeObject($)}
   ,makeElementTagId:TW3TagObj.makeElementTagId
   ,makeElementTagObj$:function($){return $.ClassType.makeElementTagObj($)}
   ,StyleTagObject:TW3CustomControl.StyleTagObject
   ,Resize:TW3MovableControl.Resize
   ,setHeight:TW3MovableControl.setHeight
   ,SetWidth:TW3MovableControl.SetWidth
   ,supportAdjustment:TW3MovableControl.supportAdjustment
   ,Invalidate:TW3CustomControl.Invalidate
};

Incidentally, here is a small Chrome (webkit) demo coded in Smart Mobile Studio. I wrote it for iPad so you may have to scale down your window slightly, but it took me roughly 2 hours to make since I had to port over a CSS library first. But no-one can tell me that object pascal doesnt make perfect sense in the HTML5 world, because it does. It makes website programming fun and easy. You can also embed your creations in an IFRAME and use it just like people used flash. But SMS is way beyond flash in terms of features.

Easy peasy :)

Easy, fun and very fast! Music from Last Ninja 1 @ Commodore 64

 Sources

http://smartmobilestudio.com/2014/06/04/giving-back-community/

 

 

Smart Pascal, nodeJS and the big picture

June 2, 2014 Leave a comment

We are standing in front of a revolution in software development. The wave has just begun to arise, and already we can see where this is going and what the impact will be. No, I’m not talking about a biblical prophecy or clairvoyance. I don’t need tarot cards or an astrologer to tell me where the future is and what it will look like — I know, because I have been a part of a team that is paving the way for the Object Pascal community, so that we can surf those waves rather than drown.

At the moment we live in a world where there are real languages and fake languages. We call the fake languages scripting engines, and they come in all shapes and sizes – with great variety in what they have to offer and the eco-systems they were designed to live in. Some script engines live in the command prompt, some in the web server and others are not so picky and can exist almost anywhere.

But with great variety comes conflicts (a paradox for sure, since more options should mean less to argue about), and with conflict otherwise knowledgable and outstanding people  waste their time in pointless arguments. Bickering and slander each other over what is presumably their favorite development suite as an excuse to troll their opponents so they feel better about themselves; typically without knowing the least bit about each other, nitpicking on superficial differences that are skin deep. But while the great majority of us were busy with buttons and pins — something was brewing in the darker corners of the internet.

As a Delphi developer, have you spent a few minutes thinking about what nodeJS actually means for you? How will nodeJS impact your life as a software engineer? How will it affect your life 10 years down the line?

Let me shed some light over what is about to take place in the world of programming.

At the moment we create the majority of our applications using native languages. All of these languages have dependencies on locally compiled libraries or operative systems files in order to function, and naturally you need real software (like C++ builder, Delphi or FPC) to build these things. The supremacy of real languages over scripting languages is of course undisputed at this level, but on another level, the level of the “user experience” and the realities of networking, the laws of physics are turned on their head and your present skill set reduced to ashes.

So the world of programming as we know it is about to change. Completely and utterly. In a very short time.

Where all are blind, rules a one eyed king

Write once, host anywhere

Write once, host anywhere

Imagine writing your next Delphi application in two steps. First, the server – where you isolate database connectivity, your storage API and basically all the mission critical and boring stuff that we simply have to write, even under ordinary, native Delphi programming.

Having isolated this in a server, be it a win32 service, a com factory or just an ordinary RPC server (like a Remobjects server or a Delphi SOAP service) you move on to the next step. Namely the user interface.

So all the database logic, file access and user-identity stuff, which includes login, access rights and personal preferences is now isolated in one part of your solution, while the main form, dialogs and “program” is in another. Your main user interface simply calls your service or server whenever it needs data or anything to display, and as such your GUI is totally segregated from the code that does mission critical processing.

Nothing new under the sun you say? This sounds like a normal “remote procedure call” client/server setup right? Just like Delphi has done for years via SOAP, COM, Datasnap and a wide variety of component packages. Or perhaps a more direct parallel in preferable in this case: how Visual Basic applications used to be built, with near hopeless dependency on ActiveX and COM in order to function.

Is this what is coming I hear you say? Well yes, but not like you think. The above was only the parallel to describe how you compartmentalize an application into two sections. The reality is that the next generation of software allows you to write both sides of the coin just like the parallel – but it lives completely in the cloud (!) And I mean completely. Platform independent in the true sense of the word, non proprietary (no ownership), fully portable and consumable from every device capable of running a HTML5 engaged browser.

The implications of the browser and server speaking the same language is monumental. Let me explain further.

Embracing the future

Building an application 4-8 years from now will be more about cherry picking online services and architecture will be (read: is already) a matter of coupling services together in a meaningful way. The value of a product will be in its code (as always) and the services you provide, which will be mostly –if not completely, JavaScript based.

In other words: game over, the mother of all scripting languages is JavaScript. Unless a beard seeking missile is launched for each and every individual with the skill set to device and implement a JavaScript virtual machine; eradicating advanced software from the face of the earth – there is not a chance in hell that any other “browser friendly alternative scripting engine” can catch up with it.

Flash is dead, VB Script is dead, PL-SQL is a joke and Java as a secure language, handcuffed as it has been for a while now, is tied to the firing-post. I will happily play the role of the prophet and say that PHP and JSP will be eroded out of the statistics of valuable server technologies within a timeframe of 10-14 years. They will still exist, but their user base will be minuscule and impotent; they wont even show up in the charts.

What we face now is a technology that enables us to architect the complete technical service layer, both the server, the database layer — as well as the user interface and experience. All of it from the same language (and project group in your favorite HTML5 authoring tool). This is what the future is all about. And this is what kids today learn as their fundamental computing experience. Kids nowadays don’t spend hours fiddling with assembler or basic like we did, they build enterprise level websites that in Delphi would cost a fortune to realize.

And that is what nodeJS does. This is where HTML5 and Javascript comes into its own right. It allows you not only to write the GUI of an application via clever JavaScript and HTML5 code, it allows you to write the complete server architect from scratch using JavaScript as the only language.

THAT is what’s lurking around the corner. Nothing technologically new. But the scale of this is unprecedented. This happens world-wide.

And I havent even talked about Smart TV’s yet and the new widget marked that has the potential to do a “Napster/Pirate bay” on the television broadcasting companies of the world.

Seeing the big picture yet?

Run at server

Run at server

Let’s move on to connect some dots. Ever noticed those C# tags [RunAt=”server”] ? They exist in Delphi as well but sadly Embarcadero never used them for anything as substantial as web development. What it does is that the descriptor informs the compiler that the following function should execute on the server, and not the client. So when you build the code the compiler generates two files rather than one (talking ASP.NET here by the way).

But the ASP technology is quickly getting outdated. nodeJS delivers what IIS/ASP.net is presently delivering — but without the bolted down, handcuffed to Microsoft dependency. NodeJS blows it apart and allows YOU to define your own protocols, security, transport channels and server activity.

Now let me ask you: How do you compete in a marked like that with a native Win32/64 – OSX compiler?

You don’t. You’re not even a candidate. Hence … an Object Pascal to JavaScript compiler would come in handy right?

Lest we forget

Real languages will never go away. The rise of cloud computing, cloud applications and fully abstracted network entities – they wont affect your Delphi investment or your C++ skills. But they will affect you in the sense that unless you are able to deliver cloud based applications — applications that cannot by their very nature include any native elements (no dll’s, no win32/64 or OSX binaries) — then chances are that you will see a drop in job offerings and contract work in the near future. We have to adapt faster than this.

But we can turn our mistakes into assets by simply taking advantage of the tools readily available today. Tools that have been custom-made for Delphi developers to gradually enrich their toolbox. And it’s actually very, very exciting and fun!

Smart Mobile Studio

Cloud apps will work anywhere

Cloud apps will work anywhere

Hopefully I have opened your eyes a bit to what is coming just around the corner, and you have more appreciation for what nodeJS and the JavaScript revolution represents.

This is why I created Smart Mobile Studio to begin with, because I knew that this was coming, the factors leading to this were just to great to ignore. Google (Chrome OS) and Mozilla (Firefox OS) are already pushing mobile operative systems based on HTML5. Now what language do you think those operative systems run? Delphi?

I also wanted to retain and protect our knowledge of object pascal – and at the same time give Delphi developers a bridge from the present reality  — into the new reality of 100% abstract portable cloud applications. We might not like the new reality where JavaScript out-performs native Delphi graphically, but that is – at the end of the day, the reality as it is.

But what we can do is to invest some time in learning what is around the corner, protect out language – which ultimately is where our value as developers exist.

Why do you think Google, Facebook and Microsoft are battling over online identity management? All the “log in with Facebook” or “log in with google” — because the company that controls online identity will be the passport holders for software merchants and credit card transactions in the future.

And no matter what you create in the coming years, being able to validate a person’s identity online will be worth billions. Think active directory on a global scale and you get the idea.

The future is near, be prepared