Recent

Author Topic: web widgetset Components to code  (Read 4323 times)

warleyalex

  • New Member
  • *
  • Posts: 24
web widgetset Components to code
« on: March 02, 2018, 10:30:47 pm »
It is on the oven a tool named TMS Web Core that actually use the Delphi XE7 and its form designer to create web applications. You Just drop components onto a form and set up at the objector inspector, the events and the properties.

All of the above is great and it will no doubt give us the same benefits as Delphi/Lazarus developers currently enjoy. The pas2js compiler will auto generate
the components for you.

It has been said in Delphi it works absolutely splendid. I was recently analyzing the generated code of the so called TMS Web Core framework, and and quite a bit of effort, doing some reverve engineering I believe I created a clone!

Since Delphi/Lazarus comes with a bunch of ready to use UI elements and widgets like modals, popups, popover, searchbar, side panels, headerBar, jsut been thinking to create lightweight kind of "fake" web widgets to be used into the Lazarus designer.

Michael Van Canneyt has suggested: "All you need to do is create a web-widgetset for the LCL." 

I've spent a few minutes reflecting on this, what's the hack is widgetset for the LCL? I've been using Lazarus less than one month. Anyway, using Package/New Package... I've created some visual web "dummy" components (TWebLabel, TWEBEdit, TWebButton, TWebComboBox and TWebMemo) all those components inherits from the TCustomControl. The idea would be drag'n drop the components on the form. After all, the main form unit looks like this:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   SysUtils, Classes, JS, Web,
  9.   WEBLib.Graphics, WEBLIB.Controls, WEBLib.StdCtrls, WEBLib.ExtCtrls,
  10.   WEBLib.Forms,  WEBLib.Dialogs;
  11.  
  12. type
  13.   TForm1 = class(TWebForm)
  14.   private
  15.     { Private declarations }
  16.     WebLabel1: TWebLabel;
  17.     WebEdit1: TWebEdit;
  18.     WebButton1: TWebButton;
  19.     WebMemo1: TWebMemo;
  20.     WebComboBox1: TWebComboBox;
  21.     WebPanel1: TWebPanel;
  22.     WebLabel2: TWebLabel;
  23.     WebImageControl1: TWebImage;
  24.   public
  25.     { Public declarations }
  26.     procedure LoadDFMValues; override;
  27.     procedure WebButton1Click(Sender: TObject);
  28.     procedure WebComboBox1Change(Sender: TObject);
  29.   end;
  30.  
  31. var
  32.   Form1: TForm1;
  33.  
  34. implementation
  35.  
  36. {$R *.lfm}
  37.  
  38. { TForm1 }
  39.  
  40. procedure TForm1.LoadDFMValues;
  41. begin
  42.   inherited;
  43.   //LoadDFMFromStream('Unit1.dfm');
  44.  
  45. end;
  46.  
  47. procedure TForm1.WebButton1Click(Sender: TObject);
  48. begin
  49.   console.log('button clicked');
  50.   WebComboBox1.Items.Add(WebEdit1.Text);
  51.   WebComboBox1.ItemIndex := WebComboBox1.Items.Count - 1;
  52.   WebMemo1.Lines.Add(WebEdit1.Text);
  53. end;
  54.  
  55. procedure TForm1.WebComboBox1Change(Sender: TObject);
  56. begin
  57.   WebLabel1.Caption := WebComboBox1.Items[WebComboBox1.GetItemIndex];
  58. end;
  59.  
  60. end.
  61.  

It is a little bit different from win32 app. Notice the LoadDFMValues method.

TMS Web Core does crazy things such auto generates the LoadDFMValues method.  If you don't know what the hell I'm talking about, in the web application world the DFM/LFM forms are embedded on the javascript, so the method should have such content:


