[Pas2js] Pas2JS-Widgets with LFM streaming

Michael Van Canneyt michael at freepascal.org
Sat Jul 27 11:47:18 CEST 2019



On Sat, 27 Jul 2019, Sven Barth via Pas2js wrote:

> Am 27.07.2019 um 09:33 schrieb Michael Van Canneyt:
>>
>>
>> On Fri, 26 Jul 2019, Sven Barth via Pas2js wrote:
>>
>>> Hello together!
>>>
>>> I've experimented a bit with Pas2JS and the Pas2JS-Widgets by 
>>> heliosroots ( 
>>> https://github.com/heliosroots/Pas2JS_Widget/tree/master ) and 
>>> implemented loading of unmodified LFM files. It's a bit cumbersome 
>>> for now, cause it needs changes in the project's HTML source as well 
>>> as the main project file for each form added, but as a proof of 
>>> concept it works. I've attached the patch that needs to be applied to 
>>> the Pas2JS-Widget master.
>>
>> Impressive :-)
>
> Thanks, though without your streaming implementation it would not have 
> been possible that easily. ;)

Well, with small steps we go ahead, each contributing where he can and
wishes...

>>> program tp2j;
>>>
>>> {$mode objfpc}
>>>
>>> uses
>>>   Forms, FormLoader, Interfaces, Unit1;
>>>
>>> procedure DoInit(aData: TObject);
>>> begin
>>>   Application.Initialize;
>>>   Application.CreateForm(TWForm1, WForm1);
>>>   Application.Run;
>>> end;
>>>
>>> begin
>>>   LoadForms([TWForm1], @DoInit, Nil);
>>> end.
>>>
>>> === code end ===
>>
>> I think you can avoid this change by checking the 'Run rtl when all 
>> page resources are fully loaded' options when creating your project.
>>
>> This will change the script tag to
>>   window.addEventListener("load", rtl.run);
>> so the program only runs when all forms are actually loaded, avoiding the
>> need to wait for the load.
>
> Sadly this does not work. I don't even see the browser fetching the 
> file... :'(

Strange. 
Probably because it does not recognize the type so it refuses to
load it automatically. Maybe a <link> element is better ?

>
>>
>> The changes to the html could be implemented without too much effort 
>> in the IDE package.
>>
>> Another approach would be to simply embed the .lfm in the html page in a
>> script tag.
>
> I personally would prefer to keep them separated to make versioning easier.

I understand this. 
Both approaches have their use.

>
> However what might be interesting (even for further functionality) might 
> be to run the HTML page through a template processor (*cough* fptemplate 
> *cough*) and replace some tag with the contents of all forms. Or 
> something like that...

I had the exact same thought when I wrote

"The changes to the html could be implemented without too much effort 
in the IDE package."

:-)

>>> - add a script tag in front of the project's script tag in the HTML 
>>> file:   <script src="unit1.lfm" type="application/x-lazarus-form" 
>>> id="TWForm1.lfm"></script>
>>> - play around with the form (add components (from the Pas2JS 
>>> component tab only!), add events, etc.)
>>> - before compiling make sure that the project *does not* contain a 
>>> requirement of Pas2JS_Designer_Package (the IDE currently adds this 
>>> when adding a component)
>>> - make sure that the *.LFM files are at the same location as the *.JS 
>>> file
>>> - load your application in the browser -> you should now see your form
>>>
>>> A screenshot showing the resulting browser and the IDE is attached. :)
>>
>> Nice job :-)
>
> Yes. Though I'd really like to know how TMS dealt with the sizes. 
> Currently the problem is when I do this on a 92 DPI Lazarus (my Win7 at 
> work) I get buttons and labels that are too small for their contents and 
> on my HighDPI Win10 the button and label looks huge compared to the 
> content... (obviously the widgets need to deal with the different 
> sizes/take into account the design DPI, but the principal problem of 
> WYSIWYG remains)

This is the problem of VCL/LCL : an outdated concept of (fixed) layout.

>>> This can obviously be further improved:
>>> - adding support also for frames and data modules
>>> - caching the converted object stream (so that it doesn't need to be 
>>> converted for each creation of a form)
>>> - add some way of asynchronous loading?
>>
>> There is the dynload unit which can help in this.
>
> I don't see a DynLoad unit. You mean Rtl.ScriptLoader and/or 
> Rtl.UnitLoader?

Sorry, yes. The name changed after I did the initial implementation,
and the old name is still in my head, sorry :(

> It's the former my FormLoader unit is based on. But 
> loading the content itself is not the problem (I've done that part 
> already ;) ). My (conceptual) problem is that the code is usually 
> synchronous. E.g. take the default main program:
>
  [snip]
>
> In the block marked with (*************) I'd essentially need to load 
> the form's resource dynamically from the server (the loading itself 
> isn't the problem) and then wait until all has been received. My 
> knowledge in JS isn't as good yet as to know the correct approach for 
> that, my gut tells me that something akin to "async/await" would be 
> necessary here.

You can't run await in the main loop so that would not work as you want it. 
There are no shortcuts for async, it is something to live with.

In essence, TApplication.CreateForm() will need to be async.

so a

TFormLoadedEvent = Procedure(Sender : TObject; aForm : TCustomForm) of Object;

Property Tapplication.OnFormLoaded : TFormLoadedEvent

is needed, allowing you to check that the form is loaded.

TApplication.Run then needs to wait till the main form is loaded before
showing it.

Michael.


More information about the Pas2js mailing list