453 lines
12 KiB
ObjectPascal
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.
|