453 lines
12 KiB
ObjectPascal

// SPDX-License-Identifier: LGPL-3.0-linking-exception
{
Created by BGRA Controls Team
Dibo, Circular, lainz (007) and contributors.
For detailed information see readme.txt
Site: https://sourceforge.net/p/bgra-controls/
Wiki: http://wiki.lazarus.freepascal.org/BGRAControls
Forum: http://forum.lazarus.freepascal.org/index.php/board,46.0.html
}
{******************************* CONTRIBUTOR(S) ******************************
- Edivando S. Santos Brasil | mailedivando@gmail.com
(Compatibility with delphi VCL 11/2018)
***************************** END CONTRIBUTOR(S) *****************************}
unit BGRAShape;
{$I bgracontrols.inc}
interface
uses
Classes, SysUtils, {$IFDEF FPC}LResources,{$ENDIF} Forms, Controls, Graphics, Dialogs,
{$IFNDEF FPC}Types, BGRAGraphics, GraphType, FPImage, {$ENDIF}
BCBaseCtrls, BGRABitmap, BGRABitmapTypes, BCTypes;
type
TBGRAShapeType = (stRegularPolygon, stEllipse);
{ TBGRAShape }
TBGRAShape = class(TBGRAGraphicCtrl)
private
{ Private declarations }
FBorderColor: TColor;
FBorderOpacity: byte;
FBorderStyle: TPenStyle;
FBorderWidth: integer;
FBorderGradient: TBCGradient;
FUseBorderGradient: boolean;
FFillColor: TColor;
FFillOpacity: byte;
FFillGradient: TBCGradient;
FUseFillGradient: boolean;
FRoundRadius: integer;
FBGRA: TBGRABitmap;
FSideCount: integer;
FRatioXY: single;
FUseRatioXY: boolean;
FAngle: single;
FShapeType: TBGRAShapeType;
procedure SetAngle(const AValue: single);
procedure SetBorderColor(const AValue: TColor);
procedure SetBorderGradient(const AValue: TBCGradient);
procedure SetBorderOpacity(const AValue: byte);
procedure SetBorderStyle(const AValue: TPenStyle);
procedure SetBorderWidth(AValue: integer);
procedure SetFillColor(const AValue: TColor);
procedure SetFillGradient(const AValue: TBCGradient);
procedure SetFillOpacity(const AValue: byte);
procedure SetRatioXY(const AValue: single);
procedure SetRoundRadius(AValue: integer);
procedure SetShapeType(const AValue: TBGRAShapeType);
procedure SetSideCount(AValue: integer);
procedure SetUseBorderGradient(const AValue: boolean);
procedure SetUseFillGradient(const AValue: boolean);
procedure SetUseRatioXY(const AValue: boolean);
protected
{ Protected declarations }
procedure Paint; override;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
public
{ Streaming }
{$IFDEF FPC}
procedure SaveToFile(AFileName: string);
procedure LoadFromFile(AFileName: string);
{$ENDIF}
procedure OnFindClass({%H-}Reader: TReader; const AClassName: string;
var ComponentClass: TComponentClass);
published
{ Published declarations }
property AutoSize;
property Align;
property Anchors;
property Angle: single Read FAngle Write SetAngle {$IFDEF FPC}default 0{$ENDIF};
property BorderWidth: integer Read FBorderWidth Write SetBorderWidth default 1;
property BorderOpacity: byte Read FBorderOpacity Write SetBorderOpacity default 255;
property BorderColor: TColor Read FBorderColor Write SetBorderColor;
property BorderGradient: TBCGradient Read FBorderGradient Write SetBorderGradient;
property BorderStyle: TPenStyle
Read FBorderStyle Write SetBorderStyle default psSolid;
property FillColor: TColor Read FFillColor Write SetFillColor;
property FillOpacity: byte Read FFillOpacity Write SetFillOpacity;
property FillGradient: TBCGradient Read FFillGradient Write SetFillGradient;
property SideCount: integer Read FSideCount Write SetSideCount default 4;
property RatioXY: single Read FRatioXY Write SetRatioXY {$IFDEF FPC}default 1{$ENDIF};
property UseRatioXY: boolean Read FUseRatioXY Write SetUseRatioXY default False;
property UseFillGradient: boolean Read FUseFillGradient
Write SetUseFillGradient default False;
property UseBorderGradient: boolean Read FUseBorderGradient
Write SetUseBorderGradient default False;
property ShapeType: TBGRAShapeType
Read FShapeType Write SetShapeType default stRegularPolygon;
property BorderSpacing;
property Caption;
property PopupMenu;
property RoundRadius: integer Read FRoundRadius Write SetRoundRadius default 0;
property Visible;
property OnClick;
property OnDblClick;
property OnMouseDown;
property OnMouseEnter;
property OnMouseLeave;
property OnMouseMove;
property OnMouseUp;
end;
{$IFDEF FPC}procedure Register;{$ENDIF}
implementation
uses BCTools;
{$IFDEF FPC}
procedure Register;
begin
RegisterComponents('BGRA Controls', [TBGRAShape]);
end;
{$ENDIF}
{ TBGRAShape }
procedure TBGRAShape.SetBorderColor(const AValue: TColor);
begin
if FBorderColor = AValue then
exit;
FBorderColor := AValue;
Invalidate;
end;
procedure TBGRAShape.SetBorderGradient(const AValue: TBCGradient);
begin
if FBorderGradient = AValue then
exit;
FBorderGradient.Assign(AValue);
Invalidate;
end;
procedure TBGRAShape.SetAngle(const AValue: single);
begin
if FAngle = AValue then
exit;
FAngle := AValue;
Invalidate;
end;
procedure TBGRAShape.SetBorderOpacity(const AValue: byte);
begin
if FBorderOpacity = AValue then
exit;
FBorderOpacity := AValue;
Invalidate;
end;
procedure TBGRAShape.SetBorderStyle(const AValue: TPenStyle);
begin
if FBorderStyle = AValue then
exit;
FBorderStyle := AValue;
Invalidate;
end;
procedure TBGRAShape.SetBorderWidth(AValue: integer);
begin
if AValue < 0 then
AValue := 0;
if FBorderWidth = AValue then
exit;
FBorderWidth := AValue;
Invalidate;
end;
procedure TBGRAShape.SetFillColor(const AValue: TColor);
begin
if FFillColor = AValue then
exit;
FFillColor := AValue;
Invalidate;
end;
procedure TBGRAShape.SetFillGradient(const AValue: TBCGradient);
begin
if FFillGradient = AValue then
exit;
FFillGradient.Assign(AValue);
Invalidate;
end;
procedure TBGRAShape.SetFillOpacity(const AValue: byte);
begin
if FFillOpacity = AValue then
exit;
FFillOpacity := AValue;
Invalidate;
end;
procedure TBGRAShape.SetRatioXY(const AValue: single);
begin
if FRatioXY = AValue then
exit;
FRatioXY := AValue;
Invalidate;
end;
procedure TBGRAShape.SetRoundRadius(AValue: integer);
begin
if AValue < 0 then
AValue := 0;
if FRoundRadius = AValue then
exit;
FRoundRadius := AValue;
Invalidate;
end;
procedure TBGRAShape.SetShapeType(const AValue: TBGRAShapeType);
begin
if FShapeType = AValue then
exit;
FShapeType := AValue;
Invalidate;
end;
procedure TBGRAShape.SetSideCount(AValue: integer);
begin
if AValue < 3 then
AValue := 3;
if FSideCount = AValue then
exit;
FSideCount := AValue;
Invalidate;
end;
procedure TBGRAShape.SetUseBorderGradient(const AValue: boolean);
begin
if FUseBorderGradient = AValue then
exit;
FUseBorderGradient := AValue;
Invalidate;
end;
procedure TBGRAShape.SetUseFillGradient(const AValue: boolean);
begin
if FUseFillGradient = AValue then
exit;
FUseFillGradient := AValue;
Invalidate;
end;
procedure TBGRAShape.SetUseRatioXY(const AValue: boolean);
begin
if FUseRatioXY = AValue then
exit;
FUseRatioXY := AValue;
Invalidate;
end;
procedure TBGRAShape.Paint;
var
cx, cy, rx, ry, curRatio, a: single;
coords: array of TPointF;
minCoord, maxCoord: TPointF;
i: integer;
borderGrad, fillGrad: TBGRACustomScanner;
scaling: Double;
begin
if FBGRA = nil then FBGRA := TBGRABitmap.Create;
scaling := GetCanvasScaleFactor;
FBGRA.SetSize(round(Width*scaling), round(Height*scaling));
FBGRA.FillTransparent;
FBGRA.PenStyle := FBorderStyle;
with FBGRA.Canvas2D do
begin
lineJoin := 'round';
if FUseBorderGradient then
begin
borderGrad := CreateGradient(FBorderGradient, Classes.rect(0, 0, FBGRA.Width, FBGRA.Height));
strokeStyle(borderGrad);
end
else
begin
borderGrad := nil;
strokeStyle(ColorToBGRA(ColorToRGB(FBorderColor), FBorderOpacity));
end;
lineStyle(FBGRA.CustomPenStyle);
lineWidth := FBorderWidth*scaling;
if FUseFillGradient then
begin
fillGrad := CreateGradient(FFillGradient, Classes.rect(0, 0, FBGRA.Width, FBGRA.Height));
fillStyle(fillGrad);
end
else
begin
fillGrad := nil;
fillStyle(ColorToBGRA(ColorToRGB(FFillColor), FFillOpacity));
end;
cx := FBGRA.Width / 2;
cy := FBGRA.Height / 2;
rx := (FBGRA.Width - FBorderWidth*scaling) / 2;
ry := (FBGRA.Height - FBorderWidth*scaling) / 2;
if FUseRatioXY and (ry <> 0) and (FRatioXY <> 0) then
begin
curRatio := rx / ry;
if FRatioXY > curRatio then
ry := ry / (FRatioXY / curRatio)
else
rx := rx / (curRatio / FRatioXY);
end;
if FShapeType = stRegularPolygon then
begin
setlength(coords, FSideCount);
for i := 0 to high(coords) do
begin
a := (i / FSideCount + FAngle / 360) * 2 * Pi;
coords[i] := PointF(sin(a), -cos(a));
end;
minCoord := coords[0];
maxCoord := coords[0];
for i := 1 to high(coords) do
begin
if coords[i].x < minCoord.x then
minCoord.x := coords[i].x;
if coords[i].y < minCoord.y then
minCoord.y := coords[i].y;
if coords[i].x > maxCoord.x then
maxCoord.x := coords[i].x;
if coords[i].y > maxCoord.y then
maxCoord.y := coords[i].y;
end;
for i := 0 to high(coords) do
begin
with (coords[i] - minCoord) do
coords[i] := PointF((x / (maxCoord.x - minCoord.x) - 0.5) *
2 * rx + cx, (y / (maxCoord.y - minCoord.y) - 0.5) * 2 * ry + cy);
end;
beginPath;
for i := 0 to high(coords) do
begin
lineTo((coords[i] + coords[(i + 1) mod length(coords)]) * (1 / 2));
arcTo(coords[(i + 1) mod length(coords)], coords[(i + 2) mod
length(coords)], FRoundRadius);
end;
closePath;
end
else
begin
save;
translate(cx, cy);
scale(rx, ry);
beginPath;
arc(0, 0, 1, 0, 2 * Pi);
restore;
end;
fill;
if FBorderWidth <> 0 then
stroke;
fillStyle(BGRAWhite);
strokeStyle(BGRABlack);
fillGrad.Free;
borderGrad.Free;
end;
FBGRA.Draw(Self.Canvas, rect(0,0,Width,Height), False);
end;
constructor TBGRAShape.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
with GetControlClassDefaultSize do
SetInitialBounds(0, 0, CX, CY);
FBGRA := nil;
FBorderColor := clWindowText;
FBorderOpacity := 255;
FBorderWidth := 1;
FBorderStyle := psSolid;
FBorderGradient := TBCGradient.Create(Self);
FBorderGradient.Point2XPercent := 100;
FBorderGradient.StartColor := clWhite;
FBorderGradient.EndColor := clBlack;
FFillColor := clWindow;
FFillOpacity := 255;
FFillGradient := TBCGradient.Create(Self);
FRoundRadius := 0;
FSideCount := 4;
FRatioXY := 1;
FUseRatioXY := False;
end;
destructor TBGRAShape.Destroy;
begin
FBGRA.Free;
FFillGradient.Free;
FBorderGradient.Free;
inherited Destroy;
end;
{$IFDEF FPC}
procedure TBGRAShape.SaveToFile(AFileName: string);
var
AStream: TMemoryStream;
begin
AStream := TMemoryStream.Create;
try
WriteComponentAsTextToStream(AStream, Self);
AStream.SaveToFile(AFileName);
finally
AStream.Free;
end;
end;
procedure TBGRAShape.LoadFromFile(AFileName: string);
var
AStream: TMemoryStream;
begin
AStream := TMemoryStream.Create;
try
AStream.LoadFromFile(AFileName);
ReadComponentFromTextStream(AStream, TComponent(Self), OnFindClass);
finally
AStream.Free;
end;
end;
{$ENDIF}
procedure TBGRAShape.OnFindClass(Reader: TReader; const AClassName: string;
var ComponentClass: TComponentClass);
begin
if CompareText(AClassName, 'TBGRAShape') = 0 then
ComponentClass := TBGRAShape;
end;
end.