mirror of
https://gitlab.com/freepascal.org/fpc/pas2js.git
synced 2025-04-09 15:37:47 +02:00
463 lines
12 KiB
ObjectPascal
463 lines
12 KiB
ObjectPascal
unit frmmain;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, webideintf, Forms, Controls, Graphics, Dialogs, EditBtn,
|
|
ExtCtrls, ComCtrls, StdCtrls, ActnList, LazFileUtils, GlobalCefApplication,
|
|
{$IFDEF DARWIN} uCEFLazarusCocoa, {$ENDIF}
|
|
{$IFDEF WINDOWS}
|
|
Windows, Messages,
|
|
{$ENDIF}
|
|
uCEFChromium, uCEFWindowParent, uCEFChromiumWindow, uCEFTypes, uCEFInterfaces,
|
|
uCEFWinControl, uCEFApplication, uCEFWorkScheduler, uCEFBrowserWindow, fpJSON, uCEFChromiumEvents;
|
|
|
|
type
|
|
|
|
{ TMainForm }
|
|
|
|
TMainForm = class(TForm)
|
|
AOpenDev: TAction;
|
|
AGoExternal: TAction;
|
|
AGo: TAction;
|
|
ALWidgets: TActionList;
|
|
BrowserWindow1: TBrowserWindow;
|
|
FEProject: TFileNameEdit;
|
|
ILWidgets: TImageList;
|
|
BLog: TMemo;
|
|
MLog: TMemo;
|
|
Panel1: TPanel;
|
|
PnlLog: TPanel;
|
|
PnlBLog: TPanel;
|
|
PCDesigner: TPageControl;
|
|
Project: TLabel;
|
|
PBottom: TPanel;
|
|
BrowserLog: TTabSheet;
|
|
Splitter1: TSplitter;
|
|
Splitter2: TSplitter;
|
|
TBExternalGo: TToolButton;
|
|
TBExternalGo1: TToolButton;
|
|
TSInspector: TTabSheet;
|
|
TSBrowser: TTabSheet;
|
|
TSLog: TTabSheet;
|
|
TBWidgets: TToolBar;
|
|
TBGo: TToolButton;
|
|
ToolButton1: TToolButton;
|
|
procedure AGoExecute(Sender: TObject);
|
|
procedure AGoExternalExecute(Sender: TObject);
|
|
procedure AGoUpdate(Sender: TObject);
|
|
procedure AOpenDevExecute(Sender: TObject);
|
|
procedure BrowserWindow1BrowserClosed(Sender: TObject);
|
|
procedure BrowserWindow1BrowserCreated(Sender: TObject);
|
|
procedure ChromiumConsoleMessage(Sender: TObject;
|
|
const browser: ICefBrowser; level: TCefLogSeverity; const message,
|
|
source: ustring; line: Integer; out Result: Boolean);
|
|
procedure cwOnBeforePopup(Sender: TObject;
|
|
const browser: ICefBrowser; const frame: ICefFrame; const targetUrl,
|
|
targetFrameName: ustring; targetDisposition: TCefWindowOpenDisposition;
|
|
userGesture: Boolean; const popupFeatures: TCefPopupFeatures;
|
|
var windowInfo: TCefWindowInfo; var client: ICefClient;
|
|
var settings: TCefBrowserSettings;
|
|
var extra_info: ICefDictionaryValue;
|
|
var noJavascriptAccess: Boolean;
|
|
var Result: Boolean);
|
|
procedure DEProjectEditingDone(Sender: TObject);
|
|
procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
|
|
procedure FormCreate(Sender: TObject);
|
|
procedure Panel1Resize(Sender: TObject);
|
|
private
|
|
FFormClosing: Boolean;
|
|
FClientID : Int64; // Just one for now
|
|
FDesignCaption : String;
|
|
FWebIDEIntf : TIDEServer;
|
|
FWidgetCount : Integer;
|
|
FWidgets : Array of String;
|
|
FAllowGo: Boolean;
|
|
{$IFDEF WINDOWS}
|
|
procedure WMEnterMenuLoop(var aMessage: TMessage); message WM_ENTERMENULOOP;
|
|
procedure WMExitMenuLoop(var aMessage: TMessage); message WM_EXITMENULOOP;
|
|
{$ENDIF}
|
|
function GetProjectURL: String;
|
|
procedure DoAddWidget(Sender: TObject);
|
|
procedure DoAction(Sender: TObject; aExchange: TIDEExchange);
|
|
procedure DoClientCame(Sender: TObject; aClient: TIDEClient);
|
|
procedure DoClientLeft(Sender: TObject; aClient: TIDEClient);
|
|
procedure DoLogRequest(Sender: TObject; aURL: String);
|
|
procedure IsWidgetEnabled(Sender: TObject);
|
|
Procedure RegisterWidgets;
|
|
Procedure RegisterWidget(aWidget: String; aImageIndex : Integer);
|
|
public
|
|
Procedure Log(Msg : String);
|
|
Procedure Log(Fmt : String; Args : Array of const);
|
|
end;
|
|
|
|
var
|
|
MainForm: TMainForm;
|
|
|
|
implementation
|
|
|
|
uses lclintf, fpmimetypes;
|
|
|
|
{$R *.lfm}
|
|
|
|
type
|
|
|
|
{ TLogMsg }
|
|
|
|
TLogMsg = class
|
|
private
|
|
FMsg: String;
|
|
public
|
|
constructor Create(AMsg: String);
|
|
procedure DoBrowserMsg(Data: PtrInt);
|
|
procedure DoLog(Data: PtrInt);
|
|
end;
|
|
|
|
|
|
{ TLogMsg }
|
|
|
|
constructor TLogMsg.Create(AMsg: String);
|
|
begin
|
|
FMsg := AMsg;
|
|
end;
|
|
|
|
procedure TLogMsg.DoBrowserMsg(Data: PtrInt);
|
|
begin
|
|
if MainForm <> nil then
|
|
MainForm.BLog.Append(FMsg);
|
|
Free;
|
|
end;
|
|
|
|
procedure TLogMsg.DoLog(Data: PtrInt);
|
|
begin
|
|
if MainForm <> nil then
|
|
MainForm.Log(FMsg);
|
|
Free;
|
|
end;
|
|
|
|
{ TMainForm }
|
|
|
|
procedure TMainForm.DEProjectEditingDone(Sender: TObject);
|
|
begin
|
|
FWebIDEIntf.ProjectDir:=ExtractFilePath(FEProject.FileName);
|
|
end;
|
|
|
|
procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: boolean);
|
|
begin
|
|
FWebIDEIntf.Active:=False;
|
|
FFormClosing := True;
|
|
BrowserWindow1.CloseBrowser(True);
|
|
CanClose:=BrowserWindow1.IsClosed;
|
|
Visible := False;
|
|
end;
|
|
|
|
Function TMainForm.GetProjectURL : String;
|
|
|
|
begin
|
|
Result:=Format('http://localhost:%d/Project/%s',[FWebIDEIntf.Port,ExtractFileName(FEProject.FileName)]);
|
|
end;
|
|
|
|
procedure TMainForm.AGoExecute(Sender: TObject);
|
|
Var
|
|
URL : String;
|
|
|
|
begin
|
|
URL:=GetProjectURL;
|
|
Log('Going to URL: %s',[URL]);
|
|
BrowserWindow1.LoadURL(URL);
|
|
end;
|
|
|
|
procedure TMainForm.AGoExternalExecute(Sender: TObject);
|
|
Var
|
|
URL : String;
|
|
|
|
begin
|
|
URL:=GetProjectURL;
|
|
Log('Going to URL: %s',[URL]);
|
|
OpenURL(URL);
|
|
end;
|
|
|
|
procedure TMainForm.AGoUpdate(Sender: TObject);
|
|
begin
|
|
(Sender as Taction).Enabled:=FAllowGo;
|
|
end;
|
|
|
|
procedure TMainForm.AOpenDevExecute(Sender: TObject);
|
|
var
|
|
p: TPoint;
|
|
begin
|
|
p.X := 0;
|
|
p.Y := 0;
|
|
BrowserWindow1.Chromium.ShowDevTools(p,nil);
|
|
end;
|
|
|
|
procedure TMainForm.BrowserWindow1BrowserClosed(Sender: TObject);
|
|
begin
|
|
Close;
|
|
end;
|
|
|
|
procedure TMainForm.BrowserWindow1BrowserCreated(Sender: TObject);
|
|
begin
|
|
// Now the browser is fully initialized we can load the initial web page.
|
|
FAllowGo:=True;
|
|
end;
|
|
|
|
procedure TMainForm.ChromiumConsoleMessage(Sender: TObject;
|
|
const browser: ICefBrowser; level: TCefLogSeverity; const message,
|
|
source: ustring; line: Integer; out Result: Boolean);
|
|
var
|
|
m: TLogMsg;
|
|
begin
|
|
if FFormClosing then
|
|
exit;
|
|
m := TLogMsg.Create(Format('%s [%s %d]', [message, source, line]));
|
|
Application.QueueAsyncCall(@m.DoBrowserMsg, 0);
|
|
// Issue https://gitlab.com/freepascal.org/fpc/source/-/issues/39367
|
|
//Application.QueueAsyncCall(@TLogMsg.Create(Format('%s [%s %d]', [message, source, line])).DoBrowserMsg, 0);
|
|
end;
|
|
|
|
procedure TMainForm.FormCreate(Sender: TObject);
|
|
var
|
|
s: String;
|
|
begin
|
|
FAllowGo:=False;
|
|
FDesignCaption:=Caption;
|
|
MimeTypes.LoadKnownTypes;
|
|
s := ExtractFilePath(Paramstr(0));
|
|
if pos('nativedesign', s) > 0 then
|
|
s := StringReplace(s, 'nativedesign', 'designdemo', [rfReplaceAll, rfIgnoreCase])
|
|
else
|
|
s := s+'designdemo';
|
|
s := AppendPathDelim(s)+'designdemo.html';
|
|
FEProject.FileName:=s;
|
|
FWebIDEIntf:=TIDEServer.Create(Self);
|
|
FWebIDEIntf.ProjectDir:=ExtractFilePath(FEProject.FileName);
|
|
FWebIDEIntf.OnClientAdded:=@DoClientCame;
|
|
FWebIDEIntf.OnClientRemoved:=@DoClientLeft;
|
|
FWebIDEIntf.OnRequest:=@DoLogRequest;
|
|
FWebIDEIntf.OnAction:=@DoAction;
|
|
FWebIDEIntf.Active:=True;
|
|
TSInspector.TabVisible:=False;
|
|
RegisterWidgets;
|
|
end;
|
|
|
|
procedure TMainForm.Panel1Resize(Sender: TObject);
|
|
begin
|
|
//if not Visible then
|
|
// exit;
|
|
if Width = 0 then begin
|
|
if MLog.Parent = PnlLog then begin
|
|
MLog.Parent := TSLog;
|
|
TSLog.TabVisible := True;
|
|
end;
|
|
if BLog.Parent = PnlBLog then begin
|
|
BLog.Parent := BrowserLog;
|
|
BrowserLog.TabVisible := True;
|
|
end;
|
|
end
|
|
else begin
|
|
if MLog.Parent = TSLog then begin
|
|
MLog.Parent := PnlLog;
|
|
TSLog.TabVisible := False;
|
|
end;
|
|
if BLog.Parent = BrowserLog then begin
|
|
BLog.Parent := PnlBLog;
|
|
BrowserLog.TabVisible := False;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{$IFDEF WINDOWS}
|
|
procedure TMainForm.WMEnterMenuLoop(var aMessage: TMessage);
|
|
|
|
begin
|
|
inherited;
|
|
if (aMessage.wParam = 0) and (GlobalCEFApp <> nil) then
|
|
GlobalCEFApp.OsmodalLoop := True;
|
|
end;
|
|
|
|
procedure TMainForm.WMExitMenuLoop(var aMessage: TMessage);
|
|
|
|
begin
|
|
inherited;
|
|
if (aMessage.wParam = 0) and (GlobalCEFApp <> nil) then
|
|
GlobalCEFApp.OsmodalLoop := False;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure TMainForm.cwOnBeforePopup(Sender: TObject;
|
|
const browser: ICefBrowser; const frame: ICefFrame; const targetUrl,
|
|
targetFrameName: ustring; targetDisposition: TCefWindowOpenDisposition;
|
|
userGesture: Boolean; const popupFeatures: TCefPopupFeatures;
|
|
var windowInfo: TCefWindowInfo; var client: ICefClient;
|
|
var settings: TCefBrowserSettings;
|
|
var extra_info: ICefDictionaryValue;
|
|
var noJavascriptAccess: Boolean;
|
|
var Result: Boolean);
|
|
begin
|
|
// For simplicity, this demo blocks all popup windows and new tabs
|
|
Result := (targetDisposition in [WOD_NEW_FOREGROUND_TAB, WOD_NEW_BACKGROUND_TAB, WOD_NEW_POPUP, WOD_NEW_WINDOW]);
|
|
end;
|
|
|
|
|
|
procedure TMainForm.DoAction(Sender: TObject; aExchange: TIDEExchange);
|
|
|
|
var
|
|
PayJSON : TJSONObject;
|
|
|
|
begin
|
|
payJSON:=Nil;
|
|
if Not (aExchange.Payload is TJSONObject) then
|
|
begin
|
|
Log('Payload is not JSON Object');
|
|
exit;
|
|
end;
|
|
payJSON:=aExchange.Payload as TJSONObject;
|
|
with aExchange do
|
|
case Name of
|
|
'create':
|
|
Log('Browser created widget of class %s, name %s',[PayJSON.Get('class',''),PayJSON.Get('widget','')]);
|
|
'select':
|
|
begin
|
|
Log('Browser selected widget of class %s, name %s',[PayJSON.Get('class',''),PayJSON.Get('widget','')]);
|
|
Log('Selected widget state: '+PayJSON.Get('state',''));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TMainForm.DoClientCame(Sender: TObject; aClient: TIDEClient);
|
|
begin
|
|
if FClientID>0 then
|
|
Log('Ignoring second client (id: %d) attachment.',[aClient.ID])
|
|
else
|
|
begin
|
|
FClientID:=aClient.ID;
|
|
Caption:=FDesignCaption+Format(' [Client: %d]',[FClientID]);
|
|
end;
|
|
end;
|
|
|
|
procedure TMainForm.DoAddWidget(Sender: TObject);
|
|
|
|
Var
|
|
Cmd : TIDECommand;
|
|
aName : String;
|
|
|
|
begin
|
|
aName:=FWidgets[(Sender as TAction).Tag];
|
|
Cmd:=TIDECommand.Create;
|
|
Cmd.NeedsConfirmation:=True;
|
|
Cmd.ClientID:=FClientID;
|
|
Cmd.name:='addWidget';
|
|
Cmd.PayLoad:=TJSONObject.Create(['class','T'+aName+'Widget']);
|
|
FWebIDEIntf.SendCommand(cmd);
|
|
end;
|
|
|
|
procedure TMainForm.DoClientLeft(Sender: TObject; aClient: TIDEClient);
|
|
begin
|
|
if (aClient.ID=FClientID) then
|
|
begin
|
|
FClientID:=-1;
|
|
Caption:=FDesignCaption;
|
|
end;
|
|
end;
|
|
|
|
procedure TMainForm.DoLogRequest(Sender: TObject; aURL: String);
|
|
var
|
|
m: TLogMsg;
|
|
begin
|
|
if FFormClosing then
|
|
exit;
|
|
m := TLogMsg.Create('Internal server request received: '+aURL);
|
|
Application.QueueAsyncCall(@m.DoLog, 0);
|
|
// Issue https://gitlab.com/freepascal.org/fpc/source/-/issues/39367
|
|
//Application.QueueAsyncCall(@TLogMsg.Create('Internal server request received: '+FURL).DoLog, 0);
|
|
end;
|
|
|
|
procedure TMainForm.IsWidgetEnabled(Sender: TObject);
|
|
begin
|
|
(Sender as TAction).Enabled:=(FClientID<>-1);
|
|
end;
|
|
|
|
procedure TMainForm.RegisterWidgets;
|
|
begin
|
|
SetLength(FWidgets,9);
|
|
FWidgetCount:=0;
|
|
RegisterWidget('Button',2);
|
|
RegisterWidget('CheckBoxInput',3);
|
|
RegisterWidget('RadioInput',4);
|
|
RegisterWidget('TextInput',5);
|
|
RegisterWidget('Image',6);
|
|
RegisterWidget('TextArea',7);
|
|
RegisterWidget('Select',8);
|
|
RegisterWidget('Container',9);
|
|
RegisterWidget('Jumbo',10);
|
|
end;
|
|
|
|
procedure TMainForm.RegisterWidget(aWidget: String; aImageIndex: Integer);
|
|
|
|
Var
|
|
A : TAction;
|
|
B : TToolButton;
|
|
L,i : Integer;
|
|
|
|
begin
|
|
FWidgets[FWidgetCount]:=aWidget;
|
|
A:=TAction.Create(Self);
|
|
A.ActionList:=ALWidgets;
|
|
A.Name:='AAdd'+aWidget;
|
|
A.Hint:='Add '+aWidget;
|
|
A.Caption:='Add '+aWidget;
|
|
A.ImageIndex:=aImageIndex;
|
|
A.Tag:=FWidgetCount;
|
|
A.OnExecute:=@DoAddWidget;
|
|
A.OnUpdate:=@IsWidgetEnabled;
|
|
L:=0;
|
|
For I:=0 to TBWidgets.ControlCount-1 do
|
|
if TBWidgets.Controls[i].BoundsRect.Right>L then
|
|
L:=TBWidgets.Controls[i].BoundsRect.Right;
|
|
B:=TToolButton.Create(Self);
|
|
B.Parent:=TBWidgets;
|
|
B.Left:=L;
|
|
B.Height:=32;
|
|
B.Action:=A;
|
|
inc(FWidgetCount);
|
|
// TBWidgets.AddControl;
|
|
|
|
end;
|
|
|
|
procedure TMainForm.Log(Msg: String);
|
|
begin
|
|
MLog.Lines.Add(Msg);
|
|
end;
|
|
|
|
procedure TMainForm.Log(Fmt: String; Args: array of const);
|
|
begin
|
|
Log(Format(Fmt,Args));
|
|
end;
|
|
|
|
|
|
initialization
|
|
{$IFDEF DARWIN}
|
|
AddCrDelegate;
|
|
{$ENDIF}
|
|
if GlobalCEFApp = nil then begin
|
|
CreateGlobalCEFApp;
|
|
if not GlobalCEFApp.StartMainProcess then begin
|
|
DestroyGlobalCEFApp;
|
|
DestroyGlobalCEFWorkScheduler;
|
|
halt(0); // exit the subprocess
|
|
end;
|
|
end;
|
|
|
|
finalization
|
|
(* Destroy from this unit, which is used after "Interfaces". So this happens before the Application object is destroyed *)
|
|
if GlobalCEFWorkScheduler <> nil then
|
|
GlobalCEFWorkScheduler.StopScheduler;
|
|
DestroyGlobalCEFApp;
|
|
DestroyGlobalCEFWorkScheduler;
|
|
|
|
end.
|
|
|