| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- // SPDX-License-Identifier: LGPL-3.0-linking-exception
- unit BGRATheme;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
- BGRABitmap, BGRABitmapTypes, BGRASVGImageList;
- type
- TBGRAThemeButtonState = (btbsNormal, btbsHover, btbsActive, btbsDisabled);
- { TBGRAThemeSurface }
- TBGRAThemeSurface = class
- private
- FBitmap: TBGRABitmap;
- FBitmapRect: TRect;
- FCanvasScale: single;
- FDestCanvas: TCanvas;
- FLclDPI: integer;
- function GetBitmap: TBGRABitmap;
- function GetBitmapDPI: integer;
- procedure SetBitmapRect(AValue: TRect);
- public
- constructor Create(AControl: TCustomControl);
- constructor Create(ADestRect: TRect; ADestCanvas: TCanvas; ACanvasScale: single; ALclDPI: integer);
- destructor Destroy; override;
- procedure DrawBitmap;
- procedure DiscardBitmap;
- procedure BitmapColorOverlay(AColor: TBGRAPixel; AOperation: TBlendOperation = boTransparent); overload;
- function ScaleForCanvas(AValue: integer; AFromDPI: integer = 96): integer;
- function ScaleForBitmap(AValue: integer; AFromDPI: integer = 96): integer;
- function ScaleForBitmap(const ARect: TRect; AFromDPI: integer = 96): TRect;
- property DestCanvas: TCanvas read FDestCanvas;
- property DestCanvasDPI: integer read FLclDPI;
- property Bitmap: TBGRABitmap read GetBitmap;
- property BitmapRect: TRect read FBitmapRect write SetBitmapRect;
- property BitmapDPI: integer read GetBitmapDPI;
- end;
- TBGRATheme = class;
- { TBGRAThemeControl }
- TBGRAThemeControl = class(TCustomControl)
- private
- FTheme: TBGRATheme;
- procedure SetTheme(AValue: TBGRATheme);
- public
- destructor Destroy; override;
- published
- property Theme: TBGRATheme read FTheme write SetTheme;
- end;
- { TBGRATheme }
- TBGRATheme = class(TComponent)
- private
- FThemedControls: TList;
- function GetThemedControl(AIndex: integer): TBGRAThemeControl;
- function GetThemedControlCount: integer;
- procedure AddThemedControl(AControl: TBGRAThemeControl);
- procedure RemoveThemedControl(AControl: TBGRAThemeControl);
- protected
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure InvalidateThemedControls;
- function PreferredButtonWidth(const hasGlyph: boolean): Integer; virtual;
- function PreferredButtonHeight(const hasGlyph: boolean): Integer; virtual;
- procedure DrawButton(Caption: string; State: TBGRAThemeButtonState;
- Focused: boolean; ARect: TRect; ASurface: TBGRAThemeSurface; AImageIndex: Integer = -1; AImageList: TBGRASVGImageList = nil); virtual;
- procedure DrawRadioButton(Caption: string; State: TBGRAThemeButtonState;
- {%H-}Focused: boolean; Checked: boolean; ARect: TRect; ASurface: TBGRAThemeSurface); virtual;
- procedure DrawCheckBox(Caption: string; State: TBGRAThemeButtonState;
- {%H-}Focused: boolean; Checked: boolean; ARect: TRect; ASurface: TBGRAThemeSurface); virtual;
- property ThemedControlCount: integer read GetThemedControlCount;
- property ThemedControl[AIndex: integer]: TBGRAThemeControl read GetThemedControl;
- published
- end;
- var
- BGRADefaultTheme: TBGRATheme;
- procedure Register;
- implementation
- uses LCLType;
- procedure Register;
- begin
- RegisterComponents('BGRA Themes', [TBGRATheme]);
- end;
- { TBGRAThemeControl }
- procedure TBGRAThemeControl.SetTheme(AValue: TBGRATheme);
- begin
- if FTheme=AValue then Exit;
- if Assigned(AValue) then AValue.RemoveThemedControl(self);
- FTheme:=AValue;
- if Assigned(AValue) then AValue.AddThemedControl(self);
- Invalidate;
- end;
- destructor TBGRAThemeControl.Destroy;
- begin
- if Assigned(FTheme) then FTheme.RemoveThemedControl(self);
- inherited Destroy;
- end;
- { TBGRAThemeSurface }
- function TBGRAThemeSurface.GetBitmap: TBGRABitmap;
- begin
- if FBitmap = nil then
- FBitmap := TBGRABitmap.Create(round(FBitmapRect.Width * FCanvasScale),
- round(FBitmapRect.Height * FCanvasScale));
- result := FBitmap;
- end;
- function TBGRAThemeSurface.GetBitmapDPI: integer;
- begin
- result := round(FLclDPI*FCanvasScale);
- end;
- procedure TBGRAThemeSurface.SetBitmapRect(AValue: TRect);
- begin
- if FBitmapRect=AValue then Exit;
- DiscardBitmap;
- FBitmapRect:=AValue;
- end;
- constructor TBGRAThemeSurface.Create(AControl: TCustomControl);
- var
- parentForm: TCustomForm;
- lclDPI: Integer;
- begin
- parentForm := GetParentForm(AControl, False);
- if Assigned(parentForm) then
- lclDPI := parentForm.PixelsPerInch
- else lclDPI := Screen.PixelsPerInch;
- Create(AControl.ClientRect, AControl.Canvas, AControl.GetCanvasScaleFactor, lclDPI);
- end;
- constructor TBGRAThemeSurface.Create(ADestRect: TRect; ADestCanvas: TCanvas;
- ACanvasScale: single; ALclDPI: integer);
- begin
- FBitmap := nil;
- FBitmapRect := ADestRect;
- FDestCanvas := ADestCanvas;
- FCanvasScale:= ACanvasScale;
- FLclDPI:= ALclDPI;
- end;
- destructor TBGRAThemeSurface.Destroy;
- begin
- FBitmap.Free;
- inherited Destroy;
- end;
- procedure TBGRAThemeSurface.DrawBitmap;
- begin
- if FBitmap = nil then exit;
- FBitmap.Draw(FDestCanvas, FBitmapRect, false);
- end;
- procedure TBGRAThemeSurface.DiscardBitmap;
- begin
- FreeAndNil(FBitmap);
- end;
- procedure TBGRAThemeSurface.BitmapColorOverlay(AColor: TBGRAPixel;
- AOperation: TBlendOperation);
- begin
- if AColor.alpha <> 0 then
- Bitmap.BlendOver(AColor, AOperation, AColor.alpha, false, true);
- end;
- function TBGRAThemeSurface.ScaleForCanvas(AValue: integer; AFromDPI: integer): integer;
- begin
- result := MulDiv(AValue, DestCanvasDPI, AFromDPI);
- end;
- function TBGRAThemeSurface.ScaleForBitmap(AValue: integer; AFromDPI: integer): integer;
- begin
- result := MulDiv(AValue, BitmapDPI, AFromDPI);
- end;
- function TBGRAThemeSurface.ScaleForBitmap(const ARect: TRect; AFromDPI: integer): TRect;
- begin
- result.Left := ScaleForBitmap(ARect.Left, AFromDPI);
- result.Top := ScaleForBitmap(ARect.Top, AFromDPI);
- result.Right := ScaleForBitmap(ARect.Right, AFromDPI);
- result.Bottom := ScaleForBitmap(ARect.Bottom, AFromDPI);
- end;
- { TBGRATheme }
- function TBGRATheme.GetThemedControl(AIndex: integer): TBGRAThemeControl;
- begin
- result := TBGRAThemeControl(FThemedControls[AIndex]);
- end;
- function TBGRATheme.GetThemedControlCount: integer;
- begin
- result := FThemedControls.Count;
- end;
- procedure TBGRATheme.AddThemedControl(AControl: TBGRAThemeControl);
- begin
- if FThemedControls.IndexOf(AControl) = -1 then
- FThemedControls.Add(AControl);
- end;
- procedure TBGRATheme.RemoveThemedControl(AControl: TBGRAThemeControl);
- begin
- FThemedControls.Remove(AControl);
- end;
- constructor TBGRATheme.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FThemedControls := TList.Create;
- end;
- destructor TBGRATheme.Destroy;
- var i: integer;
- begin
- for i := ThemedControlCount-1 downto 0 do
- ThemedControl[i].Theme := nil;
- FThemedControls.Free;
- inherited Destroy;
- end;
- procedure TBGRATheme.InvalidateThemedControls;
- var
- i: Integer;
- begin
- for i := 0 to ThemedControlCount-1 do
- ThemedControl[i].Invalidate;
- end;
- function TBGRATheme.PreferredButtonWidth(const hasGlyph: boolean): Integer;
- begin
- Result := 125;
- end;
- function TBGRATheme.PreferredButtonHeight(const hasGlyph: boolean): Integer;
- begin
- Result := 35;
- end;
- procedure TBGRATheme.DrawButton(Caption: string; State: TBGRAThemeButtonState;
- Focused: boolean; ARect: TRect; ASurface: TBGRAThemeSurface;
- AImageIndex: Integer; AImageList: TBGRASVGImageList);
- var
- Style: TTextStyle;
- begin
- With ASurface do
- begin
- DestCanvas.Font.Color := clBlack;
- case State of
- btbsNormal: DestCanvas.Brush.Color := RGBToColor(225, 225, 225);
- btbsHover: DestCanvas.Brush.Color := RGBToColor(229, 241, 251);
- btbsActive: DestCanvas.Brush.Color := RGBToColor(204, 228, 247);
- btbsDisabled: DestCanvas.Brush.Color := RGBToColor(204, 204, 204);
- end;
- DestCanvas.Pen.Color := DestCanvas.Brush.Color;
- DestCanvas.Rectangle(ARect);
- if Focused then
- begin
- DestCanvas.Pen.Color := clBlack;
- DestCanvas.Rectangle(ARect);
- end;
- if Caption <> '' then
- begin
- fillchar(Style, sizeof(Style), 0);
- Style.Alignment := taCenter;
- Style.Layout := tlCenter;
- Style.Wordbreak := True;
- DestCanvas.TextRect(ARect, 0, 0, Caption, Style);
- end;
- end;
- end;
- procedure TBGRATheme.DrawRadioButton(Caption: string; State: TBGRAThemeButtonState;
- Focused: boolean; Checked: boolean; ARect: TRect; ASurface: TBGRAThemeSurface);
- var
- Style: TTextStyle;
- Color: TBGRAPixel;
- begin
- with ASurface do
- begin
- DestCanvas.Font.Color := clBlack;
- case State of
- btbsHover: Color := BGRA(0, 120, 215);
- btbsActive: Color := BGRA(0, 84, 153);
- btbsDisabled:
- begin
- DestCanvas.Font.Color := clGray;
- Color := BGRA(204, 204, 204);
- end;
- else {btbsNormal}
- Color := BGRABlack;
- end;
- BitmapRect := RectWithSize(ARect.Left, ARect.Top, ARect.Height, ARect.Height);
- Bitmap.FillEllipseAntialias(Bitmap.Height / 2, Bitmap.Height / 2,
- Bitmap.Height / 2 - 2, Bitmap.Height / 2 - 2, BGRAWhite);
- Bitmap.EllipseAntialias(Bitmap.Height / 2, Bitmap.Height / 2,
- Bitmap.Height / 2 - 2, Bitmap.Height / 2 - 2, Color{%H-}, 1);
- if Checked then
- Bitmap.FillEllipseAntialias(Bitmap.Height / 2, Bitmap.Height /
- 2, Bitmap.Height / 4, Bitmap.Height / 4, Color);
- DrawBitmap;
- if Caption <> '' then
- begin
- fillchar(Style, sizeof(Style), 0);
- Style.Alignment := taLeftJustify;
- Style.Layout := tlCenter;
- Style.Wordbreak := True;
- DestCanvas.TextRect(Rect(Arect.Height, 0, ARect.Right, ARect.Bottom),
- ARect.Height, 0, Caption, Style);
- end;
- end;
- end;
- procedure TBGRATheme.DrawCheckBox(Caption: string; State: TBGRAThemeButtonState;
- Focused: boolean; Checked: boolean; ARect: TRect; ASurface: TBGRAThemeSurface);
- var
- Style: TTextStyle;
- Bitmap: TBGRABitmap;
- Color: TBGRAPixel;
- aleft, atop, aright, abottom: integer;
- begin
- with ASurface do
- begin
- DestCanvas.Font.Color := clBlack;
- case State of
- btbsHover: Color := BGRA(0, 120, 215);
- btbsActive: Color := BGRA(0, 84, 153);
- btbsDisabled:
- begin
- DestCanvas.Font.Color := clGray;
- Color := BGRA(204, 204, 204);
- end;
- else {btbsNormal}
- Color := BGRABlack;
- end;
- BitmapRect := RectWithSize(ARect.Left, ARect.Top, ARect.Height, ARect.Height);
- Bitmap.Rectangle(0, 0, Bitmap.Height, Bitmap.Height, Color, BGRAWhite);
- aleft := 0;
- aright := Bitmap.Height;
- atop := 0;
- abottom := Bitmap.Height;
- if Checked then
- Bitmap.DrawPolyLineAntialias(Bitmap.ComputeBezierSpline(
- [BezierCurve(pointF(aleft + 2, atop + 3), PointF((aleft + aright - 1) / 2, abottom - 3)),
- BezierCurve(PointF((aleft + aright - 1) / 2, abottom - 3), PointF(
- (aleft + aright - 1) / 2, (atop * 2 + abottom - 1) / 3), PointF(aright - 2, atop - 2))]),
- Color, 1.5);
- DrawBitmap;
- if Caption <> '' then
- begin
- fillchar(Style, sizeof(Style), 0);
- Style.Alignment := taLeftJustify;
- Style.Layout := tlCenter;
- Style.Wordbreak := True;
- DestCanvas.TextRect(Rect(Arect.Height, 0, ARect.Right, ARect.Bottom),
- ARect.Height, 0, Caption, Style);
- end;
- end;
- end;
- var
- BasicTheme: TBGRATheme;
- initialization
- BasicTheme := TBGRATheme.Create(nil);
- BGRADefaultTheme := BasicTheme;
- finalization
- FreeAndNil(BasicTheme);
- end.
|