TAChart: Fix axis click tool sometimes detecting wrong axis part.

This commit is contained in:
wp_xyz 2024-02-17 15:46:11 +01:00
parent f3d26660e7
commit 9e3e23a440
3 changed files with 126 additions and 59 deletions

View File

@ -83,10 +83,15 @@ type
{ TChartAxis } { TChartAxis }
type
TChartAxisHitTest = (ahtTitle, ahtLine, ahtLabels, ahtGrid, TChartAxisHitTest = (ahtTitle, ahtLine, ahtLabels, ahtGrid,
ahtAxisStart, ahtAxisCenter, ahtAxisEnd); ahtAxisStart, ahtAxisCenter, ahtAxisEnd);
TChartAxisHitTests = set of TChartAxisHitTest; TChartAxisHitTests = set of TChartAxisHitTest;
const
ALL_CHARTAXIS_HITTESTS = [ahtTitle, ahtLine, ahtLabels, ahtGrid, ahtAxisStart, ahtAxisCenter, ahtAxisEnd];
type
TChartAxis = class(TChartBasicAxis) TChartAxis = class(TChartBasicAxis)
strict private strict private
FListener: TListener; FListener: TListener;
@ -157,7 +162,9 @@ type
destructor Destroy; override; destructor Destroy; override;
public public
procedure Assign(ASource: TPersistent); override; procedure Assign(ASource: TPersistent); override;
function GetHitTestInfoAt(APoint: TPoint; ADelta: Integer): TChartAxisHitTests; virtual; function GetHitTestInfoAt(APoint: TPoint; ADelta: Integer;
ATest: TChartAxisHitTests = ALL_CHARTAXIS_HITTESTS): TChartAxisHitTests; virtual;
function GetLabeledAxisRect: TRect;
procedure Draw; procedure Draw;
procedure DrawTitle(ASize: Integer); procedure DrawTitle(ASize: Integer);
function GetChart: TCustomChart; inline; function GetChart: TCustomChart; inline;
@ -495,91 +502,133 @@ begin
inherited; inherited;
end; end;
function TChartAxis.GetHitTestInfoAt(APoint: TPoint; function TChartAxis.GetHitTestInfoAt(APoint: TPoint; ADelta: Integer;
ADelta: Integer): TChartAxisHitTests; ATest: TChartAxisHitTests = ALL_CHARTAXIS_HITTESTS): TChartAxisHitTests;
var var
R: TRect; R, Rax, Rline: TRect;
w, h, loc: Integer; dist, loc: Integer;
p: Integer; p: Integer;
t: TChartValueText; t: TChartValueText;
begin begin
Result := []; Result := [];
if IsPointInPolygon(APoint, FTitlePolygon) then
Include(Result, ahtTitle) if (ahtTitle in ATest) and IsPointInPolygon(APoint, FTitlePolygon) then
else begin begin
R := FAxisRect; Include(Result, ahtTitle);
case FAlignment of exit;
calLeft: end;
begin
R.Right := R.Left + Max(ADelta, TickInnerLength); Rax := GetLabeledAxisRect;
R.Left := R.Left - Max(ADelta, TickLength); Rline := Rax;
end; case FAlignment of
calRight: calLeft:
begin begin
R.Left := R.Right - Max(ADelta, TickInnerLength); Rline.Right := Rline.Left + Max(ADelta, TickInnerLength);
R.Right := R.Right + Max(ADelta, TickLength); Rline.Left := Rline.Left - Max(ADelta, TickLength);
end; if not InRange(APoint.Y, Rax.Top, Rax.Bottom) then
calTop: exit;
begin end;
R.Bottom := R.Top + Max(ADelta, TickInnerLength); calRight:
R.Top := R.Top - Max(ADelta, TickLength); begin
end; Rline.Left := Rline.Right - Max(ADelta, TickInnerLength);
calBottom: Rline.Right := Rline.Right + Max(ADelta, TickLength);
begin if not InRange(APoint.Y, Rax.Top, Rax.Bottom) then
R.Top := R.Bottom - Max(ADelta, TickInnerLength); exit;
R.Bottom := R.Bottom + Max(ADelta, TickLength); end;
end; calTop:
end; begin
if IsPointInRect(APoint, R) then Rline.Bottom := Rline.Top + Max(ADelta, TickInnerLength);
Include(Result, ahtLine) Rline.Top := Rline.Top - Max(ADelta, TickLength);
else if IsPointInside(APoint) then if not InRange(APoint.X, Rax.Left, RAx.Right) then
Include(Result, ahtLabels) exit;
else begin end;
R := FHelper.FClipRect^; calBottom:
for t in FMarkValues do begin begin
Rline.Top := Rline.Bottom - Max(ADelta, TickInnerLength);
Rline.Bottom := Rline.Bottom + Max(ADelta, TickLength);
if not InRange(APoint.X, Rax.Left, RAx.Right) then
exit;
end;
end;
if (ahtLine in ATest) and IsPointInRect(APoint, Rline) then
Include(Result, ahtLine)
else
if (ahtLabels in ATest) and IsPointInside(APoint) then
Include(Result, ahtLabels)
else
if (ahtGrid in ATest) then
begin
R := Rax;
if IsPointInRect(APoint, R) and Grid.Visible then
for t in FMarkValues do
begin
p := FHelper.GraphToImage(FHelper.FAxisTransf(t.FValue)); p := FHelper.GraphToImage(FHelper.FAxisTransf(t.FValue));
if IsVertical then begin if IsVertical then
begin
R.Top := p - ADelta; R.Top := p - ADelta;
R.Bottom := p + ADelta; R.Bottom := p + ADelta;
end else begin end else
begin
R.Left := p - ADelta; R.Left := p - ADelta;
R.Right := p + ADelta; R.Right := p + aDelta;
end; end;
if IsPointInRect(APoint, R) then begin if IsPointInRect(APoint, R) then
begin
Include(Result, ahtGrid); Include(Result, ahtGrid);
break; exit;
end; end;
end; end;
end; end;
if Result = [] then if Result = [] then
exit; exit;
R := FHelper.FClipRect^; if [ahtAxisStart, ahtAxisCenter, ahtAxisEnd] * ATest <> [] then
begin
R := Rax;
if IsVertical then begin if IsVertical then begin
h := R.Bottom - R.Top; dist := abs(R.Bottom - R.Top) div 4;
p := APoint.Y - R.Top; p := abs(APoint.Y - R.Top);
if p < h div 4 then if p < dist then
loc := +1 loc := +1
else if p > h - h div 4 then else if p > 3 * dist then
loc := -1 loc := -1
else else
loc := 0; loc := 0;
end else begin end else begin
w := abs(R.Right - R.Left); dist := abs(R.Right - R.Left) div 4;
p := abs(APoint.X - R.Left); p := abs(APoint.X - R.Left);
if p < w div 4 then if p < dist then
loc := -1 loc := -1
else if p > w - w div 4 then else if p > 3 * dist then
loc := +1 loc := +1
else else
loc := 0; loc := 0;
end; end;
if IsFlipped then loc := -loc; if IsFlipped then
loc := -loc;
case loc of case loc of
-1: Include(Result, ahtAxisStart); -1: if (ahtAxisStart in ATest) then Include(Result, ahtAxisStart);
0: Include(Result, ahtAxisCenter); 0: if (ahtAxisCenter in ATest) then Include(Result, ahtAxisCenter);
+1: Include(Result, ahtAxisEnd); +1: if (ahtAxisEnd in ATest) then Include(Result, ahtAxisEnd);
end;
end;
end;
function TChartAxis.GetLabeledAxisRect: TRect;
begin
Result := FHelper.FClipRect^; //FAxisRect;
if FAtDataOnly then
begin
if IsVertical then
begin
Result.Bottom := FHelper.GraphToImage(FHelper.MinForMarks);
Result.Top := FHelper.GraphToImage(FHelper.MaxForMarks);
end else
begin
Result.Left := FHelper.GraphToImage(FHelper.MinForMarks);
Result.Right := FHelper.GraphToImage(FHelper.MaxForMarks);
end; end;
end; end;
end; end;

