TAChart: Fix drawing of checkboxes/radiobuttons on Cocoa, part 2.

git-svn-id: trunk@62633 -
This commit is contained in:
wp 2020-02-16 15:48:08 +00:00
parent d66d8ecf94
commit 6483f57d33

View File

@ -20,14 +20,19 @@
unit TAChartListbox; unit TAChartListbox;
{$mode objfpc}{$H+} {$MODE objfpc}{$H+}
{$IFDEF DARWIN}
{$DEFINE USE_BITMAPS}
{$ENDIF}
interface interface
uses uses
Classes, Controls, StdCtrls, Classes, Controls, StdCtrls, Types,
TAChartUtils, TACustomSeries, TALegend, TAGraph; TAChartUtils, TACustomSeries, TALegend, TAGraph;
type type
TChartListbox = class; TChartListbox = class;
@ -51,7 +56,10 @@ type
TChartListbox = class(TCustomListbox) TChartListbox = class(TCustomListbox)
private private
FChart: TChart; FChart: TChart;
FCheckBoxSize: TSize;
FCheckStyle: TCheckBoxesStyle; FCheckStyle: TCheckBoxesStyle;
FDown: Boolean;
FHot: Boolean;
FLegendItems: TChartLegendItems; FLegendItems: TChartLegendItems;
FListener: TListener; FListener: TListener;
FOnAddSeries: TChartListboxAddSeriesEvent; FOnAddSeries: TChartListboxAddSeriesEvent;
@ -77,8 +85,10 @@ type
procedure DrawItem( procedure DrawItem(
AIndex: Integer; ARect: TRect; AState: TOwnerDrawState); override; AIndex: Integer; ARect: TRect; AState: TOwnerDrawState); override;
procedure KeyDown(var AKey: Word; AShift: TShiftState); override; procedure KeyDown(var AKey: Word; AShift: TShiftState); override;
procedure MouseDown( procedure MouseDown(AButton: TMouseButton; AShift: TShiftState; AX, AY: Integer); override;
AButton: TMouseButton; AShift: TShiftState; AX, AY: Integer); override; procedure MouseEnter; override;
procedure MouseLeave; override;
procedure MouseUp(AButton: TMouseButton; AShift: TShiftState; AX, AY: Integer); override;
protected protected
procedure CalcRects( procedure CalcRects(
const AItemRect: TRect; out ACheckboxRect, ASeriesIconRect: TRect); const AItemRect: TRect; out ACheckboxRect, ASeriesIconRect: TRect);
@ -185,11 +195,49 @@ uses
Graphics, Math, LCLIntf, LCLType, SysUtils, Themes, Graphics, Math, LCLIntf, LCLType, SysUtils, Themes,
TACustomSource, TADrawerCanvas, TADrawUtils, TAEnumerators, TAGeometry; TACustomSource, TADrawerCanvas, TADrawUtils, TAEnumerators, TAGeometry;
procedure Register; type
TThemedStatesUsed = {%H-}tbRadioButtonUnCheckedNormal..tbCheckboxCheckedDisabled;
{$IFDEF USE_BITMAPS}
var
ThemedBitmaps: Array[TThemedStatesUsed] of TBitmap;
function CreateBitmap(AThemedButton: TThemedButton; ABkColor: TColor): TBitmap;
var
s: TSize;
details: TThemedElementDetails;
begin begin
RegisterComponents(CHART_COMPONENT_IDE_PAGE, [TChartListbox]); Result := ThemedBitmaps[AThemedButton];
if Assigned(Result) and (Result.Canvas.Pixels[0, 0] = ABkColor) then
exit;
FreeAndNil(Result);
Result := TBitmap.Create;
details := ThemeServices.GetElementDetails(AThemedButton);
s := ThemeServices.GetDetailSize(details);
Result.SetSize(s.CX, s.CY);
Result.Canvas.Brush.Color := ABkColor;
Result.Canvas.FillRect(0, 0, Result.Width, Result.Height);
ThemeServices.DrawElement(Result.Canvas.Handle, details, Rect(0, 0, Result.Width, Result.Height));
ThemedBitmaps[AThemedButton] := Result;
end; end;
procedure InitBitmaps;
var
tb: TThemedButton;
begin
for tb in TThemedStatesUsed do
ThemedBitmaps[tb] := nil;
end;
procedure FreeBitmaps;
var
tb: TThemedButton;
begin
for tb in TThemedStatesUsed do
FreeAndNil(ThemedBitmaps[tb]);
end;
{$ENDIF}
{ TChartListbox } { TChartListbox }
constructor TChartListbox.Create(AOwner: TComponent); constructor TChartListbox.Create(AOwner: TComponent);
@ -211,17 +259,18 @@ procedure TChartListbox.CalcRects(
const AItemRect: TRect; out ACheckboxRect, ASeriesIconRect: TRect); const AItemRect: TRect; out ACheckboxRect, ASeriesIconRect: TRect);
{ based on the rect of a listbox item, calculates the locations of the { based on the rect of a listbox item, calculates the locations of the
checkbox and of the series icon } checkbox and of the series icon }
const
MARGIN = 4;
var var
w, x: Integer; x: Integer;
begin begin
ACheckBoxRect := ZeroRect; ACheckBoxRect := ZeroRect;
ASeriesIconRect := ZeroRect; ASeriesIconRect := ZeroRect;
w := Canvas.TextHeight('Tg'); x := AItemRect.Left + MARGIN;
x := 4;
if cloShowCheckboxes in Options then begin if cloShowCheckboxes in Options then begin
with AItemRect do ACheckBoxRect := Rect(0, 0, FCheckboxSize.CX, FCheckboxSize.CY);
ACheckboxRect := Bounds(Left + 4, (Top + Bottom - w) div 2, w, w); OffsetRect(ACheckboxRect, x, (AItemRect.Top + AItemRect.Bottom - FCheckboxSize.CY) div 2);
if cloShowIcons in Options then if cloShowIcons in Options then
x += ACheckboxRect.Right; x += ACheckboxRect.Right;
end end
@ -285,35 +334,67 @@ begin
ClickedSeriesIcon(FSeriesIconClicked); ClickedSeriesIcon(FSeriesIconClicked);
end; end;
function GetThemedButtonOffset(Hot, Pressed, Disabled: Boolean): Integer;
begin
Result := 0;
if Disabled then
Result := 3
else if Hot then
Result := 1
else if Pressed then
Result := 2;
end;
procedure TChartListbox.DrawItem( procedure TChartListbox.DrawItem(
AIndex: Integer; ARect: TRect; AState: TOwnerDrawState); AIndex: Integer; ARect: TRect; AState: TOwnerDrawState);
{ draws the listbox item } { draws the listbox item }
const const
THEMED_FLAGS: array [TCheckboxesStyle, Boolean] of TThemedButton = ( THEMED_BASE: array [TCheckboxesStyle, Boolean] of TThemedButton = (
(tbCheckBoxUncheckedNormal, tbCheckBoxCheckedNormal), (tbCheckBoxUncheckedNormal, tbCheckBoxCheckedNormal),
(tbRadioButtonUnCheckedNormal, tbRadioButtonCheckedNormal) (tbRadioButtonUnCheckedNormal, tbRadioButtonCheckedNormal)
); );
var var
id: IChartDrawer; id: IChartDrawer;
rcb, ricon: TRect; rcb, ricon: TRect;
te: TThemedElementDetails;
x: Integer; x: Integer;
ch: Boolean; tb: TThemedButton;
tbBase, tbOffs: Integer;
{$IFDEF USE_BITMAPS}
bmp: TBitmap;
{$ELSE}
ted: TThemedElementDetails;
{$ENDIF}
begin begin
if Assigned(OnDrawItem) then begin if Assigned(OnDrawItem) then begin
OnDrawItem(Self, AIndex, ARect, AState); OnDrawItem(Self, AIndex, ARect, AState);
exit; exit;
end; end;
if (FChart = nil) or not InRange(AIndex, 0, Count - 1) then if (FChart = nil) or not InRange(AIndex, 0, Count - 1) then
exit; exit;
Canvas.FillRect(ARect); Canvas.FillRect(ARect);
if cloShowCheckboxes in Options then begin
tbBase := ord(THEMED_BASE[FCheckStyle, Checked[AIndex]]);
tbOffs := GetThemedButtonOffset(FHot, FDown, false);
tb := TThemedButton(ord(tbBase) + tbOffs);
{$IFDEF USE_BITMAPS}
bmp := CreateBitmap(tb, Canvas.Brush.Color);
FCheckboxSize := Size(bmp.Width, bmp.Height);
{$ELSE}
ted := ThemeServices.GetElementDetails(tb);
FCheckboxSize := ThemeServices.GetDetailSize(ted);
{$ENDIF}
end;
CalcRects(ARect, rcb, ricon); CalcRects(ARect, rcb, ricon);
if cloShowCheckboxes in Options then begin if cloShowCheckboxes in Options then begin
ch := Checked[AIndex]; {$IFDEF USE_BITMAPS}
te := ThemeServices.GetElementDetails(THEMED_FLAGS[FCheckStyle, ch]); Canvas.Draw(rcb.Left, rcb.Top, bmp);
ThemeServices.DrawElement(Canvas.Handle, te, rcb); {$ELSE}
ThemeServices.DrawElement(Canvas.Handle, ted, rcb);
{$ENDIF}
x := rcb.Right; x := rcb.Right;
end end
else else
@ -417,8 +498,6 @@ begin
AHeight := Max(AHeight, GetSystemMetrics(SM_CYMENUCHECK) + 2); AHeight := Max(AHeight, GetSystemMetrics(SM_CYMENUCHECK) + 2);
end; end;
procedure TChartListbox.MouseDown(
AButton: TMouseButton; AShift: TShiftState; AX, AY: Integer);
{ standard MouseDown handler: checks if the click occured on the checkbox, { standard MouseDown handler: checks if the click occured on the checkbox,
on the series icon, or on the text. on the series icon, or on the text.
The visibility state of the item's series is changed when clicking on the The visibility state of the item's series is changed when clicking on the
@ -428,11 +507,14 @@ procedure TChartListbox.MouseDown(
An event OnItemClick is generated when the click occured neither on the An event OnItemClick is generated when the click occured neither on the
checkbox nor the series icon. checkbox nor the series icon.
} }
procedure TChartListbox.MouseDown(
AButton: TMouseButton; AShift: TShiftState; AX, AY: Integer);
var var
rcb, ricon: TRect; rcb, ricon: TRect;
index: Integer; index: Integer;
p: TPoint; p: TPoint;
begin begin
FDown := true;
FSeriesIconClicked := -1; FSeriesIconClicked := -1;
try try
if AButton <> mbLeft then exit; if AButton <> mbLeft then exit;
@ -452,6 +534,24 @@ begin
end; end;
end; end;
procedure TChartListbox.MouseEnter;
begin
FHot := true;
inherited;
end;
procedure TChartListbox.MouseLeave;
begin
FHot := false;
inherited;
end;
procedure TChartListBox.MouseUp(
AButton: TMouseButton; AShift: TShiftState; AX, AY: Integer);
begin
FDown := false;
inherited;
end;
procedure TChartListbox.Notification(AComponent: TComponent; AOperation: TOperation); procedure TChartListbox.Notification(AComponent: TComponent; AOperation: TOperation);
begin begin
if (AOperation = opRemove) and (AComponent = FChart) then if (AOperation = opRemove) and (AComponent = FChart) then
@ -601,5 +701,18 @@ begin
Invalidate; Invalidate;
end; end;
procedure Register;
begin
RegisterComponents(CHART_COMPONENT_IDE_PAGE, [TChartListbox]);
end;
{$IFDEF USE_BITMAPS}
initialization
InitBitmaps;
finalization
FreeBitmaps;
{$ENDIF}
end. end.