procedure TForm1.LoadDFMValues;
begin
  inherited;

  WebLabel1        := TWebLabel.Create(Self);
  WebEdit1         := TWebEdit.Create(Self);
  WebButton1       := TWebButton.Create(Self);
  WebMemo1         := TWebMemo.Create(Self);
  WebComboBox1     := TWebComboBox.Create(Self);
  WebPanel1        := TWebPanel.Create(Self);
  WebLabel2        := TWebLabel.Create(WebPanel1);
  WebImageControl1 := TWebImage.Create(WebPanel1);

  WebLabel1.BeginUpdate;
  WebEdit1.BeginUpdate;
  WebButton1.BeginUpdate;
  WebMemo1.BeginUpdate;
  WebComboBox1.BeginUpdate;
  WebPanel1.BeginUpdate;
  WebLabel2.BeginUpdate;
  WebImageControl1.BeginUpdate;

  try
    Self.Name           := 'Form1';
    Self.Left           := 0;
    Self.Top            := 0;
    Self.Width          := 640;
    Self.Height         := 480;
    Self.Font.Charset   := 1;
    Self.Font.Color     := 0;
    Self.Font.Height    := -13;
    Self.Font.Name      := 'Tahoma';
    Self.Font.Style     := [fsBold];
    Self.FFormContainer := 'appcontent';
    Self.TabOrder       := 1;

    WebLabel1.Parent  := Self;
    WebLabel1.Name    := 'WebLabel1';
    WebLabel1.Left    := 64;
    WebLabel1.Top     := 232;
    WebLabel1.Width   := 40;
    WebLabel1.Height  := 16;
    WebLabel1.Caption := '-Label-';
    WebEdit1.Parent   := Self;

    WebEdit1.Name     := 'WebEdit1';
    WebEdit1.Left     := 64;
    WebEdit1.Top      := 42;
    WebEdit1.Width    := 121;
    WebEdit1.Height   := 24;
    WebEdit1.TabOrder := 0;
    WebEdit1.TextHint := 'Add some text ...';

    WebButton1.Parent   := Self;
    WebButton1.Name     := 'WebButton1';
    WebButton1.Left     := 204;
    WebButton1.Top      := 40;
    WebButton1.Width    := 75;
    WebButton1.Height   := 25;
    WebButton1.Caption  := 'Add';
    WebButton1.OnClick := @WebButton1Click;
    WebButton1.TabOrder := 1;

    WebMemo1.Parent     := Self;
    WebMemo1.Name       := 'WebMemo1';
    WebMemo1.Left       := 64;
    WebMemo1.Top        := 88;
    WebMemo1.Width      := 215;
    WebMemo1.Height     := 89;
    WebMemo1.AutoSize   := false;
    WebMemo1.SelLength  := 0;
    WebMemo1.SelStart   := 0;
    WebMemo1.TabOrder   := 2;

    WebComboBox1.Parent     := Self;
    WebComboBox1.Name       := 'WebComboBox1';
    WebComboBox1.Left       := 64;
    WebComboBox1.Top        := 197;
    WebComboBox1.Width      := 215;
    WebComboBox1.Height     := 24;
    WebComboBox1.ItemIndex  := -1;
    WebComboBox1.TabOrder   := 3;
    WebComboBox1.Text       := 'WebComboBox1';
    WebComboBox1.OnChange  := @WebComboBox1Change;

    WebPanel1.Parent      := Self;
    WebPanel1.Name        := 'WebPanel1';
    WebPanel1.Left        := 64;
    WebPanel1.Top         := 272;
    WebPanel1.Width       := 513;
    WebPanel1.Height      := 89;
    WebPanel1.WidthStyle  := TSizeStyle.ssPercent;
    WebPanel1.WidthPercent:= 80;
    WebPanel1.BorderStyle := TBorderStyle.bsSingle;

    WebLabel2.Parent      := WebPanel1;
    WebLabel2.Name        := 'WebLabel2';
    WebLabel2.Left        := 3;
    WebLabel2.Top         := 29;
    WebLabel2.Width       := 411;
    WebLabel2.Height      := 32;
    WebLabel2.Caption     := 'This demo shows the use of basic controls like TWebEdit, TWebButton, TWebComboBox, TWebMemo and TWebLabel.';
    WebLabel2.WordWrap   := true;
    WebLabel2.WidthStyle  := TSizeStyle.ssPercent;

    WebImageControl1.Parent    := WebPanel1;
    WebImageControl1.Name      := 'WebImageControl1';
    WebImageControl1.Left      := 6;
    WebImageControl1.Top       := 7;
    WebImageControl1.Width     := 16;
    WebImageControl1.Height    := 16;
    WebImageControl1.AutoSize := true;
    WebImageControl1.Picture.LoadFromFile('Picture.png');
  finally

    WebLabel1.EndUpdate;
    WebEdit1.EndUpdate;
    WebButton1.EndUpdate;
    WebMemo1.EndUpdate;
    WebComboBox1.EndUpdate;
    WebPanel1.EndUpdate;
    WebLabel2.EndUpdate;
    WebImageControl1.EndUpdate;

  end;
end;


Exactly, they're using kinda magical trick, scan and parse the .DFM then generates the creation code for the current form and its owned components, pretty close to GExpert feature "Components to Code" plugin.

Any idea how to implement the "Components to Code" using Lazarus?

