frmmain.pp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. unit frmmain;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, webideintf, Forms, Controls, Graphics, Dialogs, EditBtn,
  6. ExtCtrls, ComCtrls, StdCtrls, ActnList, LazFileUtils, GlobalCefApplication,
  7. {$IFDEF DARWIN} uCEFLazarusCocoa, {$ENDIF}
  8. {$IFDEF WINDOWS}
  9. Windows, Messages,
  10. {$ENDIF}
  11. uCEFChromium, uCEFWindowParent, uCEFChromiumWindow, uCEFTypes, uCEFInterfaces,
  12. uCEFWinControl, uCEFApplication, uCEFWorkScheduler, uCEFBrowserWindow, fpJSON, uCEFChromiumEvents;
  13. type
  14. { TMainForm }
  15. TMainForm = class(TForm)
  16. AOpenDev: TAction;
  17. AGoExternal: TAction;
  18. AGo: TAction;
  19. ALWidgets: TActionList;
  20. BrowserWindow1: TBrowserWindow;
  21. FEProject: TFileNameEdit;
  22. ILWidgets: TImageList;
  23. BLog: TMemo;
  24. MLog: TMemo;
  25. Panel1: TPanel;
  26. PnlLog: TPanel;
  27. PnlBLog: TPanel;
  28. PCDesigner: TPageControl;
  29. Project: TLabel;
  30. PBottom: TPanel;
  31. BrowserLog: TTabSheet;
  32. Splitter1: TSplitter;
  33. Splitter2: TSplitter;
  34. TBExternalGo: TToolButton;
  35. TBExternalGo1: TToolButton;
  36. TSInspector: TTabSheet;
  37. TSBrowser: TTabSheet;
  38. TSLog: TTabSheet;
  39. TBWidgets: TToolBar;
  40. TBGo: TToolButton;
  41. ToolButton1: TToolButton;
  42. procedure AGoExecute(Sender: TObject);
  43. procedure AGoExternalExecute(Sender: TObject);
  44. procedure AGoUpdate(Sender: TObject);
  45. procedure AOpenDevExecute(Sender: TObject);
  46. procedure BrowserWindow1BrowserClosed(Sender: TObject);
  47. procedure BrowserWindow1BrowserCreated(Sender: TObject);
  48. procedure ChromiumConsoleMessage(Sender: TObject;
  49. const browser: ICefBrowser; level: TCefLogSeverity; const message,
  50. source: ustring; line: Integer; out Result: Boolean);
  51. procedure cwOnBeforePopup(Sender: TObject;
  52. const browser: ICefBrowser; const frame: ICefFrame; const targetUrl,
  53. targetFrameName: ustring; targetDisposition: TCefWindowOpenDisposition;
  54. userGesture: Boolean; const popupFeatures: TCefPopupFeatures;
  55. var windowInfo: TCefWindowInfo; var client: ICefClient;
  56. var settings: TCefBrowserSettings;
  57. var extra_info: ICefDictionaryValue;
  58. var noJavascriptAccess: Boolean;
  59. var Result: Boolean);
  60. procedure DEProjectEditingDone(Sender: TObject);
  61. procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
  62. procedure FormCreate(Sender: TObject);
  63. procedure Panel1Resize(Sender: TObject);
  64. private
  65. FFormClosing: Boolean;
  66. FClientID : Int64; // Just one for now
  67. FDesignCaption : String;
  68. FWebIDEIntf : TIDEServer;
  69. FWidgetCount : Integer;
  70. FWidgets : Array of String;
  71. FAllowGo: Boolean;
  72. {$IFDEF WINDOWS}
  73. procedure WMEnterMenuLoop(var aMessage: TMessage); message WM_ENTERMENULOOP;
  74. procedure WMExitMenuLoop(var aMessage: TMessage); message WM_EXITMENULOOP;
  75. {$ENDIF}
  76. function GetProjectURL: String;
  77. procedure DoAddWidget(Sender: TObject);
  78. procedure DoAction(Sender: TObject; aExchange: TIDEExchange);
  79. procedure DoClientCame(Sender: TObject; aClient: TIDEClient);
  80. procedure DoClientLeft(Sender: TObject; aClient: TIDEClient);
  81. procedure DoLogRequest(Sender: TObject; aURL: String);
  82. procedure IsWidgetEnabled(Sender: TObject);
  83. Procedure RegisterWidgets;
  84. Procedure RegisterWidget(aWidget: String; aImageIndex : Integer);
  85. public
  86. Procedure Log(Msg : String);
  87. Procedure Log(Fmt : String; Args : Array of const);
  88. end;
  89. var
  90. MainForm: TMainForm;
  91. implementation
  92. uses lclintf, fpmimetypes;
  93. {$R *.lfm}
  94. type
  95. { TLogMsg }
  96. TLogMsg = class
  97. private
  98. FMsg: String;
  99. public
  100. constructor Create(AMsg: String);
  101. procedure DoBrowserMsg(Data: PtrInt);
  102. procedure DoLog(Data: PtrInt);
  103. end;
  104. { TLogMsg }
  105. constructor TLogMsg.Create(AMsg: String);
  106. begin
  107. FMsg := AMsg;
  108. end;
  109. procedure TLogMsg.DoBrowserMsg(Data: PtrInt);
  110. begin
  111. if MainForm <> nil then
  112. MainForm.BLog.Append(FMsg);
  113. Free;
  114. end;
  115. procedure TLogMsg.DoLog(Data: PtrInt);
  116. begin
  117. if MainForm <> nil then
  118. MainForm.Log(FMsg);
  119. Free;
  120. end;
  121. { TMainForm }
  122. procedure TMainForm.DEProjectEditingDone(Sender: TObject);
  123. begin
  124. FWebIDEIntf.ProjectDir:=ExtractFilePath(FEProject.FileName);
  125. end;
  126. procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: boolean);
  127. begin
  128. FWebIDEIntf.Active:=False;
  129. FFormClosing := True;
  130. BrowserWindow1.CloseBrowser(True);
  131. CanClose:=BrowserWindow1.IsClosed;
  132. Visible := False;
  133. end;
  134. Function TMainForm.GetProjectURL : String;
  135. begin
  136. Result:=Format('http://localhost:%d/Project/%s',[FWebIDEIntf.Port,ExtractFileName(FEProject.FileName)]);
  137. end;
  138. procedure TMainForm.AGoExecute(Sender: TObject);
  139. Var
  140. URL : String;
  141. begin
  142. URL:=GetProjectURL;
  143. Log('Going to URL: %s',[URL]);
  144. BrowserWindow1.LoadURL(URL);
  145. end;
  146. procedure TMainForm.AGoExternalExecute(Sender: TObject);
  147. Var
  148. URL : String;
  149. begin
  150. URL:=GetProjectURL;
  151. Log('Going to URL: %s',[URL]);
  152. OpenURL(URL);
  153. end;
  154. procedure TMainForm.AGoUpdate(Sender: TObject);
  155. begin
  156. (Sender as Taction).Enabled:=FAllowGo;
  157. end;
  158. procedure TMainForm.AOpenDevExecute(Sender: TObject);
  159. var
  160. p: TPoint;
  161. begin
  162. p.X := 0;
  163. p.Y := 0;
  164. BrowserWindow1.Chromium.ShowDevTools(p,nil);
  165. end;
  166. procedure TMainForm.BrowserWindow1BrowserClosed(Sender: TObject);
  167. begin
  168. Close;
  169. end;
  170. procedure TMainForm.BrowserWindow1BrowserCreated(Sender: TObject);
  171. begin
  172. // Now the browser is fully initialized we can load the initial web page.
  173. FAllowGo:=True;
  174. end;
  175. procedure TMainForm.ChromiumConsoleMessage(Sender: TObject;
  176. const browser: ICefBrowser; level: TCefLogSeverity; const message,
  177. source: ustring; line: Integer; out Result: Boolean);
  178. var
  179. m: TLogMsg;
  180. begin
  181. if FFormClosing then
  182. exit;
  183. m := TLogMsg.Create(Format('%s [%s %d]', [message, source, line]));
  184. Application.QueueAsyncCall(@m.DoBrowserMsg, 0);
  185. // Issue https://gitlab.com/freepascal.org/fpc/source/-/issues/39367
  186. //Application.QueueAsyncCall(@TLogMsg.Create(Format('%s [%s %d]', [message, source, line])).DoBrowserMsg, 0);
  187. end;
  188. procedure TMainForm.FormCreate(Sender: TObject);
  189. var
  190. s: String;
  191. begin
  192. FAllowGo:=False;
  193. FDesignCaption:=Caption;
  194. MimeTypes.LoadKnownTypes;
  195. s := ExtractFilePath(Paramstr(0));
  196. if pos('nativedesign', s) > 0 then
  197. s := StringReplace(s, 'nativedesign', 'designdemo', [rfReplaceAll, rfIgnoreCase])
  198. else
  199. s := s+'designdemo';
  200. s := AppendPathDelim(s)+'designdemo.html';
  201. FEProject.FileName:=s;
  202. FWebIDEIntf:=TIDEServer.Create(Self);
  203. FWebIDEIntf.ProjectDir:=ExtractFilePath(FEProject.FileName);
  204. FWebIDEIntf.OnClientAdded:=@DoClientCame;
  205. FWebIDEIntf.OnClientRemoved:=@DoClientLeft;
  206. FWebIDEIntf.OnRequest:=@DoLogRequest;
  207. FWebIDEIntf.OnAction:=@DoAction;
  208. FWebIDEIntf.Active:=True;
  209. TSInspector.TabVisible:=False;
  210. RegisterWidgets;
  211. end;
  212. procedure TMainForm.Panel1Resize(Sender: TObject);
  213. begin
  214. //if not Visible then
  215. // exit;
  216. if Width = 0 then begin
  217. if MLog.Parent = PnlLog then begin
  218. MLog.Parent := TSLog;
  219. TSLog.TabVisible := True;
  220. end;
  221. if BLog.Parent = PnlBLog then begin
  222. BLog.Parent := BrowserLog;
  223. BrowserLog.TabVisible := True;
  224. end;
  225. end
  226. else begin
  227. if MLog.Parent = TSLog then begin
  228. MLog.Parent := PnlLog;
  229. TSLog.TabVisible := False;
  230. end;
  231. if BLog.Parent = BrowserLog then begin
  232. BLog.Parent := PnlBLog;
  233. BrowserLog.TabVisible := False;
  234. end;
  235. end;
  236. end;
  237. {$IFDEF WINDOWS}
  238. procedure TMainForm.WMEnterMenuLoop(var aMessage: TMessage);
  239. begin
  240. inherited;
  241. if (aMessage.wParam = 0) and (GlobalCEFApp <> nil) then
  242. GlobalCEFApp.OsmodalLoop := True;
  243. end;
  244. procedure TMainForm.WMExitMenuLoop(var aMessage: TMessage);
  245. begin
  246. inherited;
  247. if (aMessage.wParam = 0) and (GlobalCEFApp <> nil) then
  248. GlobalCEFApp.OsmodalLoop := False;
  249. end;
  250. {$ENDIF}
  251. procedure TMainForm.cwOnBeforePopup(Sender: TObject;
  252. const browser: ICefBrowser; const frame: ICefFrame; const targetUrl,
  253. targetFrameName: ustring; targetDisposition: TCefWindowOpenDisposition;
  254. userGesture: Boolean; const popupFeatures: TCefPopupFeatures;
  255. var windowInfo: TCefWindowInfo; var client: ICefClient;
  256. var settings: TCefBrowserSettings;
  257. var extra_info: ICefDictionaryValue;
  258. var noJavascriptAccess: Boolean;
  259. var Result: Boolean);
  260. begin
  261. // For simplicity, this demo blocks all popup windows and new tabs
  262. Result := (targetDisposition in [WOD_NEW_FOREGROUND_TAB, WOD_NEW_BACKGROUND_TAB, WOD_NEW_POPUP, WOD_NEW_WINDOW]);
  263. end;
  264. procedure TMainForm.DoAction(Sender: TObject; aExchange: TIDEExchange);
  265. var
  266. PayJSON : TJSONObject;
  267. begin
  268. payJSON:=Nil;
  269. if Not (aExchange.Payload is TJSONObject) then
  270. begin
  271. Log('Payload is not JSON Object');
  272. exit;
  273. end;
  274. payJSON:=aExchange.Payload as TJSONObject;
  275. with aExchange do
  276. case Name of
  277. 'create':
  278. Log('Browser created widget of class %s, name %s',[PayJSON.Get('class',''),PayJSON.Get('widget','')]);
  279. 'select':
  280. begin
  281. Log('Browser selected widget of class %s, name %s',[PayJSON.Get('class',''),PayJSON.Get('widget','')]);
  282. Log('Selected widget state: '+PayJSON.Get('state',''));
  283. end;
  284. end;
  285. end;
  286. procedure TMainForm.DoClientCame(Sender: TObject; aClient: TIDEClient);
  287. begin
  288. if FClientID>0 then
  289. Log('Ignoring second client (id: %d) attachment.',[aClient.ID])
  290. else
  291. begin
  292. FClientID:=aClient.ID;
  293. Caption:=FDesignCaption+Format(' [Client: %d]',[FClientID]);
  294. end;
  295. end;
  296. procedure TMainForm.DoAddWidget(Sender: TObject);
  297. Var
  298. Cmd : TIDECommand;
  299. aName : String;
  300. begin
  301. aName:=FWidgets[(Sender as TAction).Tag];
  302. Cmd:=TIDECommand.Create;
  303. Cmd.NeedsConfirmation:=True;
  304. Cmd.ClientID:=FClientID;
  305. Cmd.name:='addWidget';
  306. Cmd.PayLoad:=TJSONObject.Create(['class','T'+aName+'Widget']);
  307. FWebIDEIntf.SendCommand(cmd);
  308. end;
  309. procedure TMainForm.DoClientLeft(Sender: TObject; aClient: TIDEClient);
  310. begin
  311. if (aClient.ID=FClientID) then
  312. begin
  313. FClientID:=-1;
  314. Caption:=FDesignCaption;
  315. end;
  316. end;
  317. procedure TMainForm.DoLogRequest(Sender: TObject; aURL: String);
  318. var
  319. m: TLogMsg;
  320. begin
  321. if FFormClosing then
  322. exit;
  323. m := TLogMsg.Create('Internal server request received: '+aURL);
  324. Application.QueueAsyncCall(@m.DoLog, 0);
  325. // Issue https://gitlab.com/freepascal.org/fpc/source/-/issues/39367
  326. //Application.QueueAsyncCall(@TLogMsg.Create('Internal server request received: '+FURL).DoLog, 0);
  327. end;
  328. procedure TMainForm.IsWidgetEnabled(Sender: TObject);
  329. begin
  330. (Sender as TAction).Enabled:=(FClientID<>-1);
  331. end;
  332. procedure TMainForm.RegisterWidgets;
  333. begin
  334. SetLength(FWidgets,9);
  335. FWidgetCount:=0;
  336. RegisterWidget('Button',2);
  337. RegisterWidget('CheckBoxInput',3);
  338. RegisterWidget('RadioInput',4);
  339. RegisterWidget('TextInput',5);
  340. RegisterWidget('Image',6);
  341. RegisterWidget('TextArea',7);
  342. RegisterWidget('Select',8);
  343. RegisterWidget('Container',9);
  344. RegisterWidget('Jumbo',10);
  345. end;
  346. procedure TMainForm.RegisterWidget(aWidget: String; aImageIndex: Integer);
  347. Var
  348. A : TAction;
  349. B : TToolButton;
  350. L,i : Integer;
  351. begin
  352. FWidgets[FWidgetCount]:=aWidget;
  353. A:=TAction.Create(Self);
  354. A.ActionList:=ALWidgets;
  355. A.Name:='AAdd'+aWidget;
  356. A.Hint:='Add '+aWidget;
  357. A.Caption:='Add '+aWidget;
  358. A.ImageIndex:=aImageIndex;
  359. A.Tag:=FWidgetCount;
  360. A.OnExecute:=@DoAddWidget;
  361. A.OnUpdate:=@IsWidgetEnabled;
  362. L:=0;
  363. For I:=0 to TBWidgets.ControlCount-1 do
  364. if TBWidgets.Controls[i].BoundsRect.Right>L then
  365. L:=TBWidgets.Controls[i].BoundsRect.Right;
  366. B:=TToolButton.Create(Self);
  367. B.Parent:=TBWidgets;
  368. B.Left:=L;
  369. B.Height:=32;
  370. B.Action:=A;
  371. inc(FWidgetCount);
  372. // TBWidgets.AddControl;
  373. end;
  374. procedure TMainForm.Log(Msg: String);
  375. begin
  376. MLog.Lines.Add(Msg);
  377. end;
  378. procedure TMainForm.Log(Fmt: String; Args: array of const);
  379. begin
  380. Log(Format(Fmt,Args));
  381. end;
  382. initialization
  383. {$IFDEF DARWIN}
  384. AddCrDelegate;
  385. {$ENDIF}
  386. if GlobalCEFApp = nil then begin
  387. CreateGlobalCEFApp;
  388. if not GlobalCEFApp.StartMainProcess then begin
  389. DestroyGlobalCEFApp;
  390. DestroyGlobalCEFWorkScheduler;
  391. halt(0); // exit the subprocess
  392. end;
  393. end;
  394. finalization
  395. (* Destroy from this unit, which is used after "Interfaces". So this happens before the Application object is destroyed *)
  396. if GlobalCEFWorkScheduler <> nil then
  397. GlobalCEFWorkScheduler.StopScheduler;
  398. DestroyGlobalCEFApp;
  399. DestroyGlobalCEFWorkScheduler;
  400. end.