View File

@ -253,6 +253,8 @@ type
procedure GetClipRange(out AMin, AMax: Integer); virtual; abstract; procedure GetClipRange(out AMin, AMax: Integer); virtual; abstract;
function GetDefaultPenColor: TColor; function GetDefaultPenColor: TColor;
function GraphToImage(AGraph: Double): Integer; virtual; abstract; function GraphToImage(AGraph: Double): Integer; virtual; abstract;
property MaxForMarks: Double read FMaxForMarks;
property MinForMarks: Double read FMinForMarks;
end; end;
TAxisDrawHelperClass = class of TAxisDrawHelper; TAxisDrawHelperClass = class of TAxisDrawHelper;

View File

@ -2337,14 +2337,30 @@ function TAxisClickTool.GetHitTestInfo(APoint: TPoint): Boolean;
var var
ax: TChartAxis; ax: TChartAxis;
begin begin
{ We must test the axes twice because some positions may be misinterpreted
to belong to other axes. A click on the axis line of an additional left axis
may be interpreted as a click on the bottom axis since the bottom axis usually
tested before the 2nd y axis. }
for ax in FChart.AxisList do
begin
FHitTest := ax.GetHitTestInfoAt(APoint, FGrabRadius, [ahtLine, ahtAxisStart, ahtAxisCenter, ahtAxisEnd]);
if FHitTest <> [] then
begin
FAxis := ax;
Result := true;
exit;
end;
end;
for ax in FChart.AxisList do begin for ax in FChart.AxisList do begin
FHitTest := ax.GetHitTestInfoAt(APoint, FGrabRadius); FHitTest := ax.GetHitTestInfoAt(APoint, FGrabRadius, ALL_CHARTAXIS_HITTESTS - [ahtLine]);
if FHitTest <> [] then begin if FHitTest <> [] then begin
FAxis := ax; FAxis := ax;
Result := true; Result := true;
exit; exit;
end; end;
end; end;
Result := false; Result := false;
FAxis := nil; FAxis := nil;
FHitTest := []; FHitTest := [];