Regards,

warleyalex

Phil

  • Hero Member
  • *****
  • Posts: 2737
Re: web widgetset Components to code
« Reply #1 on: March 02, 2018, 10:57:01 pm »
Any idea how to implement the "Components to Code" using Lazarus?

I think you've already answered the question: Parse the .lfm file and generate the code. IDEs have been doing that for years.


engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: web widgetset Components to code
« Reply #2 on: March 03, 2018, 03:31:41 am »
Take a look at JsonToObject and ObjectToJson in unit fpjsonrtti.

warleyalex

  • New Member
  • *
  • Posts: 24
Re: web widgetset Components to code
« Reply #3 on: March 05, 2018, 02:12:44 pm »
The solution is almost 100%-ready! There's a component serialisation into Pascal in the oven but not integrated with the Lazarus IDE yet.

ttomas

  • Full Member
  • ***
  • Posts: 245
Re: web widgetset Components to code
« Reply #4 on: March 05, 2018, 03:40:39 pm »
Best solution is to integrate in pas2js compiler.
If you look the source in
pas2js\compiler\packages\pastojs\src
pas2js\compiler\packages\fcl-passrc\src
 you can find a lot of interesting code.
1. Parse *.pas and look for {$R *.lfm} resource.
2. Parse lfm
3. Compile pas2js and include/link/??? lfm code in *.js

Idea is to have only one *.pas file.
« Last Edit: March 05, 2018, 03:49:51 pm by ttomas »

warleyalex

  • New Member
  • *
  • Posts: 24
Re: web widgetset Components to code
« Reply #5 on: March 05, 2018, 11:50:23 pm »
LFM/DFM-serialization mechanism which Lazarus/Delphi ships with may be pretty useful.
Let's start from the simple example. As you can see LFM-format is pretty straightforward.

Code: Pascal  [Select][+][-]
  1. object Form1: TForm1
  2.   Left = 350
  3.   Height = 480
  4.   Hint = 'Form1'
  5.   Top = 0
  6.   Width = 640
  7.   Caption = 'Form1'
  8.   ClientHeight = 480
  9.   ClientWidth = 640
  10.   LCLVersion = '1.9.0.0'
  11.   object WebLabel1: TLabel
  12.     Left = 64
  13.     Height = 13
  14.     Top = 232
  15.     Width = 33
  16.     Caption = '-Label-'
  17.     ParentColor = False
  18.   end
  19.   object WebEdit1: TEdit
  20.     Left = 64
  21.     Height = 21
  22.     Top = 42
  23.     Width = 121
  24.     ParentShowHint = False
  25.     ShowHint = True
  26.     TabOrder = 0
  27.     Text = 'WebEdit1'
  28.     TextHint = 'Add some text ...'
  29.   end
  30.   object WebButton1: TButton
  31.     Left = 204
  32.     Height = 25
  33.     Top = 40
  34.     Width = 75
  35.     Caption = 'Add'
  36.     OnClick = WebButton1Click
  37.     TabOrder = 1
  38.   end
  39.   object WebMemo1: TMemo
  40.     Left = 64
  41.     Height = 145
  42.     Top = 89
  43.     Width = 215
  44.     Lines.Strings = (
  45.       'WebMemo1'
  46.     )
  47.     TabOrder = 2
  48.   end
  49.   object WebComboBox1: TComboBox
  50.     Left = 64
  51.     Height = 21
  52.     Top = 197
  53.     Width = 215
  54.     ItemHeight = 13
  55.     OnChange = WebComboBox1Change
  56.     TabOrder = 3
  57.     Text = 'WebComboBox1'
  58.   end
  59.   object WebPanel1: TPanel
  60.     Left = 64
  61.     Height = 89
  62.     Top = 280
  63.     Width = 513
  64.     ClientHeight = 89
  65.     ClientWidth = 513
  66.     TabOrder = 4
  67.     object WebLabel2: TLabel
  68.       Left = 3
  69.       Height = 32
  70.       Top = 29
  71.       Width = 411
  72.       AutoSize = False
  73.       Caption = 'This demo shows the use of basic controls like TWebEdit, TWebButton, TWebComboBox, TWebMemo and TWebLabel.'
  74.       ParentColor = False
  75.       WordWrap = True
  76.     end
  77.     object WebImageControl1: TImage
  78.       Left = 6
  79.       Height = 16
  80.       Top = 7
  81.       Width = 16
  82.       AutoSize = True
  83.     end
  84.   end
  85.   object Btn: TButton
  86.     Left = 64
  87.     Height = 33
  88.     Top = 384
  89.     Width = 95
  90.     Caption = 'Btn'
  91.     OnClick = BtnClick
  92.     TabOrder = 5
  93.   end
  94. end
  95.  
