// 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.