CEF4Delphi/source/uCEFFMXBufferPanel.pas
Salvador Díaz Fau fec1b3be79 Fixed colors in FMXExternalPumpBrowser for MacOS
Added functions to copy the CEF binaries and the CEF helpers automatically to FMXExternalPumpBrowser for MacOS
Added TFMXBufferPanel.OnResized
Added more comments with missing functionality in Linux and MacOS
2021-05-18 16:40:37 +02:00

531 lines
14 KiB
ObjectPascal

// ************************************************************************
// ***************************** CEF4Delphi *******************************
// ************************************************************************
//
// CEF4Delphi is based on DCEF3 which uses CEF to embed a chromium-based
// browser in Delphi applications.
//
// The original license of DCEF3 still applies to CEF4Delphi.
//
// For more information about CEF4Delphi visit :
// https://www.briskbard.com/index.php?lang=en&pageid=cef
//
// Copyright © 2021 Salvador Diaz Fau. All rights reserved.
//
// ************************************************************************
// ************ vvvv Original license and comments below vvvv *************
// ************************************************************************
(*
* Delphi Chromium Embedded 3
*
* Usage allowed under the restrictions of the Lesser GNU General Public License
* or alternatively the restrictions of the Mozilla Public License 1.1
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* Unit owner : Henri Gourvest <hgourvest@gmail.com>
* Web site : http://www.progdigy.com
* Repository : http://code.google.com/p/delphichromiumembedded/
* Group : http://groups.google.com/group/delphichromiumembedded
*
* Embarcadero Technologies, Inc is not permitted to use or redistribute
* this source code without explicit permission.
*
*)
unit uCEFFMXBufferPanel;
{$I cef.inc}
interface
uses
{$IFDEF MSWINDOWS}
Winapi.Windows, FMX.Platform.Win,
{$ELSE}
System.SyncObjs,
{$ENDIF}
System.Classes, System.UIConsts, System.Types, System.UITypes,
{$IFDEF DELPHI17_UP}
FMX.Graphics,
{$ENDIF}
FMX.Types, FMX.Controls, FMX.Forms,
uCEFTypes;
type
TDialogKeyEvent = procedure(Sender: TObject; var Key: Word; Shift: TShiftState) of object;
{$IFNDEF FPC}{$IFDEF DELPHI16_UP}[ComponentPlatformsAttribute(pidWin32 or pidWin64)]{$ENDIF}{$ENDIF}
TFMXBufferPanel = class(TControl)
protected
{$IFDEF MSWINDOWS}
FMutex : THandle;
{$ELSE}
FBufferCS : TCriticalSection;
{$ENDIF}
FBuffer : TBitmap;
FScanlineSize : integer;
FColor : TAlphaColor;
FHighSpeedDrawing : boolean;
FOnDialogKey : TDialogKeyEvent;
FForcedDeviceScaleFactor : single;
procedure CreateSyncObj;
procedure DestroySyncObj;
procedure DestroyBuffer;
function GetScreenScale : single; virtual;
function GetBufferWidth : integer;
function GetBufferHeight : integer;
function GetParentForm : TCustomForm;
function GetParentFormHandle : TCefWindowHandle;
function GetRealScreenScale(var aResultScale : single) : boolean; virtual;
function CopyBuffer : boolean;
function SaveBufferToFile(const aFilename : string) : boolean;
procedure Paint; override;
procedure DialogKey(var Key: Word; Shift: TShiftState); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure AfterConstruction; override;
function SaveToFile(const aFilename : string) : boolean;
procedure InvalidatePanel;
function BeginBufferDraw : boolean;
procedure EndBufferDraw;
procedure BufferDraw(const aBitmap : TBitmap; const aSrcRect, aDstRect : TRectF);
function UpdateBufferDimensions(aWidth, aHeight : integer) : boolean;
function BufferIsResized(aUseMutex : boolean = True) : boolean;
function ScreenToClient(aPoint : TPoint) : TPoint; overload;
function ScreenToClient(aPoint : TPointF) : TPointF; overload;
function ClientToScreen(aPoint : TPoint) : TPoint; overload;
function ClientToScreen(aPoint : TPointF) : TPointF; overload;
property Buffer : TBitmap read FBuffer;
property ScanlineSize : integer read FScanlineSize;
property BufferWidth : integer read GetBufferWidth;
property BufferHeight : integer read GetBufferHeight;
property ScreenScale : single read GetScreenScale;
property ForcedDeviceScaleFactor : single read FForcedDeviceScaleFactor write FForcedDeviceScaleFactor;
published
property Align;
property Anchors;
property Visible;
property Enabled;
property TabOrder;
property Color : TAlphaColor read FColor write FColor default claWhite;
property HighSpeedDrawing : boolean read FHighSpeedDrawing write FHighSpeedDrawing default True;
{$IFDEF DELPHI17_UP}
property TabStop;
property CanFocus;
property CanParentFocus;
property Height;
property Width;
property Padding;
property Opacity;
property Margins;
property Position;
property RotationAngle;
property RotationCenter;
property Scale;
property Size;
property OnResized;
{$ENDIF}
{$IFNDEF DELPHI23_UP}
property Hint;
property ShowHint;
{$ENDIF}
property OnEnter;
property OnExit;
property OnResize;
property OnClick;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnMouseEnter;
property OnMouseLeave;
property OnMouseWheel;
property OnKeyUp;
property OnKeyDown;
property OnDialogKey : TDialogKeyEvent read FOnDialogKey write FOnDialogKey;
end;
implementation
uses
System.SysUtils, System.Math,
{$IFDEF MSWINDOWS}FMX.Helpers.Win,{$ENDIF}
FMX.Platform, uCEFMiscFunctions, uCEFApplicationCore;
constructor TFMXBufferPanel.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
{$IFDEF MSWINDOWS}
FMutex := 0;
{$ELSE}
FBufferCS := nil;
{$ENDIF}
FBuffer := nil;
FScanlineSize := 0;
FColor := claWhite;
FOnDialogKey := nil;
FHighSpeedDrawing := True;
if (GlobalCEFApp <> nil) and (GlobalCEFApp.ForcedDeviceScaleFactor <> 0) then
FForcedDeviceScaleFactor := GlobalCEFApp.ForcedDeviceScaleFactor
else
FForcedDeviceScaleFactor := 0;
end;
destructor TFMXBufferPanel.Destroy;
begin
DestroyBuffer;
DestroySyncObj;
inherited Destroy;
end;
procedure TFMXBufferPanel.AfterConstruction;
begin
inherited AfterConstruction;
CreateSyncObj;
end;
procedure TFMXBufferPanel.CreateSyncObj;
begin
{$IFDEF MSWINDOWS}
FMutex := CreateMutex(nil, False, nil);
{$ELSE}
FBufferCS := TCriticalSection.Create;
{$ENDIF}
end;
procedure TFMXBufferPanel.DestroySyncObj;
begin
{$IFDEF MSWINDOWS}
if (FMutex <> 0) then
begin
CloseHandle(FMutex);
FMutex := 0;
end;
{$ELSE}
if (FBufferCS <> nil) then FreeAndNil(FBufferCS);
{$ENDIF}
end;
procedure TFMXBufferPanel.DestroyBuffer;
begin
if BeginBufferDraw then
begin
if (FBuffer <> nil) then FreeAndNil(FBuffer);
EndBufferDraw;
end;
end;
function TFMXBufferPanel.SaveBufferToFile(const aFilename : string) : boolean;
begin
Result := False;
try
if (FBuffer <> nil) then
begin
FBuffer.SaveToFile(aFilename);
Result := True;
end;
except
on e : exception do
if CustomExceptionHandler('TFMXBufferPanel.SaveBufferToFile', e) then raise;
end;
end;
function TFMXBufferPanel.SaveToFile(const aFilename : string) : boolean;
begin
Result := False;
if BeginBufferDraw then
begin
Result := SaveBufferToFile(aFilename);
EndBufferDraw;
end;
end;
procedure TFMXBufferPanel.InvalidatePanel;
begin
InvalidateRect(TRectF.Create(0, 0, Width, Height));
end;
function TFMXBufferPanel.BeginBufferDraw : boolean;
begin
{$IFDEF MSWINDOWS}
Result := (FMutex <> 0) and (WaitForSingleObject(FMutex, 5000) = WAIT_OBJECT_0);
{$ELSE}
if (FBufferCS <> nil) then
begin
FBufferCS.Acquire;
Result := True;
end
else
Result := False;
{$ENDIF}
end;
procedure TFMXBufferPanel.EndBufferDraw;
begin
{$IFDEF MSWINDOWS}
if (FMutex <> 0) then ReleaseMutex(FMutex);
{$ELSE}
if (FBufferCS <> nil) then FBufferCS.Release;
{$ENDIF}
end;
function TFMXBufferPanel.CopyBuffer : boolean;
var
TempSrc, TempDst, TempClip : TRectF;
TempState : TCanvasSaveState;
TempScale : single;
begin
Result := False;
if Canvas.BeginScene then
try
if BeginBufferDraw then
try
if (FBuffer <> nil) then
begin
TempScale := ScreenScale;
TempSrc := TRectF.Create(0, 0, FBuffer.Width, FBuffer.Height);
TempDst := TRectF.Create(0, 0, FBuffer.Width / TempScale, FBuffer.Height / TempScale);
TempClip := TRectF.Create(0, 0, Width, Height);
TempState := Canvas.SaveState;
try
Canvas.IntersectClipRect(TempClip);
Canvas.DrawBitmap(FBuffer, TempSrc, TempDst, 1, FHighSpeedDrawing);
Result := True;
finally
Canvas.RestoreState(TempState);
end;
end;
finally
EndBufferDraw;
end;
finally
Canvas.EndScene;
end;
end;
procedure TFMXBufferPanel.DialogKey(var Key: Word; Shift: TShiftState);
begin
if assigned(FOnDialogKey) then FOnDialogKey(self, Key, Shift);
inherited DialogKey(Key, Shift);
end;
procedure TFMXBufferPanel.Paint;
var
TempRect : TRectF;
begin
if (csDesigning in ComponentState) or not(CopyBuffer) then
begin
TempRect := TRectF.Create(0, 0, Width, Height);
if Canvas.BeginScene then
try
Canvas.ClearRect(TempRect, FColor);
finally
Canvas.EndScene;
end;
end;
end;
function TFMXBufferPanel.GetParentForm : TCustomForm;
var
TempComp : TComponent;
begin
Result := nil;
TempComp := Owner;
while (TempComp <> nil) do
if (TempComp is TCustomForm) then
begin
Result := TCustomForm(TempComp);
exit;
end
else
TempComp := TempComp.owner;
end;
function TFMXBufferPanel.GetParentFormHandle : TCefWindowHandle;
{$IFDEF MSWINDOWS}
var
TempForm : TCustomForm;
{$ENDIF}
begin
InitializeWindowHandle(Result);
{$IFDEF MSWINDOWS}
TempForm := GetParentForm;
if (TempForm <> nil) then
Result := FmxHandleToHWND(TempForm.Handle)
else
if (Application <> nil) and
(Application.MainForm <> nil) then
Result := FmxHandleToHWND(Application.MainForm.Handle);
{$ENDIF}
end;
function TFMXBufferPanel.GetRealScreenScale(var aResultScale : single) : boolean;
{$IFDEF DELPHI24_UP}{$IFDEF MSWINDOWS}
var
TempHandle : TCefWindowHandle;
{$ENDIF}{$ENDIF}
begin
Result := False;
aResultScale := 1;
{$IFDEF DELPHI24_UP}{$IFDEF MSWINDOWS}
TempHandle := GetParentFormHandle;
if (TempHandle <> 0) then
begin
Result := True;
aResultScale := GetWndScale(TempHandle);
end;
{$ENDIF}{$ENDIF}
end;
function TFMXBufferPanel.GetScreenScale : single;
var
TempScale : single;
begin
if (FForcedDeviceScaleFactor <> 0) then
Result := FForcedDeviceScaleFactor
else
if GetRealScreenScale(TempScale) then
Result := TempScale
else
if (GlobalCEFApp <> nil) then
Result := GlobalCEFApp.DeviceScaleFactor
else
Result := 1;
end;
function TFMXBufferPanel.GetBufferWidth : integer;
begin
if (FBuffer <> nil) then
Result := FBuffer.Width
else
Result := 0;
end;
function TFMXBufferPanel.GetBufferHeight : integer;
begin
if (FBuffer <> nil) then
Result := FBuffer.Height
else
Result := 0;
end;
procedure TFMXBufferPanel.BufferDraw(const aBitmap : TBitmap; const aSrcRect, aDstRect : TRectF);
begin
if (FBuffer <> nil) then
if FBuffer.Canvas.BeginScene then
try
FBuffer.Canvas.DrawBitmap(aBitmap, aSrcRect, aDstRect, 1, FHighSpeedDrawing);
finally
FBuffer.Canvas.EndScene;
end;
end;
function TFMXBufferPanel.UpdateBufferDimensions(aWidth, aHeight : integer) : boolean;
var
TempScale : single;
begin
Result := False;
TempScale := ScreenScale;
if ((FBuffer = nil) or
(FBuffer.BitmapScale <> TempScale) or
(FBuffer.Width <> aWidth) or
(FBuffer.Height <> aHeight)) then
begin
if (FBuffer <> nil) then FreeAndNil(FBuffer);
FBuffer := TBitmap.Create(aWidth, aHeight);
{$IFDEF DELPHI17_UP}
FBuffer.BitmapScale := TempScale;
FScanlineSize := FBuffer.BytesPerLine;
{$ELSE}
FScanlineSize := aWidth * SizeOf(TRGBQuad);
{$ENDIF}
Result := True;
end;
end;
function TFMXBufferPanel.BufferIsResized(aUseMutex : boolean) : boolean;
var
TempWidth, TempHeight : integer;
TempScale : single;
begin
Result := False;
if not(aUseMutex) or BeginBufferDraw then
begin
TempScale := ScreenScale;
TempWidth := round(Width * TempScale);
TempHeight := round(Height * TempScale);
Result := (FBuffer <> nil) and
(FBuffer.BitmapScale = TempScale) and
(FBuffer.Width = TempWidth) and
(FBuffer.Height = TempHeight);
if aUseMutex then EndBufferDraw;
end;
end;
function TFMXBufferPanel.ScreenToClient(aPoint : TPoint) : TPoint;
var
TempPoint : TPointF;
begin
TempPoint.x := aPoint.x;
TempPoint.y := aPoint.y;
TempPoint := ScreenToLocal(TempPoint);
Result.x := round(TempPoint.x);
Result.y := round(TempPoint.y);
end;
function TFMXBufferPanel.ScreenToClient(aPoint : TPointF) : TPointF;
begin
Result := ScreenToLocal(aPoint);
end;
function TFMXBufferPanel.ClientToScreen(aPoint : TPoint) : TPoint;
var
TempPoint : TPointF;
begin
TempPoint.x := aPoint.x;
TempPoint.y := aPoint.y;
TempPoint := LocalToScreen(TempPoint);
Result.x := round(TempPoint.x);
Result.y := round(TempPoint.y);
end;
function TFMXBufferPanel.ClientToScreen(aPoint : TPointF) : TPointF;
begin
Result := LocalToScreen(aPoint);
end;
end.