I have modified the pascalstream example to include the BeginUpdade..EndUpdate block and we serialise the form into Pascal:
Code: Pascal  [Select][+][-]
  1. WebLabel1:=TLabel.Create(Self);
  2. WebEdit1:=TEdit.Create(Self);
  3. WebButton1:=TButton.Create(Self);
  4. WebMemo1:=TMemo.Create(Self);
  5. WebComboBox1:=TComboBox.Create(Self);
  6. WebPanel1:=TPanel.Create(Self);
  7. WebLabel2:=TLabel.Create(Self);
  8. WebImageControl1:=TImage.Create(Self);
  9. Btn:=TButton.Create(Self);
  10.  
  11. WebLabel1.BeginUpdate;
  12. WebEdit1.BeginUpdate;
  13. WebButton1.BeginUpdate;
  14. WebMemo1.BeginUpdate;
  15. WebComboBox1.BeginUpdate;
  16. WebPanel1.BeginUpdate;
  17. WebLabel2.BeginUpdate;
  18. WebImageControl1.BeginUpdate;
  19. Btn.BeginUpdate;
  20. try
  21.   Name:='Form1';
  22.   Left:=350;
  23.   Height:=480;
  24.   Top:=0;
  25.   Width:=640;
  26.   Caption:='Form1';
  27.   ClientHeight:=480;
  28.   ClientWidth:=640;
  29.   LCLVersion:='1.9.0.0';
  30.   with WebLabel1 do begin
  31.     Name:='WebLabel1';
  32.     Left:=64;
  33.     Height:=13;
  34.     Top:=232;
  35.     Width:=33;
  36.     Caption:='-Label-';
  37.     ParentColor:=False;
  38.     TPasStreamAccess(TComponent(WebLabel1)).SetParentComponent(Self);
  39.   end;
  40.   with WebEdit1 do begin
  41.     Name:='WebEdit1';
  42.     Left:=64;
  43.     Height:=21;
  44.     Top:=42;
  45.     Width:=121;
  46.     ParentShowHint:=False;
  47.     ShowHint:=True;
  48.     TabOrder:=0;
  49.     Text:='WebEdit1';
  50.     TextHint:='Add some text ...';
  51.     TPasStreamAccess(TComponent(WebEdit1)).SetParentComponent(Self);
  52.   end;
  53.   with WebButton1 do begin
  54.     Name:='WebButton1';
  55.     Left:=204;
  56.     Height:=25;
  57.     Top:=40;
  58.     Width:=75;
  59.     Caption:='Add';
  60.     OnClick:=@WebButton1Click;
  61.     TabOrder:=1;
  62.     TPasStreamAccess(TComponent(WebButton1)).SetParentComponent(Self);
  63.   end;
  64.   with WebMemo1 do begin
  65.     Name:='WebMemo1';
  66.     Left:=64;
  67.     Height:=145;
  68.     Top:=89;
  69.     Width:=215;
  70.     ExecCustomCSP(Lines,[#7'Strings'#1#6#8'WebMemo1'#0#0]);
  71.     TabOrder:=2;
  72.     TPasStreamAccess(TComponent(WebMemo1)).SetParentComponent(Self);
  73.   end;
  74.   with WebComboBox1 do begin
  75.     Name:='WebComboBox1';
  76.     Left:=64;
  77.     Height:=21;
  78.     Top:=197;
  79.     Width:=215;
  80.     ItemHeight:=13;
  81.     OnChange:=@WebComboBox1Change;
  82.     TabOrder:=3;
  83.     Text:='WebComboBox1';
  84.     TPasStreamAccess(TComponent(WebComboBox1)).SetParentComponent(Self);
  85.   end;
  86.   with WebPanel1 do begin
  87.     Name:='WebPanel1';
  88.     Left:=64;
  89.     Height:=89;
  90.     Top:=280;
  91.     Width:=513;
  92.     ClientHeight:=89;
  93.     ClientWidth:=513;
  94.     TabOrder:=4;
  95.     TPasStreamAccess(TComponent(WebPanel1)).SetParentComponent(Self);
  96.     with WebLabel2 do begin
  97.       Name:='WebLabel2';
  98.       Left:=3;
  99.       Height:=32;
  100.       Top:=29;
  101.       Width:=411;
  102.       AutoSize:=False;
  103.       Caption:='This demo shows the use of basic controls like TWebEdit, TWebButton, TWebComboBox, TWebMemo and TWebLabel.';
  104.       ParentColor:=False;
  105.       WordWrap:=True;
  106.       TPasStreamAccess(TComponent(WebLabel2)).SetParentComponent(WebPanel1);
  107.     end;
  108.     with WebImageControl1 do begin
  109.       Name:='WebImageControl1';
  110.       Left:=6;
  111.       Height:=16;
  112.       Top:=7;
  113.       Width:=16;
  114.       AutoSize:=True;
  115.       TPasStreamAccess(TComponent(WebImageControl1)).SetParentComponent(WebPanel1);
  116.     end;
  117.   end;
  118.   with Btn do begin
  119.     Name:='Btn';
  120.     Left:=64;
  121.     Height:=33;
  122.     Top:=384;
  123.     Width:=95;
  124.     Caption:='Btn';
  125.     OnClick:=@BtnClick;
  126.     TabOrder:=5;
  127.     TPasStreamAccess(TComponent(Btn)).SetParentComponent(Self);
  128.   end;
  129.   // Component serialized as Pascal - End
  130. finally
  131.   WebLabel1.EndUpdate;
  132.   WebEdit1.EndUpdate;
  133.   WebButton1.EndUpdate;
  134.   WebMemo1.EndUpdate;
  135.   WebComboBox1.EndUpdate;
  136.   WebPanel1.EndUpdate;
  137.   WebLabel2.EndUpdate;
  138.   WebImageControl1.EndUpdate;
  139.   Btn.EndUpdate;
  140. end;
  141.  
  142.  

I think the best solution is to integrate the DFM2PAS serialization in pas2js compiler.

Each time the project is recompiled, all "form units" are automatically generated.

and we can just add a directive at the LoadDFMValue for instance.

//The source code below is displayed for informational purposes only
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   SysUtils, Classes, JS, Web,
  9.   WEBLib.Graphics, WEBLIB.Controls, WEBLib.StdCtrls, WEBLib.ExtCtrls,   WEBLib.Forms,  WEBLib.Dialogs;
  10.  
  11. type
  12.   TForm1 = class(TWebForm)
  13.   private
  14.     { Private declarations }
  15.     WebLabel1        : TWebLabel;
  16.     WebEdit1         : TWebEdit;
  17.     WebButton1       : TWebButton;
  18.     WebMemo1         : TWebMemo;
  19.     WebComboBox1     : TWebComboBox;
  20.     WebPanel1        : TWebPanel;
  21.     WebLabel2        : TWebLabel;
  22.     WebImageControl1 : TWebImage;
  23.   public
  24.     { Public declarations }
  25.     procedure LoadDFMValues; override;
  26.     procedure WebButton1Click(Sender: TObject);
  27.     procedure WebComboBox1Change(Sender: TObject);
  28.   end;
  29.  
  30. var
  31.   Form1: TForm1;
  32.  
  33. implementation
  34.  
  35. //{$R *.lfm}
  36.  
  37. { TForm1 }
  38.  
  39. procedure TForm1.LoadDFMValues;
  40. begin
  41.   inherited;
  42.   {$I 'Form1:impl'}
  43.  
  44. end;
  45.  
  46. procedure TForm1.WebButton1Click(Sender: TObject);
  47. begin
  48.   console.log('button clicked');
  49.   WebComboBox1.Items.Add(WebEdit1.Text);
  50.   WebComboBox1.ItemIndex := WebComboBox1.Items.Count - 1;
  51.   WebMemo1.Lines.Add(WebEdit1.Text);
  52. end;
  53.  
  54. procedure TForm1.WebComboBox1Change(Sender: TObject);
  55. begin
  56.   WebLabel1.Caption := WebComboBox1.Items[WebComboBox1.GetItemIndex];
  57. end;
  58.  
  59. initialization
  60. RegisterForm({$I %FILE%}, TForm1);
  61.  
  62. end.
  63.  




RazorGrass

  • Newbie
  • Posts: 2
Re: web widgetset Components to code
« Reply #6 on: March 22, 2018, 05:21:55 pm »
Hi warleyalex,
I don't know if this related or even possible. I saw your post on Framework7 many months ago and am now ready to get going on a F7 project. I am seeing it was done with SmartPascal. Are the files downloadable, working, still a thing? Can it be ported to work in Lazarus?

https://muut.com/i/framework7/getting-started:f7-with-delphi-pascal-like
« Last Edit: March 22, 2018, 05:25:02 pm by RazorGrass »

 

TinyPortal © 2005-2018