// SPDX-License-Identifier: LGPL-3.0-linking-exception {******************************* CONTRIBUTOR(S) ****************************** - Edivando S. Santos Brasil | mailedivando@gmail.com (Compatibility with delphi VCL 11/2018) ***************************** END CONTRIBUTOR(S) *****************************} unit BCMaterialDesignButton; {$I bgracontrols.inc} interface uses Classes, SysUtils, {$IFDEF FPC}LResources,{$ENDIF} Types, Controls, Graphics, ExtCtrls, BCBaseCtrls, BGRABitmap, BGRABitmapTypes; type { TBCMaterialDesignButton } TBCMaterialDesignButton = class(TBGRAGraphicCtrl) private FNormalColor: TColor; FNormalColorEffect: TColor; FRoundBorders: single; FShadow: boolean; FShadowColor: TColor; FShadowSize: integer; FTextColor: TColor; FTextFont: string; FTextQuality: TBGRAFontQuality; FTextShadow: boolean; FTextShadowColor: TColor; FTextShadowOffsetX: integer; FTextShadowOffsetY: integer; FTextShadowSize: integer; FTextSize: integer; FTextStyle: TFontStyles; FTimer: TTimer; FBGRA: TBGRABitmap; FBGRAShadow: TBGRABitmap; FMousePos: TPoint; FCircleSize: single; FCircleAlpha: byte; procedure SetFNormalColor(AValue: TColor); procedure SetFNormalColorEffect(AValue: TColor); procedure SetFRoundBorders(AValue: single); procedure SetFShadow(AValue: boolean); procedure SetFShadowColor(AValue: TColor); procedure SetFShadowSize(AValue: integer); procedure SetFTextColor(AValue: TColor); procedure SetFTextFont(AValue: string); procedure SetFTextQuality(AValue: TBGRAFontQuality); procedure SetFTextShadow(AValue: boolean); procedure SetFTextShadowColor(AValue: TColor); procedure SetFTextShadowOffsetX(AValue: integer); procedure SetFTextShadowOffsetY(AValue: integer); procedure SetFTextShadowSize(AValue: integer); procedure SetFTextSize(AValue: integer); procedure SetFTextStyle(AValue: TFontStyles); protected procedure CalculatePreferredSize(var PreferredWidth, PreferredHeight: integer; {%H-}WithThemeSpace: boolean); override; procedure OnStartTimer({%H-}Sender: TObject); procedure OnTimer({%H-}Sender: TObject); procedure Paint; override; procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: integer); override; class function GetControlClassDefaultSize: TSize; override; procedure TextChanged; override; procedure UpdateShadow; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure ClickMe; published property RoundBorders: single read FRoundBorders write SetFRoundBorders {$IFDEF FPC}default 5{$ENDIF}; property NormalColor: TColor read FNormalColor write SetFNormalColor default clWhite; property NormalColorEffect: TColor read FNormalColorEffect write SetFNormalColorEffect default clSilver; property Shadow: boolean read FShadow write SetFShadow default True; property ShadowColor: TColor read FShadowColor write SetFShadowColor default clGray; property ShadowSize: integer read FShadowSize write SetFShadowSize default 5; property TextColor: TColor read FTextColor write SetFTextColor default clBlack; property TextSize: integer read FTextSize write SetFTextSize default 16; property TextShadow: boolean read FTextShadow write SetFTextShadow default True; property TextShadowColor: TColor read FTextShadowColor write SetFTextShadowColor default clBlack; property TextShadowSize: integer read FTextShadowSize write SetFTextShadowSize default 2; property TextShadowOffsetX: integer read FTextShadowOffsetX write SetFTextShadowOffsetX default 0; property TextShadowOffsetY: integer read FTextShadowOffsetY write SetFTextShadowOffsetY default 0; property TextStyle: TFontStyles read FTextStyle write SetFTextStyle default []; property TextFont: string read FTextFont write SetFTextFont; property TextQuality: TBGRAFontQuality read FTextQuality write SetFTextQuality default fqFineAntialiasing; published property Action; property Align; property Anchors; property AutoSize; property BidiMode; property BorderSpacing; {$IFDEF FPC} //# property OnChangeBounds; {$ENDIF} property Caption; property Constraints; property DragCursor; property DragKind; property DragMode; property Enabled; property OnClick; property OnContextPopup; property OnDragDrop; property OnDragOver; property OnEndDrag; property OnMouseDown; property OnMouseMove; property OnMouseUp; property OnMouseEnter; property OnMouseLeave; property OnMouseWheel; property OnMouseWheelDown; property OnMouseWheelUp; property OnResize; property OnStartDrag; property ParentBidiMode; property ParentFont; property ParentShowHint; property PopupMenu; property ShowHint; property Visible; end; {$IFDEF FPC}procedure Register;{$ENDIF} implementation function DrawTextShadow(AWidth, AHeight: integer; AText: string; AFontHeight: integer; ATextColor, AShadowColor: TBGRAPixel; AOffSetX, AOffSetY: integer; ARadius: integer = 0; AFontStyle: TFontStyles = []; AFontName: string = 'Default'; AShowShadow: boolean = True; AFontQuality: TBGRAFontQuality = fqFineAntialiasing): TBGRACustomBitmap; var bmpOut, bmpSdw: TBGRABitmap; begin bmpOut := TBGRABitmap.Create(AWidth, AHeight); bmpOut.FontAntialias := True; bmpOut.FontHeight := AFontHeight; bmpOut.FontStyle := AFontStyle; bmpOut.FontName := AFontName; bmpOut.FontQuality := AFontQuality; if AShowShadow then begin bmpSdw := TBGRABitmap.Create(AWidth, AHeight); bmpSdw.FontAntialias := True; bmpSdw.FontHeight := AFontHeight; bmpSdw.FontStyle := AFontStyle; bmpSdw.FontName := AFontName; bmpSdw.FontQuality := AFontQuality; bmpSdw.TextRect(Rect(0, 0, bmpSdw.Width, bmpSdw.Height), AText, taCenter, tlCenter, AShadowColor); BGRAReplace(bmpSdw, bmpSdw.FilterBlurRadial(ARadius, rbFast)); bmpOut.PutImage(0 + AOffSetX, 0 + AOffSetY, bmpSdw, dmDrawWithTransparency); bmpSdw.Free; end; bmpOut.TextRect(Rect(0, 0, bmpOut.Width, bmpOut.Height), AText, taCenter, tlCenter, ATextColor); Result := bmpOut; end; {$IFDEF FPC}procedure Register; begin RegisterComponents('BGRA Button Controls', [TBCMaterialDesignButton]); end; {$ENDIF} { TBCMaterialDesignButton } procedure TBCMaterialDesignButton.SetFRoundBorders(AValue: single); begin if FRoundBorders = AValue then Exit; FRoundBorders := AValue; UpdateShadow; Invalidate; end; procedure TBCMaterialDesignButton.SetFShadow(AValue: boolean); begin if FShadow = AValue then Exit; FShadow := AValue; InvalidatePreferredSize; AdjustSize; UpdateShadow; Invalidate; end; procedure TBCMaterialDesignButton.SetFShadowColor(AValue: TColor); begin if FShadowColor = AValue then Exit; FShadowColor := AValue; UpdateShadow; Invalidate; end; procedure TBCMaterialDesignButton.SetFShadowSize(AValue: integer); begin if FShadowSize = AValue then Exit; FShadowSize := AValue; InvalidatePreferredSize; AdjustSize; UpdateShadow; Invalidate; end; procedure TBCMaterialDesignButton.SetFTextColor(AValue: TColor); begin if FTextColor = AValue then Exit; FTextColor := AValue; Invalidate; end; procedure TBCMaterialDesignButton.SetFTextFont(AValue: string); begin if FTextFont = AValue then Exit; FTextFont := AValue; InvalidatePreferredSize; AdjustSize; Invalidate; end; procedure TBCMaterialDesignButton.SetFTextQuality(AValue: TBGRAFontQuality); begin if FTextQuality = AValue then Exit; FTextQuality := AValue; InvalidatePreferredSize; AdjustSize; Invalidate; end; procedure TBCMaterialDesignButton.SetFTextShadow(AValue: boolean); begin if FTextShadow = AValue then Exit; FTextShadow := AValue; InvalidatePreferredSize; AdjustSize; Invalidate; end; procedure TBCMaterialDesignButton.SetFTextShadowColor(AValue: TColor); begin if FTextShadowColor = AValue then Exit; FTextShadowColor := AValue; UpdateShadow; Invalidate; end; procedure TBCMaterialDesignButton.SetFTextShadowOffsetX(AValue: integer); begin if FTextShadowOffsetX = AValue then Exit; FTextShadowOffsetX := AValue; InvalidatePreferredSize; AdjustSize; Invalidate; end; procedure TBCMaterialDesignButton.SetFTextShadowOffsetY(AValue: integer); begin if FTextShadowOffsetY = AValue then Exit; FTextShadowOffsetY := AValue; InvalidatePreferredSize; AdjustSize; Invalidate; end; procedure TBCMaterialDesignButton.SetFTextShadowSize(AValue: integer); begin if FTextShadowSize = AValue then Exit; FTextShadowSize := AValue; InvalidatePreferredSize; AdjustSize; Invalidate; end; procedure TBCMaterialDesignButton.SetFTextSize(AValue: integer); begin if FTextSize = AValue then Exit; FTextSize := AValue; InvalidatePreferredSize; AdjustSize; Invalidate; end; procedure TBCMaterialDesignButton.SetFTextStyle(AValue: TFontStyles); begin if FTextStyle = AValue then Exit; FTextStyle := AValue; InvalidatePreferredSize; AdjustSize; Invalidate; end; procedure TBCMaterialDesignButton.CalculatePreferredSize( var PreferredWidth, PreferredHeight: integer; WithThemeSpace: boolean); var ts: TSize; begin inherited CalculatePreferredSize(PreferredWidth, PreferredHeight, WithThemeSpace); if Caption <> '' then begin FBGRA.FontQuality := FTextQuality; FBGRA.FontName := FTextFont; FBGRA.FontStyle := FTextStyle; FBGRA.FontHeight := FTextSize; FBGRA.FontAntialias := True; ts := FBGRA.TextSize(Caption); Inc(PreferredWidth, ts.cx + 26); Inc(PreferredHeight, ts.cy + 10); end; if FShadow then begin Inc(PreferredWidth, FShadowSize * 2); Inc(PreferredHeight, FShadowSize * 2); end; end; procedure TBCMaterialDesignButton.SetFNormalColor(AValue: TColor); begin if FNormalColor = AValue then Exit; FNormalColor := AValue; Invalidate; end; procedure TBCMaterialDesignButton.SetFNormalColorEffect(AValue: TColor); begin if FNormalColorEffect = AValue then Exit; FNormalColorEffect := AValue; Invalidate; end; procedure TBCMaterialDesignButton.OnStartTimer(Sender: TObject); begin FCircleAlpha := 255; FCircleSize := 5; end; procedure TBCMaterialDesignButton.OnTimer(Sender: TObject); begin FCircleSize := FCircleSize + 8; if FCircleAlpha - 10 > 0 then FCircleAlpha := FCircleAlpha - 10 else FCircleAlpha := 0; if FCircleAlpha <= 0 then FTimer.Enabled := False; Invalidate; end; procedure TBCMaterialDesignButton.Paint; var temp: TBGRABitmap; round_rect_left: integer; round_rect_width: integer; round_rect_height: integer; text_height: integer; begin if (FBGRA.Width <> Width) or (FBGRA.Height <> Height) then begin FBGRA.SetSize(Width, Height); FBGRAShadow.SetSize(Width, Height); UpdateShadow; end; FBGRA.FillTransparent; if FShadow then FBGRA.PutImage(0, 0, FBGRAShadow, dmDrawWithTransparency); temp := TBGRABitmap.Create(Width, Height, FNormalColor); temp.EllipseAntialias(FMousePos.X, FMousePos.Y, FCircleSize, FCircleSize, ColorToBGRA(FNormalColorEffect, FCircleAlpha), 1, ColorToBGRA(FNormalColorEffect, FCircleAlpha)); if FShadow then begin round_rect_left := FShadowSize; round_rect_width := Width - FShadowSize; round_rect_height := Height - FShadowSize; end else begin round_rect_left := 0; round_rect_width := width; round_rect_height := height; end; FBGRA.FillRoundRectAntialias(round_rect_left, 0, round_rect_width, round_rect_height, FRoundBorders, FRoundBorders, temp, [rrDefault], False); temp.Free; if Caption <> '' then begin if FShadow then text_height := Height - FShadowSize else text_height := Height; temp := DrawTextShadow(Width, text_height, Caption, FTextSize, FTextColor, FTextShadowColor, FTextShadowOffsetX, FTextShadowOffsetY, FTextShadowSize, FTextStyle, FTextFont, FTextShadow, FTextQuality) as TBGRABitmap; FBGRA.PutImage(0, 0, temp, dmDrawWithTransparency); temp.Free; end; FBGRA.Draw(Canvas, 0, 0, False); end; procedure TBCMaterialDesignButton.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: integer); begin FTimer.Enabled := False; FMousePos := Point(X, Y); FTimer.Enabled := True; inherited MouseDown(Button, Shift, X, Y); end; class function TBCMaterialDesignButton.GetControlClassDefaultSize: TSize; begin Result.CX := 123; Result.CY := 33; end; procedure TBCMaterialDesignButton.TextChanged; begin InvalidatePreferredSize; AdjustSize; Invalidate; end; procedure TBCMaterialDesignButton.UpdateShadow; begin FBGRAShadow.FillTransparent; if FShadow then begin FBGRAShadow.RoundRectAntialias(FShadowSize, FShadowSize, Width - FShadowSize, Height - FShadowSize, FRoundBorders, FRoundBorders, FShadowColor, 1, FShadowColor, [rrDefault]); BGRAReplace(FBGRAShadow, FBGRAShadow.FilterBlurRadial(FShadowSize, FShadowSize, rbFast) as TBGRABitmap); end; end; constructor TBCMaterialDesignButton.Create(AOwner: TComponent); begin inherited Create(AOwner); with GetControlClassDefaultSize do SetInitialBounds(0, 0, CX, CY); FTimer := TTimer.Create(Self); FTimer.Interval := 15; FTimer.Enabled := False; {$IFDEF FPC}//# FTimer.OnStartTimer := OnStartTimer; {$ENDIF} FTimer.OnTimer := OnTimer; FBGRA := TBGRABitmap.Create(Width, Height); FBGRAShadow := TBGRABitmap.Create(Width, Height); FRoundBorders := 5; FNormalColor := clWhite; FNormalColorEffect := clSilver; FShadow := True; FShadowColor := clGray; FShadowSize := 5; FTextColor := clBlack; FTextSize := 16; FTextShadow := True; FTextShadowColor := clBlack; FTextShadowSize := 2; FTextShadowOffsetX := 0; FTextShadowOffsetY := 0; FTextStyle := []; FTextFont := 'default'; FTextQuality := fqFineAntialiasing; end; destructor TBCMaterialDesignButton.Destroy; begin FTimer.Enabled := False; {$IFDEF FPC}//# FTimer.OnStartTimer := nil; {$ENDIF} FTimer.OnTimer := nil; FreeAndNil(FBGRA); FreeAndNil(FBGRAShadow); inherited Destroy; end; procedure TBCMaterialDesignButton.ClickMe; begin FMousePos := Point(Width div 2, Height div 2); FTimer.Enabled := True; inherited Click; end; end.