Smart Pascal: Information to alpha testers
Note: This is re-posted here since we are experiencing networking problems at http://www.smartmobilestudio.com. The information should show up there shortly.
Our next-gen RTL will shortly be sent to people who have shown interest in testing our new RTL. We will finish the RTL in 3 stages of alpha before we hit beta (including code freeze, just fixes to existing code) and then release. After that we move on to the IDE to bring that up to date as well.
Important changes
You will notice that visual controls now have a ton of new methods, but one very interesting in particular called: ObjectReady. This method holds an important and central role in the new architecture.
You may remember that sometimes you had to use Handle.ReadyExecute() in the previous RTL? Often to synchronize an activity, setting properties or just calling ReSize() when the control was ready and available in the DOM.
To make a long story short, ObjectReady() is called when a control has been constructed in full and the element is ready to be used. This also includes the ready-state of child elements. So if you create 10 child controls, ObjectReady will only be called once those 10 children have finished constructing – and the handle(s) have safely been injected into the DOM.
To better understand why this is required, remember that JavaScript is asynchronous. So even though the constructor finish – child objects can still be busy “building” in the background.
If you look in SmartCL.Components.pas, notice that when the constructor finishes –it does a asynchronous call to a procedure named ReadySync(). This is a very important change from the previous version – because now synchronization can be trusted. I have also added a limit to how long ReadySync() can wait. So if it takes to long the ReadySync() method will simply exit.
ObjectReady
As you probably have guessed, when ReadySync() is done and all child elements have been successfully created – it called the ObjectReady() method.
This makes things so much easier to work with. In many ways it resembles Delphi and Freepascal’s AfterConstruction() method.
To summarize the call-chain (or timeline) all visual controls follow as they are constructed:
- Constructor Create()
- InitializeObject()
- ReadySync
- ObjectReady()
- Invalidate
- Resize
If you are pondering what on earth Invalidate() is doing in a framework based on HTML elements: it calls Resize() via the RequestAnimationFrame API. TW3GraphicControl visual controls that are actually drawn, much like native VCL or LCL components are – naturally invalidate the graphics in this method. But ordinary controls just does a synchronized resize (and re-layout) of the content.
When implementing your own visual controls (inheriting from TW3CustomControl), the basic procedures you would have to override and implement are:
- InitializeObject;
- FinalizeObject;
- ObjectReady;
- Resize;
Naturally, if you dont create any child controls or data of any type – then you can omit InitializeObject and FinalizeObject; these act as constructor and destructor in our framework. So in the JVL (Javascript Visual component Library) you dont override the constructor and destructor directly unless it is extremely important.
Where to do what
What the JVL does is to ensure a fixed set of behavioral traits in a linear fashion- inside an environment where anything goes. In order to achieve that the call chain (as explained above) must be predictable and rock solid.
Here is a quick cheat sheet over what to do and where:
- You create child instances and set variables in InitializeObject()
- You set values and access the child instances for the first time in ObjectReady()
- You release any child instances and data in FinalizeObject()
- You enable/disable behavior in CreationFlags()
- You position and place child controls in Resize()
- Calling Invalidate() refreshes the layout or graphics, depending on what type of control you are working with.
What does a custom control look like ?
Its actually quite simple. Now I have included a ton of units here in order to describe what they contain, then you can remove those you wont need. The reason we have fragmented the code like this (for example System.Reader, System.Stream.Reader and so on) is because node.js, Arduino, Raspberry PI, Phonegap, NodeWebKit are all platforms that run JavaScript in one form or another – and each have code that is not 1:1 compatible with the next.
Universal code, or code that executes the same on all platforms is isolated in the System namespace. All files prefixed with “System.” are universal and can be used everywhere, regardless of project type, target or platform.
When it comes to the reader / writer classes, it’s not just streams. We also have binary buffers (yes, you actually have allocmem() etc. in our RTL) – but you also have special readers that work with database blobs, Bson attachments .. hence we had no option but to fragment the units. At least it makes for smaller code 🙂
unit MyOwnControlExample; interface uses // ## The System namespace is platform independent System.Widget, // TW3Component System.Types, // General types System.Types.Convert, // Binary access to types System.Types.Graphics, // Graphic types (TRect, TPoint etc) System.Colors, // TColor constants + tools System.Time, // TW3Dispatch + time methods // Binary data and streams System.Streams, System.Reader, System.Stream.Reader, System.Writer, System.Stream.Writer, // Binary data and allocmem, freemem, move, copy etc System.Memory, System.Memory.Allocation, System.Memory.Buffer, // ## The SmartCL namespace works only with the DOM SmartCL.System, // Fundamental methods and classes SmartCL.Time, // Adds requestAnimationFrame API to TW3Dispatch SmartCL.Graphics, // Offscreen pixmap, canvas etc. SmartCL.Components, // Classes for visual controls SmartCL.Effects, // Adds about 50 fx prefixed CSS3 GPU effect methods SmartCL.Fonts, // Font and typeface control SmartCL.Borders, // Classes that control the border of a control SmartCL.CSS.Classes, // Classes for self.css management SmartCL.CSS.StyleSheet, // Create stylesheets or add styles by code { Typical child controls SmartCL.Controls.Image, SmartCL.Controls.Label, SmartCL.Controls.Panel, SmartCL.Controls.Button, SMartCL.Controls.Scrollbar, SMartCL.Controls.ToggleSwitch, SmartCL.Controls.Toolbar } ; type TMyVisualControl = class(TW3CustomControl) protected procedure InitializeObject; override; procedure FinalizeObject; override; procedure ObjectReady; override; procedure Resize; override; end; implementation procedure TMyVisualControl.InitializeObject; begin inherited; // create child instances here end; procedure TMyVisualControl.FinalizeObject; begin // Release child instances here inherited; end; procedure TMyVisualControl.ObjectReady; begin inherited; // interact with controls first time here end; procedure TMyVisualControl.Resize; begin inherited; if not (csDestroying in ComponentState) then begin // Position child elements here end; end;
CreateFlags? What is that?
Delphi’s VCL and Lazarus’s LCL have had support for CreateFlags for ages. It essentially allows you to set some very important properties when a control is created; properties that enable or disable how the control behaves.
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 // flag to issue key and focus events );
As you can see these properties are quite fundamental, but there are times when you want to alter the default behavior radically to make something work. Child elements that you position and size directly doesn’t need cfReportReSize for instance. This might not mean much if its 100 or 200 child elements. But if its 4000 rows in a DB grid, then dropping that event check has a huge impact.
ComponentState
Yes, finally all visual (and non visual) have componentstate support. This makes your code much more elegant, and also way more compatible with Delphi and Lazarus. Here are the component-states we support right now:
TComponentState = set of ( csCreating, // Set by the RTL when a control is created csLoading, // Set by the RTL when a control is loading resources csReady, // Set by the RTL when the control is ready for use csSized, // Set this when a resize call is required csMoved, // Set this when a control has moved csDestroying // Set by the RTL when a control is being destroyed );
So now you can do things like:
procedure TMyControl.StartAnimation; begin // Can we do this yet? if (csReady in ComponentState) then begin // Ok do magical code here end else //If not, call back in 100ms - unless the control is being destroyed if not (csDestroying in ComponentState) then TW3Dispatch.Execute(StartAnimation, 100); end;
Also notice TW3Dispatch. You will find this in System.Time and SmartCL.Time (it is a partial class and can be expanded in different units); we have isolated timers, repeats and delayed dispatching in one place.
SmartCL.Time adds RequestAnimationFrame() and CancelAnimationFrame() access, which is an absolute must for synchronized graphics and resize.
Other changes
In this post I have tried to give you an overview of immediate changes. Changes that will hit you the moment you fire up Smart with the new RTL. It is very important that you change your existing code to make use of the ObjectReady() method in particular. Try to give the child elements some air between creation and first use – you get a much more consistent result on all browsers.
The total list of changes is almost to big to post on a blog but I did publish a part of it earlier. But not even that list contains the full extent. I have tried to give you an understanding
Recent
The vatican vault
- January 2022
- October 2021
- March 2021
- November 2020
- September 2020
- July 2020
- June 2020
- April 2020
- March 2020
- February 2020
- January 2020
- November 2019
- October 2019
- September 2019
- August 2019
- July 2019
- June 2019
- May 2019
- April 2019
- March 2019
- February 2019
- January 2019
- December 2018
- November 2018
- October 2018
- September 2018
- August 2018
- July 2018
- June 2018
- May 2018
- April 2018
- March 2018
- February 2018
- January 2018
- December 2017
- November 2017
- October 2017
- August 2017
- July 2017
- June 2017
- May 2017
- April 2017
- March 2017
- February 2017
- January 2017
- December 2016
- November 2016
- October 2016
- September 2016
- August 2016
- July 2016
- June 2016
- May 2016
- April 2016
- March 2016
- January 2016
- December 2015
- November 2015
- October 2015
- September 2015
- August 2015
- June 2015
- May 2015
- April 2015
- March 2015
- February 2015
- January 2015
- December 2014
- November 2014
- October 2014
- September 2014
- August 2014
- July 2014
- June 2014
- May 2014
- April 2014
- March 2014
- February 2014
- January 2014
- December 2013
- November 2013
- October 2013
- September 2013
- August 2013
- July 2013
- June 2013
- May 2013
- February 2013
- August 2012
- June 2012
- May 2012
- April 2012