Archive

Archive for January 20, 2017

Smart Pascal: Update specifics

January 20, 2017 Leave a comment

With what is probably the biggest update Smart Mobile Studio has seen to date only a couple of weeks away, what should the Smart Pascal programmer know about the new RTL?

I have written quite a few articles on the changes, so you may want to backtrack and look at them first – but in this post I will try to summerize two of the most important changes.

Unit files, a background

The first version of Smart Mobile Studio (sms) was designed purely for webkit based, HTML5 applications. As the name implies the initial release was designed purely for mobile application development; with the initial target firmly fixed on IPhone and IPad. As a result, getting rid of webkit exclusive code has been very time-consuming to say the least.

At the same time we added support for embedded devices, like the Espruino micro-controller, and in leu of that process -it became clear that the number of devices we can support is huge. As of writing we have code for more than 40 embedded devices lined up, ready to be added to the RTL (after the new RTL is in circulation).

Add to this the fact that node.js is now the most powerful cloud technology in the world, the old “DOM only”, webkit oriented codebase was hopelessly narrow minded and incapable of doing what we wanted. Something had to give, otherwise this would be impossible to maintain – not to mention document in any reasonable logical form.

A better unit system

I had to come up with a system that preserved universal code – yet exposed special features depending on the target you compile for.

So when you compile for HTML5 you get all the cool stuff the document object model (DOM) has to offer, but also full access to the universal units. Same goes for node.js, its has all the standard stuff that is universal – but expose a ton of JS objects that exist nowhere else.

What I ended up with was that each namespace should have the same units, but adapted for the platform it represents. So you will have units like System.Time.pas, which represents universal functionality regarding timing and synchronization; But you are supposed to use the one prefixed by your target namespace. For instance, SmartCL.Time or SmartNJ.Time. In cases where the platform doesnt expose anything beyond the universal code, you simply fall back on the System.*.pas unit.

namespace_scheme

It may look complex, but it’s super easy once you get it

The rules are super simple:

  • Coding for the browser? Use the units prefixed with SmartCL.*
  • Coding for node.js? Use the units prefixed with SmartNJ.*
  • Coding for embedded? Use the units prefixed with SmartIOT.*

Remember that you should always include the system version of the unit as well. So if you include SmartCL.Time.pas, add System.Time.pas to the uses list as well. It makes it easier to navigate should you want to CTRL + Click on a symbol.

Better synchronization engine

JavaScript is async by nature. Getting JavaScript to behave like traditional object pascal is not only hard, it is futile. Besides, you dont want to be treated like a child and shielded from the reality of JavaScript – because that’s where all the new cool stuff is at.

In Delphi and Lazarus we are used to our code being blocking. So if you create child objects in your constructur, these objects are available there and then. Well, thats not how JavaScript works. You can create an object, the code continues, but in reality the object is not finished yet. It may in fact be assembling in the background (!)

To make writing custom-controls sane and human, we had to change a few things. So in your constructor (which is InitializeObject in our RTL, not the actual class constructor) you are expected to create child object – but you should never use them.

Instead there is a method called ObjectReady() which naturally fires when all your child elements have been constructed properly and the control has been safely injected into the document object model. This is where you set properties and start to manipulate your child objects.

synchro

So finally the setup process is stable, fast and working as you expect. As long as you follow this simple rule, your controls will appear and function exactly like you expect them to.

  • Never override Create and Destroy, use InitializeObject and FinalizeObject
  • Override ObjectReady() to set initial values

Creation flags

We have also added creation flags to visual controls. This is a notion familiar to both Delphi’s VCl and Lazarus LCL component structures. It allows you to define a few flags that determines behavior.

For instance, if your control is sized manually and will never really need to adapt – then it’s a waste of time to have Resize() fire when the size is altered. Your code will run much faster if the RTL can just ignore these changes. The flags you can set are:

  TW3CreationFlags = set of
    (
    cfIgnoreReadyState,     // Ignore waiting for readystate
    cfSupportAdjustment,    // Controls requires boxing adjustment (default!)
    cfReportChildAddition,  // Dont call ChildAdded() on element insertion
    cfReportChildRemoval,   // Dont call ChildRemoved() on element removal
    cfReportMovement,       // Report movements? Managed call to Moved()
    cfReportResize,         // Report resize? Manages call to Resize()
    cfAllowSelection,       // Allow for text selection
    cfKeyCapture            // assign tabindex, causing the control to issue key and focus events
    );

To alter the default behavior you override the method CreationFlags(), which is a class function:

class function TW3TagObj.CreationFlags: TW3CreationFlags;
begin
  result := [
    // cfAllowSelection,
    cfSupportAdjustment,
    cfReportChildAddition,
    cfReportChildRemoval,
    cfReportMovement,
    cfReportReSize
    ];
end;

Notice the AlloSelection flag? This determines if mouse selection of content is allowed. If you are making a text display, or editor, or anything that the user should be allowed to select – then you want this flag set.

The cfKeyCapture is also a flag many people have asked about. It determines if keyboard input should be allowed for the element. By default this is set to OFF. But now you dont have to manually mess around with element attributes. Just add that flag and your control will fire the OnKeyPress event whenever it has focus and the user press a key.

Tweening and fallback effects

In the last update a bug managed to sneak into our SmartCL.effect.pas library, which means a lot of people havent been able to fully use all the effects. This bug has now been fixed, but we have also added a highly efficient tweening library. The concept is that effects will be implemented both as hardware acellerated, GPU powered effects – but also with CPU based tweening alternatives.

