pas2js/demo/webwidget/nativedesign/frmmain.pp

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.