For mobile devices it is highly adviced to use the GPU versions (the normal SmartCl.Effects.pas versions).

When you add the unit SmartCL.Effects.pas to your forms, what happens is that ALL TW3MovableObject based controls are suddenly extended with around 20 fx prefixed methods. This is because we use partial classes (which means classes can be extended).

This is super powerful, and with a single line of code you can make elements fly over the screen, shrink, zoom out or scale in size. You even get a callback when the effect has finished – and you can daisy-chain calls to execute a whole list of effects in sequence.

FMyButton.fxSizeTo(10, 10, 200, 200, 0.8, procedure ()
  begin
   writeln("Effect is done!");
  end);

And execute in sequence. The effects will wait their turn and execute one by one.

FMyButton.fxMoveTo(10,10, 0.8)
  .fxSizeTo(100,100, 0.8)
  .fxFadeTo(0.3, 0.8);

Stay tuned for more info!

Smart Pascal: Download streams

January 20, 2017 Leave a comment

Real, binary streams has been a part of the Smart Pascal RTL for quite some time now. As a Delphi developer you probably take that for granted, but truth be told – no other JavaScript framework even comes close to our implementation. So this is unique to Smart Pascal, believe it or not.

The same can be said about the ability to allocate, move and work with memory buffers. Sure you can write similar code by hand in pure JavaScript, but the amount of code you have to write will quickly remind you why object orientation is so important.

Binary data counts

So you got streams, what of it? I hear you say. But you are missing the point here. If there is one thing JavaScript sucks at, it’s dealing with binary data. It has no concept really of bytes versus 32 bit integers, or 64bit integers. There is no such thing as a pointer in JavaScript. So while we have enjoyed pointers, memory allocations, being able to manipulate memory directly and use streams to abstract from our binary data for decades in Delphi — all of this is brand new under JavaScript.

672274_cd11_2

And binary data counts. The moment you want to write code that does something on any substancial level – the capacity for dealing with binary data in a uniform way is imperative. If you are into HTML5 game coding you will sooner or later get in contact with map editors (or level editors) that work best in binary format. If you plan on making a sound app that runs in the cloud, again being able to read binary files (just like we do in Delphi) is really, really important. Just stop and think for a few seconds how poor Delphi and C++ builder would be without streams.

Making data available

One developer asked me an important question earlier: how do you get data out? And he meant that quite literally. What if my Smart app is the producer of binary data? What if I use SMS to create the fancy map editor, or the waveform generator code or whatever – what then?

Indeed that is a good question, but thankfully an easy one.

Most JavaScript objects, or data objects in general, inside a browser can be exported. What this means is that the browser can tag a spesific object with an ID, and then make the data available as a normal link.

For instance, if you have a block of memory like an uint8Array and you want that data exported, you would call url.createObjectURL() and it will create an URL you can use to get that data. Let’s have a look at the code you need first:

function BinaryStreamToURLObject(Stream: TStream):String;
var
  mBlob:  THandle;
begin
  if stream<>NIL then
  begin
    stream.position:=0;
    var mTemp := TDatatype.BytesToTypedArray(Stream.read(stream.size));
    asm
      var encdec = window.URL || window.webkitURL;
      @mBlob = new Blob([@mTemp],{ type: "application/octet-binary" } );
      @result = encdec.createObjectURL(@mBlob);
      console.log(@result);
    end;
  end;
end;

procedure ForceDownloadOf(FileName: string; Stream: TStream);
var
  LARef:  TControlHandle;
begin
  if Stream <> nil then
  begin
    if Stream.Size > 0 then
    begin
      // Create node
      asm
        @LARef = document.createElement('a');
      end;

      // Setup values
      LARef.style := "display: none";
      LARef.href := BinaryStreamToURLObject(Stream);
      LARef.download := Filename;

      // Add to DOM
      asm
        document.body.appendChild(@LARef);
      end;

      // Wait for the obj to appear in the DOM
      LARef.readyExecute( procedure ()
        begin
          // Invoke click on link
          LARef.click();
        end);

    end;
  end;
end;

Note #1: Notice how I use a TControlHandle in the example above. Why? Because this handle has a helper class that gives us some perks, like readyExecute(), which fires when the element is safely in the DOM and is ready to be used.

Note #2: Since the built-in browser in Smart Mobile Studio doesnt have download functionality, nothing will happen when you run this inside the IDE. So click on the “Open in browser” and run your app there to see it.

The first function takes a stream and converts it into a blob object. The second function creates an anchor object, and then calls the click() method on that anchor. Essentially kick-starting the download. It is the exact same as you clicking on a download link, except we do it purely through code.

Let’s go through the steps

  • Grab all the data from the stream
  • Convert from TByteArray to a typed browser array
  • Fetch the browser’s URL object
  • Call createObjectURL, passing the data
  • Return the internal URL for the data, which is now kept safe
  • Create an anchor link object
  • Make sure the anchor is invisible
  • Set the URL to our blob above
  • Add the anchor to the DOM
  • Call the Click() method on the anchor

Voila! Not to hard was it đŸ™‚

So now you can just have a button and in the onClick event you just call ForceDownload() and bob’s your uncle đŸ™‚

Here is the internal link I got after saving a TW3Dataset to a stream and pushing it through the steps above: blob:http%3A//192.168.38.102%3A8090/9a351a97-5f6d-4a43-b23b-c81b77972e21

This link is relative to the content, so it will only work for as long as your Smart app is in memory (actually, only while the blob is managed, you can remove the blob as well).